2
0

exynos4210_pwm.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * Samsung exynos4210 Pulse Width Modulation Timer
  3. *
  4. * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
  5. * All rights reserved.
  6. *
  7. * Evgeny Voevodin <e.voevodin@samsung.com>
  8. *
  9. * This program is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License as published by the
  11. * Free Software Foundation; either version 2 of the License, or (at your
  12. * option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. * See the GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License along
  20. * with this program; if not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include "qemu/osdep.h"
  23. #include "qemu/log.h"
  24. #include "hw/sysbus.h"
  25. #include "migration/vmstate.h"
  26. #include "qemu/timer.h"
  27. #include "qemu/module.h"
  28. #include "hw/ptimer.h"
  29. #include "hw/arm/exynos4210.h"
  30. #include "hw/irq.h"
  31. #include "qom/object.h"
  32. //#define DEBUG_PWM
  33. #ifdef DEBUG_PWM
  34. #define DPRINTF(fmt, ...) \
  35. do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
  36. ## __VA_ARGS__); } while (0)
  37. #else
  38. #define DPRINTF(fmt, ...) do {} while (0)
  39. #endif
  40. #define EXYNOS4210_PWM_TIMERS_NUM 5
  41. #define EXYNOS4210_PWM_REG_MEM_SIZE 0x50
  42. #define TCFG0 0x0000
  43. #define TCFG1 0x0004
  44. #define TCON 0x0008
  45. #define TCNTB0 0x000C
  46. #define TCMPB0 0x0010
  47. #define TCNTO0 0x0014
  48. #define TCNTB1 0x0018
  49. #define TCMPB1 0x001C
  50. #define TCNTO1 0x0020
  51. #define TCNTB2 0x0024
  52. #define TCMPB2 0x0028
  53. #define TCNTO2 0x002C
  54. #define TCNTB3 0x0030
  55. #define TCMPB3 0x0034
  56. #define TCNTO3 0x0038
  57. #define TCNTB4 0x003C
  58. #define TCNTO4 0x0040
  59. #define TINT_CSTAT 0x0044
  60. #define TCNTB(x) (0xC * (x))
  61. #define TCMPB(x) (0xC * (x) + 1)
  62. #define TCNTO(x) (0xC * (x) + 2)
  63. #define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
  64. #define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
  65. /*
  66. * Attention! Timer4 doesn't have OUTPUT_INVERTER,
  67. * so Auto Reload bit is not accessible by macros!
  68. */
  69. #define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x))
  70. #define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0))
  71. #define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1))
  72. #define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2))
  73. #define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3))
  74. #define TCON_TIMER4_AUTO_RELOAD (1 << 22)
  75. #define TINT_CSTAT_STATUS(x) (1 << (5 + (x)))
  76. #define TINT_CSTAT_ENABLE(x) (1 << (x))
  77. /* timer struct */
  78. typedef struct {
  79. uint32_t id; /* timer id */
  80. qemu_irq irq; /* local timer irq */
  81. uint32_t freq; /* timer frequency */
  82. /* use ptimer.c to represent count down timer */
  83. ptimer_state *ptimer; /* timer */
  84. /* registers */
  85. uint32_t reg_tcntb; /* counter register buffer */
  86. uint32_t reg_tcmpb; /* compare register buffer */
  87. struct Exynos4210PWMState *parent;
  88. } Exynos4210PWM;
  89. #define TYPE_EXYNOS4210_PWM "exynos4210.pwm"
  90. OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210PWMState, EXYNOS4210_PWM)
  91. struct Exynos4210PWMState {
  92. SysBusDevice parent_obj;
  93. MemoryRegion iomem;
  94. uint32_t reg_tcfg[2];
  95. uint32_t reg_tcon;
  96. uint32_t reg_tint_cstat;
  97. Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
  98. };
  99. /*** VMState ***/
  100. static const VMStateDescription vmstate_exynos4210_pwm = {
  101. .name = "exynos4210.pwm.pwm",
  102. .version_id = 1,
  103. .minimum_version_id = 1,
  104. .fields = (const VMStateField[]) {
  105. VMSTATE_UINT32(id, Exynos4210PWM),
  106. VMSTATE_UINT32(freq, Exynos4210PWM),
  107. VMSTATE_PTIMER(ptimer, Exynos4210PWM),
  108. VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
  109. VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
  110. VMSTATE_END_OF_LIST()
  111. }
  112. };
  113. static const VMStateDescription vmstate_exynos4210_pwm_state = {
  114. .name = "exynos4210.pwm",
  115. .version_id = 1,
  116. .minimum_version_id = 1,
  117. .fields = (const VMStateField[]) {
  118. VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
  119. VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
  120. VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
  121. VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
  122. EXYNOS4210_PWM_TIMERS_NUM, 0,
  123. vmstate_exynos4210_pwm, Exynos4210PWM),
  124. VMSTATE_END_OF_LIST()
  125. }
  126. };
  127. /*
  128. * PWM update frequency.
  129. * Must be called within a ptimer_transaction_begin/commit block
  130. * for s->timer[id].ptimer.
  131. */
  132. static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
  133. {
  134. uint32_t freq;
  135. freq = s->timer[id].freq;
  136. if (id > 1) {
  137. s->timer[id].freq = 24000000 /
  138. ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
  139. (GET_DIVIDER(s->reg_tcfg[1], id)));
  140. } else {
  141. s->timer[id].freq = 24000000 /
  142. ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
  143. (GET_DIVIDER(s->reg_tcfg[1], id)));
  144. }
  145. if (freq != s->timer[id].freq) {
  146. ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
  147. DPRINTF("freq=%uHz\n", s->timer[id].freq);
  148. }
  149. }
  150. /*
  151. * Counter tick handler
  152. */
  153. static void exynos4210_pwm_tick(void *opaque)
  154. {
  155. Exynos4210PWM *s = (Exynos4210PWM *)opaque;
  156. Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
  157. uint32_t id = s->id;
  158. bool cmp;
  159. DPRINTF("timer %u tick\n", id);
  160. /* set irq status */
  161. p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
  162. /* raise IRQ */
  163. if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
  164. DPRINTF("timer %u IRQ\n", id);
  165. qemu_irq_raise(p->timer[id].irq);
  166. }
  167. /* reload timer */
  168. if (id != 4) {
  169. cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
  170. } else {
  171. cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
  172. }
  173. if (cmp) {
  174. DPRINTF("auto reload timer %u count to %x\n", id,
  175. p->timer[id].reg_tcntb);
  176. ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
  177. ptimer_run(p->timer[id].ptimer, 1);
  178. } else {
  179. /* stop timer, set status to STOP, see Basic Timer Operation */
  180. p->reg_tcon &= ~TCON_TIMER_START(id);
  181. ptimer_stop(p->timer[id].ptimer);
  182. }
  183. }
  184. /*
  185. * PWM Read
  186. */
  187. static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset,
  188. unsigned size)
  189. {
  190. Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
  191. uint32_t value = 0;
  192. int index;
  193. switch (offset) {
  194. case TCFG0: case TCFG1:
  195. index = (offset - TCFG0) >> 2;
  196. value = s->reg_tcfg[index];
  197. break;
  198. case TCON:
  199. value = s->reg_tcon;
  200. break;
  201. case TCNTB0: case TCNTB1:
  202. case TCNTB2: case TCNTB3: case TCNTB4:
  203. index = (offset - TCNTB0) / 0xC;
  204. value = s->timer[index].reg_tcntb;
  205. break;
  206. case TCMPB0: case TCMPB1:
  207. case TCMPB2: case TCMPB3:
  208. index = (offset - TCMPB0) / 0xC;
  209. value = s->timer[index].reg_tcmpb;
  210. break;
  211. case TCNTO0: case TCNTO1:
  212. case TCNTO2: case TCNTO3: case TCNTO4:
  213. index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
  214. value = ptimer_get_count(s->timer[index].ptimer);
  215. break;
  216. case TINT_CSTAT:
  217. value = s->reg_tint_cstat;
  218. break;
  219. default:
  220. qemu_log_mask(LOG_GUEST_ERROR,
  221. "exynos4210.pwm: bad read offset " HWADDR_FMT_plx,
  222. offset);
  223. break;
  224. }
  225. return value;
  226. }
  227. /*
  228. * PWM Write
  229. */
  230. static void exynos4210_pwm_write(void *opaque, hwaddr offset,
  231. uint64_t value, unsigned size)
  232. {
  233. Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
  234. int index;
  235. uint32_t new_val;
  236. int i;
  237. switch (offset) {
  238. case TCFG0: case TCFG1:
  239. index = (offset - TCFG0) >> 2;
  240. s->reg_tcfg[index] = value;
  241. /* update timers frequencies */
  242. for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
  243. ptimer_transaction_begin(s->timer[i].ptimer);
  244. exynos4210_pwm_update_freq(s, s->timer[i].id);
  245. ptimer_transaction_commit(s->timer[i].ptimer);
  246. }
  247. break;
  248. case TCON:
  249. for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
  250. ptimer_transaction_begin(s->timer[i].ptimer);
  251. if ((value & TCON_TIMER_MANUAL_UPD(i)) >
  252. (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
  253. /*
  254. * TCNTB and TCMPB are loaded into TCNT and TCMP.
  255. * Update timers.
  256. */
  257. /* this will start timer to run, this ok, because
  258. * during processing start bit timer will be stopped
  259. * if needed */
  260. ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
  261. DPRINTF("set timer %d count to %x\n", i,
  262. s->timer[i].reg_tcntb);
  263. }
  264. if ((value & TCON_TIMER_START(i)) >
  265. (s->reg_tcon & TCON_TIMER_START(i))) {
  266. /* changed to start */
  267. ptimer_run(s->timer[i].ptimer, 1);
  268. DPRINTF("run timer %d\n", i);
  269. }
  270. if ((value & TCON_TIMER_START(i)) <
  271. (s->reg_tcon & TCON_TIMER_START(i))) {
  272. /* changed to stop */
  273. ptimer_stop(s->timer[i].ptimer);
  274. DPRINTF("stop timer %d\n", i);
  275. }
  276. ptimer_transaction_commit(s->timer[i].ptimer);
  277. }
  278. s->reg_tcon = value;
  279. break;
  280. case TCNTB0: case TCNTB1:
  281. case TCNTB2: case TCNTB3: case TCNTB4:
  282. index = (offset - TCNTB0) / 0xC;
  283. s->timer[index].reg_tcntb = value;
  284. break;
  285. case TCMPB0: case TCMPB1:
  286. case TCMPB2: case TCMPB3:
  287. index = (offset - TCMPB0) / 0xC;
  288. s->timer[index].reg_tcmpb = value;
  289. break;
  290. case TINT_CSTAT:
  291. new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
  292. new_val &= ~(0x3E0 & value);
  293. for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
  294. if ((new_val & TINT_CSTAT_STATUS(i)) <
  295. (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
  296. qemu_irq_lower(s->timer[i].irq);
  297. }
  298. }
  299. s->reg_tint_cstat = new_val;
  300. break;
  301. default:
  302. qemu_log_mask(LOG_GUEST_ERROR,
  303. "exynos4210.pwm: bad write offset " HWADDR_FMT_plx,
  304. offset);
  305. break;
  306. }
  307. }
  308. /*
  309. * Set default values to timer fields and registers
  310. */
  311. static void exynos4210_pwm_reset(DeviceState *d)
  312. {
  313. Exynos4210PWMState *s = EXYNOS4210_PWM(d);
  314. int i;
  315. s->reg_tcfg[0] = 0x0101;
  316. s->reg_tcfg[1] = 0x0;
  317. s->reg_tcon = 0;
  318. s->reg_tint_cstat = 0;
  319. for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
  320. s->timer[i].reg_tcmpb = 0;
  321. s->timer[i].reg_tcntb = 0;
  322. ptimer_transaction_begin(s->timer[i].ptimer);
  323. exynos4210_pwm_update_freq(s, s->timer[i].id);
  324. ptimer_stop(s->timer[i].ptimer);
  325. ptimer_transaction_commit(s->timer[i].ptimer);
  326. }
  327. }
  328. static const MemoryRegionOps exynos4210_pwm_ops = {
  329. .read = exynos4210_pwm_read,
  330. .write = exynos4210_pwm_write,
  331. .endianness = DEVICE_NATIVE_ENDIAN,
  332. };
  333. /*
  334. * PWM timer initialization
  335. */
  336. static void exynos4210_pwm_init(Object *obj)
  337. {
  338. Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
  339. SysBusDevice *dev = SYS_BUS_DEVICE(obj);
  340. int i;
  341. for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
  342. sysbus_init_irq(dev, &s->timer[i].irq);
  343. s->timer[i].ptimer = ptimer_init(exynos4210_pwm_tick,
  344. &s->timer[i],
  345. PTIMER_POLICY_LEGACY);
  346. s->timer[i].id = i;
  347. s->timer[i].parent = s;
  348. }
  349. memory_region_init_io(&s->iomem, obj, &exynos4210_pwm_ops, s,
  350. "exynos4210-pwm", EXYNOS4210_PWM_REG_MEM_SIZE);
  351. sysbus_init_mmio(dev, &s->iomem);
  352. }
  353. static void exynos4210_pwm_finalize(Object *obj)
  354. {
  355. Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
  356. int i;
  357. for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
  358. ptimer_free(s->timer[i].ptimer);
  359. }
  360. }
  361. static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
  362. {
  363. DeviceClass *dc = DEVICE_CLASS(klass);
  364. device_class_set_legacy_reset(dc, exynos4210_pwm_reset);
  365. dc->vmsd = &vmstate_exynos4210_pwm_state;
  366. }
  367. static const TypeInfo exynos4210_pwm_info = {
  368. .name = TYPE_EXYNOS4210_PWM,
  369. .parent = TYPE_SYS_BUS_DEVICE,
  370. .instance_size = sizeof(Exynos4210PWMState),
  371. .instance_init = exynos4210_pwm_init,
  372. .instance_finalize = exynos4210_pwm_finalize,
  373. .class_init = exynos4210_pwm_class_init,
  374. };
  375. static void exynos4210_pwm_register_types(void)
  376. {
  377. type_register_static(&exynos4210_pwm_info);
  378. }
  379. type_init(exynos4210_pwm_register_types)