xref: /qemu/hw/timer/sh_timer.c (revision fc524567087c2537b5103cdfc1d41e4f442892b6)
1cd1a3f68Sths /*
2cd1a3f68Sths  * SuperH Timer modules.
3cd1a3f68Sths  *
4cd1a3f68Sths  * Copyright (c) 2007 Magnus Damm
5cd1a3f68Sths  * Based on arm_timer.c by Paul Brook
6cd1a3f68Sths  * Copyright (c) 2005-2006 CodeSourcery.
7cd1a3f68Sths  *
88e31bf38SMatthew Fernandez  * This code is licensed under the GPL.
9cd1a3f68Sths  */
10cd1a3f68Sths 
11282bc81eSPeter Maydell #include "qemu/osdep.h"
12*8be545baSRichard Henderson #include "system/memory.h"
1346e44759SBALATON Zoltan #include "qemu/log.h"
1464552b6bSMarkus Armbruster #include "hw/irq.h"
150d09e41aSPaolo Bonzini #include "hw/sh4/sh.h"
1695f4dc44SPhilippe Mathieu-Daudé #include "hw/timer/tmu012.h"
1783c9f4caSPaolo Bonzini #include "hw/ptimer.h"
18ad52cfc1SBALATON Zoltan #include "trace.h"
19cd1a3f68Sths 
20cd1a3f68Sths #define TIMER_TCR_TPSC          (7 << 0)
21cd1a3f68Sths #define TIMER_TCR_CKEG          (3 << 3)
22cd1a3f68Sths #define TIMER_TCR_UNIE          (1 << 5)
23cd1a3f68Sths #define TIMER_TCR_ICPE          (3 << 6)
24cd1a3f68Sths #define TIMER_TCR_UNF           (1 << 8)
25cd1a3f68Sths #define TIMER_TCR_ICPF          (1 << 9)
26cd1a3f68Sths #define TIMER_TCR_RESERVED      (0x3f << 10)
27cd1a3f68Sths 
28cd1a3f68Sths #define TIMER_FEAT_CAPT   (1 << 0)
29cd1a3f68Sths #define TIMER_FEAT_EXTCLK (1 << 1)
30cd1a3f68Sths 
31e7786f27Saurel32 #define OFFSET_TCOR   0
32e7786f27Saurel32 #define OFFSET_TCNT   1
33e7786f27Saurel32 #define OFFSET_TCR    2
34e7786f27Saurel32 #define OFFSET_TCPR   3
35e7786f27Saurel32 
36cd1a3f68Sths typedef struct {
37cd1a3f68Sths     ptimer_state *timer;
38cd1a3f68Sths     uint32_t tcnt;
39cd1a3f68Sths     uint32_t tcor;
40cd1a3f68Sths     uint32_t tcr;
41cd1a3f68Sths     uint32_t tcpr;
42cd1a3f68Sths     int freq;
43cd1a3f68Sths     int int_level;
44703243a0Sbalrog     int old_level;
45cd1a3f68Sths     int feat;
46cd1a3f68Sths     int enabled;
4796e2fc41Saurel32     qemu_irq irq;
485d9b737eSBALATON Zoltan } SHTimerState;
49cd1a3f68Sths 
50cd1a3f68Sths /* Check all active timers, and schedule the next timer interrupt. */
51cd1a3f68Sths 
sh_timer_update(SHTimerState * s)525d9b737eSBALATON Zoltan static void sh_timer_update(SHTimerState *s)
53cd1a3f68Sths {
54703243a0Sbalrog     int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
55703243a0Sbalrog 
56ac3c9e74SBALATON Zoltan     if (new_level != s->old_level) {
5796e2fc41Saurel32         qemu_set_irq(s->irq, new_level);
58ac3c9e74SBALATON Zoltan     }
59703243a0Sbalrog     s->old_level = s->int_level;
60703243a0Sbalrog     s->int_level = new_level;
61cd1a3f68Sths }
62cd1a3f68Sths 
sh_timer_read(void * opaque,hwaddr offset)63a8170e5eSAvi Kivity static uint32_t sh_timer_read(void *opaque, hwaddr offset)
64cd1a3f68Sths {
655d9b737eSBALATON Zoltan     SHTimerState *s = opaque;
66cd1a3f68Sths 
67cd1a3f68Sths     switch (offset >> 2) {
68e7786f27Saurel32     case OFFSET_TCOR:
69cd1a3f68Sths         return s->tcor;
70e7786f27Saurel32     case OFFSET_TCNT:
71cd1a3f68Sths         return ptimer_get_count(s->timer);
72e7786f27Saurel32     case OFFSET_TCR:
73cd1a3f68Sths         return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
74e7786f27Saurel32     case OFFSET_TCPR:
75ac3c9e74SBALATON Zoltan         if (s->feat & TIMER_FEAT_CAPT) {
76cd1a3f68Sths             return s->tcpr;
77ac3c9e74SBALATON Zoltan         }
78cd1a3f68Sths     }
7946e44759SBALATON Zoltan     qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
8046e44759SBALATON Zoltan                   __func__, offset);
8146e44759SBALATON Zoltan     return 0;
82cd1a3f68Sths }
83cd1a3f68Sths 
sh_timer_write(void * opaque,hwaddr offset,uint32_t value)84f64ccec4SBALATON Zoltan static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value)
85cd1a3f68Sths {
865d9b737eSBALATON Zoltan     SHTimerState *s = opaque;
87cd1a3f68Sths     int freq;
88cd1a3f68Sths 
89cd1a3f68Sths     switch (offset >> 2) {
90e7786f27Saurel32     case OFFSET_TCOR:
91cd1a3f68Sths         s->tcor = value;
9228015830SPeter Maydell         ptimer_transaction_begin(s->timer);
93cd1a3f68Sths         ptimer_set_limit(s->timer, s->tcor, 0);
9428015830SPeter Maydell         ptimer_transaction_commit(s->timer);
95cd1a3f68Sths         break;
96e7786f27Saurel32     case OFFSET_TCNT:
97cd1a3f68Sths         s->tcnt = value;
9828015830SPeter Maydell         ptimer_transaction_begin(s->timer);
99cd1a3f68Sths         ptimer_set_count(s->timer, s->tcnt);
10028015830SPeter Maydell         ptimer_transaction_commit(s->timer);
101cd1a3f68Sths         break;
102e7786f27Saurel32     case OFFSET_TCR:
10328015830SPeter Maydell         ptimer_transaction_begin(s->timer);
104cd1a3f68Sths         if (s->enabled) {
10522138965SBALATON Zoltan             /*
10622138965SBALATON Zoltan              * Pause the timer if it is running. This may cause some inaccuracy
1073b885dabSBALATON Zoltan              * due to rounding, but avoids a whole lot of other messiness
10822138965SBALATON Zoltan              */
109cd1a3f68Sths             ptimer_stop(s->timer);
110cd1a3f68Sths         }
111cd1a3f68Sths         freq = s->freq;
112cd1a3f68Sths         /* ??? Need to recalculate expiry time after changing divisor.  */
113cd1a3f68Sths         switch (value & TIMER_TCR_TPSC) {
114f94bff13SBALATON Zoltan         case 0:
115f94bff13SBALATON Zoltan             freq >>= 2;
116f94bff13SBALATON Zoltan             break;
117f94bff13SBALATON Zoltan         case 1:
118f94bff13SBALATON Zoltan             freq >>= 4;
119f94bff13SBALATON Zoltan             break;
120f94bff13SBALATON Zoltan         case 2:
121f94bff13SBALATON Zoltan             freq >>= 6;
122f94bff13SBALATON Zoltan             break;
123f94bff13SBALATON Zoltan         case 3:
124f94bff13SBALATON Zoltan             freq >>= 8;
125f94bff13SBALATON Zoltan             break;
126f94bff13SBALATON Zoltan         case 4:
127f94bff13SBALATON Zoltan             freq >>= 10;
128f94bff13SBALATON Zoltan             break;
129cd1a3f68Sths         case 6:
1302f5af2dcSThomas Huth         case 7:
1312f5af2dcSThomas Huth             if (s->feat & TIMER_FEAT_EXTCLK) {
1322f5af2dcSThomas Huth                 break;
1332f5af2dcSThomas Huth             }
13497edd8baSThomas Huth             /* fallthrough */
1352f5af2dcSThomas Huth         default:
13646e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
13746e44759SBALATON Zoltan                           "%s: Reserved TPSC value\n", __func__);
138cd1a3f68Sths         }
139cd1a3f68Sths         switch ((value & TIMER_TCR_CKEG) >> 3) {
1402f5af2dcSThomas Huth         case 0:
1412f5af2dcSThomas Huth             break;
142cd1a3f68Sths         case 1:
143cd1a3f68Sths         case 2:
1442f5af2dcSThomas Huth         case 3:
1452f5af2dcSThomas Huth             if (s->feat & TIMER_FEAT_EXTCLK) {
1462f5af2dcSThomas Huth                 break;
1472f5af2dcSThomas Huth             }
14897edd8baSThomas Huth             /* fallthrough */
1492f5af2dcSThomas Huth         default:
15046e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
15146e44759SBALATON Zoltan                           "%s: Reserved CKEG value\n", __func__);
152cd1a3f68Sths         }
153cd1a3f68Sths         switch ((value & TIMER_TCR_ICPE) >> 6) {
1542f5af2dcSThomas Huth         case 0:
1552f5af2dcSThomas Huth             break;
156cd1a3f68Sths         case 2:
1572f5af2dcSThomas Huth         case 3:
1582f5af2dcSThomas Huth             if (s->feat & TIMER_FEAT_CAPT) {
1592f5af2dcSThomas Huth                 break;
160cd1a3f68Sths             }
16197edd8baSThomas Huth             /* fallthrough */
1622f5af2dcSThomas Huth         default:
16346e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
16446e44759SBALATON Zoltan                           "%s: Reserved ICPE value\n", __func__);
1652f5af2dcSThomas Huth         }
1662f5af2dcSThomas Huth         if ((value & TIMER_TCR_UNF) == 0) {
167cd1a3f68Sths             s->int_level = 0;
1682f5af2dcSThomas Huth         }
169cd1a3f68Sths 
170cd1a3f68Sths         value &= ~TIMER_TCR_UNF;
171cd1a3f68Sths 
1722f5af2dcSThomas Huth         if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) {
17346e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
17446e44759SBALATON Zoltan                           "%s: Reserved ICPF value\n", __func__);
1752f5af2dcSThomas Huth         }
176cd1a3f68Sths 
177cd1a3f68Sths         value &= ~TIMER_TCR_ICPF; /* capture not supported */
178cd1a3f68Sths 
1792f5af2dcSThomas Huth         if (value & TIMER_TCR_RESERVED) {
18046e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
18146e44759SBALATON Zoltan                           "%s: Reserved TCR bits set\n", __func__);
1822f5af2dcSThomas Huth         }
183cd1a3f68Sths         s->tcr = value;
184cd1a3f68Sths         ptimer_set_limit(s->timer, s->tcor, 0);
185cd1a3f68Sths         ptimer_set_freq(s->timer, freq);
186cd1a3f68Sths         if (s->enabled) {
187cd1a3f68Sths             /* Restart the timer if still enabled.  */
188cd1a3f68Sths             ptimer_run(s->timer, 0);
189cd1a3f68Sths         }
19028015830SPeter Maydell         ptimer_transaction_commit(s->timer);
191cd1a3f68Sths         break;
192e7786f27Saurel32     case OFFSET_TCPR:
193cd1a3f68Sths         if (s->feat & TIMER_FEAT_CAPT) {
194cd1a3f68Sths             s->tcpr = value;
195cd1a3f68Sths             break;
196cd1a3f68Sths         }
19797edd8baSThomas Huth         /* fallthrough */
198cd1a3f68Sths     default:
19946e44759SBALATON Zoltan         qemu_log_mask(LOG_GUEST_ERROR,
20046e44759SBALATON Zoltan                       "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
201cd1a3f68Sths     }
202cd1a3f68Sths     sh_timer_update(s);
203cd1a3f68Sths }
204cd1a3f68Sths 
sh_timer_start_stop(void * opaque,int enable)205cd1a3f68Sths static void sh_timer_start_stop(void *opaque, int enable)
206cd1a3f68Sths {
2075d9b737eSBALATON Zoltan     SHTimerState *s = opaque;
208cd1a3f68Sths 
209ad52cfc1SBALATON Zoltan     trace_sh_timer_start_stop(enable, s->enabled);
21028015830SPeter Maydell     ptimer_transaction_begin(s->timer);
211cd1a3f68Sths     if (s->enabled && !enable) {
212cd1a3f68Sths         ptimer_stop(s->timer);
213cd1a3f68Sths     }
214cd1a3f68Sths     if (!s->enabled && enable) {
215cd1a3f68Sths         ptimer_run(s->timer, 0);
216cd1a3f68Sths     }
21728015830SPeter Maydell     ptimer_transaction_commit(s->timer);
218cd1a3f68Sths     s->enabled = !!enable;
219cd1a3f68Sths }
220cd1a3f68Sths 
sh_timer_tick(void * opaque)221cd1a3f68Sths static void sh_timer_tick(void *opaque)
222cd1a3f68Sths {
2235d9b737eSBALATON Zoltan     SHTimerState *s = opaque;
224cd1a3f68Sths     s->int_level = s->enabled;
225cd1a3f68Sths     sh_timer_update(s);
226cd1a3f68Sths }
227cd1a3f68Sths 
sh_timer_init(uint32_t freq,int feat,qemu_irq irq)22896e2fc41Saurel32 static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
229cd1a3f68Sths {
2305d9b737eSBALATON Zoltan     SHTimerState *s;
231cd1a3f68Sths 
232373b96b9SBALATON Zoltan     s = g_malloc0(sizeof(*s));
233cd1a3f68Sths     s->freq = freq;
234cd1a3f68Sths     s->feat = feat;
235cd1a3f68Sths     s->tcor = 0xffffffff;
236cd1a3f68Sths     s->tcnt = 0xffffffff;
237cd1a3f68Sths     s->tcpr = 0xdeadbeef;
238e7786f27Saurel32     s->tcr = 0;
239cd1a3f68Sths     s->enabled = 0;
240703243a0Sbalrog     s->irq = irq;
241cd1a3f68Sths 
2429598c1bbSPeter Maydell     s->timer = ptimer_init(sh_timer_tick, s, PTIMER_POLICY_LEGACY);
243e7786f27Saurel32 
244e7786f27Saurel32     sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
245e7786f27Saurel32     sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
246e7786f27Saurel32     sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
247e7786f27Saurel32     sh_timer_write(s, OFFSET_TCR  >> 2, s->tcpr);
248cd1a3f68Sths     /* ??? Save/restore.  */
249cd1a3f68Sths     return s;
250cd1a3f68Sths }
251cd1a3f68Sths 
252cd1a3f68Sths typedef struct {
25389e29451SBenoît Canet     MemoryRegion iomem;
25489e29451SBenoît Canet     MemoryRegion iomem_p4;
25589e29451SBenoît Canet     MemoryRegion iomem_a7;
256cd1a3f68Sths     void *timer[3];
257cd1a3f68Sths     int level[3];
258cd1a3f68Sths     uint32_t tocr;
259cd1a3f68Sths     uint32_t tstr;
260cd1a3f68Sths     int feat;
261cd1a3f68Sths } tmu012_state;
262cd1a3f68Sths 
tmu012_read(void * opaque,hwaddr offset,unsigned size)263f64ccec4SBALATON Zoltan static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size)
264cd1a3f68Sths {
2655d9b737eSBALATON Zoltan     tmu012_state *s = opaque;
266cd1a3f68Sths 
267ad52cfc1SBALATON Zoltan     trace_sh_timer_read(offset);
268cd1a3f68Sths     if (offset >= 0x20) {
2692f5af2dcSThomas Huth         if (!(s->feat & TMU012_FEAT_3CHAN)) {
27046e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
27146e44759SBALATON Zoltan                           "%s: Bad channel offset 0x%" HWADDR_PRIx "\n",
27246e44759SBALATON Zoltan                           __func__, offset);
2732f5af2dcSThomas Huth         }
274cd1a3f68Sths         return sh_timer_read(s->timer[2], offset - 0x20);
275cd1a3f68Sths     }
276cd1a3f68Sths 
277ac3c9e74SBALATON Zoltan     if (offset >= 0x14) {
278cd1a3f68Sths         return sh_timer_read(s->timer[1], offset - 0x14);
279ac3c9e74SBALATON Zoltan     }
280ac3c9e74SBALATON Zoltan     if (offset >= 0x08) {
281cd1a3f68Sths         return sh_timer_read(s->timer[0], offset - 0x08);
282ac3c9e74SBALATON Zoltan     }
283ac3c9e74SBALATON Zoltan     if (offset == 4) {
284cd1a3f68Sths         return s->tstr;
285ac3c9e74SBALATON Zoltan     }
286ac3c9e74SBALATON Zoltan     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
287cd1a3f68Sths         return s->tocr;
288ac3c9e74SBALATON Zoltan     }
289cd1a3f68Sths 
29046e44759SBALATON Zoltan     qemu_log_mask(LOG_GUEST_ERROR,
29146e44759SBALATON Zoltan                   "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
292cd1a3f68Sths     return 0;
293cd1a3f68Sths }
294cd1a3f68Sths 
tmu012_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)295a8170e5eSAvi Kivity static void tmu012_write(void *opaque, hwaddr offset,
29689e29451SBenoît Canet                         uint64_t value, unsigned size)
297cd1a3f68Sths {
2985d9b737eSBALATON Zoltan     tmu012_state *s = opaque;
299cd1a3f68Sths 
300ad52cfc1SBALATON Zoltan     trace_sh_timer_write(offset, value);
301cd1a3f68Sths     if (offset >= 0x20) {
3022f5af2dcSThomas Huth         if (!(s->feat & TMU012_FEAT_3CHAN)) {
30346e44759SBALATON Zoltan             qemu_log_mask(LOG_GUEST_ERROR,
30446e44759SBALATON Zoltan                           "%s: Bad channel offset 0x%" HWADDR_PRIx "\n",
30546e44759SBALATON Zoltan                           __func__, offset);
3062f5af2dcSThomas Huth         }
307cd1a3f68Sths         sh_timer_write(s->timer[2], offset - 0x20, value);
308cd1a3f68Sths         return;
309cd1a3f68Sths     }
310cd1a3f68Sths 
311cd1a3f68Sths     if (offset >= 0x14) {
312cd1a3f68Sths         sh_timer_write(s->timer[1], offset - 0x14, value);
313cd1a3f68Sths         return;
314cd1a3f68Sths     }
315cd1a3f68Sths 
316cd1a3f68Sths     if (offset >= 0x08) {
317cd1a3f68Sths         sh_timer_write(s->timer[0], offset - 0x08, value);
318cd1a3f68Sths         return;
319cd1a3f68Sths     }
320cd1a3f68Sths 
321cd1a3f68Sths     if (offset == 4) {
322cd1a3f68Sths         sh_timer_start_stop(s->timer[0], value & (1 << 0));
323cd1a3f68Sths         sh_timer_start_stop(s->timer[1], value & (1 << 1));
3242f5af2dcSThomas Huth         if (s->feat & TMU012_FEAT_3CHAN) {
325cd1a3f68Sths             sh_timer_start_stop(s->timer[2], value & (1 << 2));
3262f5af2dcSThomas Huth         } else {
3272f5af2dcSThomas Huth             if (value & (1 << 2)) {
32846e44759SBALATON Zoltan                 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad channel\n", __func__);
3292f5af2dcSThomas Huth             }
3302f5af2dcSThomas Huth         }
331cd1a3f68Sths 
332cd1a3f68Sths         s->tstr = value;
333cd1a3f68Sths         return;
334cd1a3f68Sths     }
335cd1a3f68Sths 
336cd1a3f68Sths     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
337cd1a3f68Sths         s->tocr = value & (1 << 0);
338cd1a3f68Sths     }
339cd1a3f68Sths }
340cd1a3f68Sths 
34189e29451SBenoît Canet static const MemoryRegionOps tmu012_ops = {
34289e29451SBenoît Canet     .read = tmu012_read,
34389e29451SBenoît Canet     .write = tmu012_write,
34489e29451SBenoît Canet     .endianness = DEVICE_NATIVE_ENDIAN,
345cd1a3f68Sths };
346cd1a3f68Sths 
tmu012_init(MemoryRegion * sysmem,hwaddr base,int feat,uint32_t freq,qemu_irq ch0_irq,qemu_irq ch1_irq,qemu_irq ch2_irq0,qemu_irq ch2_irq1)347f64ccec4SBALATON Zoltan void tmu012_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq,
34896e2fc41Saurel32                  qemu_irq ch0_irq, qemu_irq ch1_irq,
34996e2fc41Saurel32                  qemu_irq ch2_irq0, qemu_irq ch2_irq1)
350cd1a3f68Sths {
351cd1a3f68Sths     tmu012_state *s;
352cd1a3f68Sths     int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
353cd1a3f68Sths 
354373b96b9SBALATON Zoltan     s = g_malloc0(sizeof(*s));
355cd1a3f68Sths     s->feat = feat;
356703243a0Sbalrog     s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
357703243a0Sbalrog     s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
3582f5af2dcSThomas Huth     if (feat & TMU012_FEAT_3CHAN) {
359703243a0Sbalrog         s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
360703243a0Sbalrog                                     ch2_irq0); /* ch2_irq1 not supported */
3612f5af2dcSThomas Huth     }
36289e29451SBenoît Canet 
36365307c77SBALATON Zoltan     memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, "timer", 0x30);
36489e29451SBenoît Canet 
3652c9b15caSPaolo Bonzini     memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4",
36665307c77SBALATON Zoltan                              &s->iomem, 0, memory_region_size(&s->iomem));
36789e29451SBenoît Canet     memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
36889e29451SBenoît Canet 
3692c9b15caSPaolo Bonzini     memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7",
37065307c77SBALATON Zoltan                              &s->iomem, 0, memory_region_size(&s->iomem));
37189e29451SBenoît Canet     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
372cd1a3f68Sths     /* ??? Save/restore.  */
373cd1a3f68Sths }
374