xref: /openbmc/qemu/hw/timer/sh_timer.c (revision 56983463)
1 /*
2  * SuperH Timer modules.
3  *
4  * Copyright (c) 2007 Magnus Damm
5  * Based on arm_timer.c by Paul Brook
6  * Copyright (c) 2005-2006 CodeSourcery.
7  *
8  * This code is licensed under the GPL.
9  */
10 
11 #include "hw/hw.h"
12 #include "hw/sh4/sh.h"
13 #include "qemu/timer.h"
14 #include "exec/address-spaces.h"
15 #include "hw/ptimer.h"
16 
17 //#define DEBUG_TIMER
18 
19 #define TIMER_TCR_TPSC          (7 << 0)
20 #define TIMER_TCR_CKEG          (3 << 3)
21 #define TIMER_TCR_UNIE          (1 << 5)
22 #define TIMER_TCR_ICPE          (3 << 6)
23 #define TIMER_TCR_UNF           (1 << 8)
24 #define TIMER_TCR_ICPF          (1 << 9)
25 #define TIMER_TCR_RESERVED      (0x3f << 10)
26 
27 #define TIMER_FEAT_CAPT   (1 << 0)
28 #define TIMER_FEAT_EXTCLK (1 << 1)
29 
30 #define OFFSET_TCOR   0
31 #define OFFSET_TCNT   1
32 #define OFFSET_TCR    2
33 #define OFFSET_TCPR   3
34 
35 typedef struct {
36     ptimer_state *timer;
37     uint32_t tcnt;
38     uint32_t tcor;
39     uint32_t tcr;
40     uint32_t tcpr;
41     int freq;
42     int int_level;
43     int old_level;
44     int feat;
45     int enabled;
46     qemu_irq irq;
47 } sh_timer_state;
48 
49 /* Check all active timers, and schedule the next timer interrupt. */
50 
51 static void sh_timer_update(sh_timer_state *s)
52 {
53     int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
54 
55     if (new_level != s->old_level)
56       qemu_set_irq (s->irq, new_level);
57 
58     s->old_level = s->int_level;
59     s->int_level = new_level;
60 }
61 
62 static uint32_t sh_timer_read(void *opaque, hwaddr offset)
63 {
64     sh_timer_state *s = (sh_timer_state *)opaque;
65 
66     switch (offset >> 2) {
67     case OFFSET_TCOR:
68         return s->tcor;
69     case OFFSET_TCNT:
70         return ptimer_get_count(s->timer);
71     case OFFSET_TCR:
72         return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
73     case OFFSET_TCPR:
74         if (s->feat & TIMER_FEAT_CAPT)
75             return s->tcpr;
76     default:
77         hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
78         return 0;
79     }
80 }
81 
82 static void sh_timer_write(void *opaque, hwaddr offset,
83                             uint32_t value)
84 {
85     sh_timer_state *s = (sh_timer_state *)opaque;
86     int freq;
87 
88     switch (offset >> 2) {
89     case OFFSET_TCOR:
90         s->tcor = value;
91         ptimer_set_limit(s->timer, s->tcor, 0);
92         break;
93     case OFFSET_TCNT:
94         s->tcnt = value;
95         ptimer_set_count(s->timer, s->tcnt);
96         break;
97     case OFFSET_TCR:
98         if (s->enabled) {
99             /* Pause the timer if it is running.  This may cause some
100                inaccuracy dure to rounding, but avoids a whole lot of other
101                messyness.  */
102             ptimer_stop(s->timer);
103         }
104         freq = s->freq;
105         /* ??? Need to recalculate expiry time after changing divisor.  */
106         switch (value & TIMER_TCR_TPSC) {
107         case 0: freq >>= 2; break;
108         case 1: freq >>= 4; break;
109         case 2: freq >>= 6; break;
110         case 3: freq >>= 8; break;
111         case 4: freq >>= 10; break;
112 	case 6:
113 	case 7: if (s->feat & TIMER_FEAT_EXTCLK) break;
114 	default: hw_error("sh_timer_write: Reserved TPSC value\n"); break;
115         }
116         switch ((value & TIMER_TCR_CKEG) >> 3) {
117 	case 0: break;
118         case 1:
119         case 2:
120         case 3: if (s->feat & TIMER_FEAT_EXTCLK) break;
121 	default: hw_error("sh_timer_write: Reserved CKEG value\n"); break;
122         }
123         switch ((value & TIMER_TCR_ICPE) >> 6) {
124 	case 0: break;
125         case 2:
126         case 3: if (s->feat & TIMER_FEAT_CAPT) break;
127 	default: hw_error("sh_timer_write: Reserved ICPE value\n"); break;
128         }
129 	if ((value & TIMER_TCR_UNF) == 0)
130             s->int_level = 0;
131 
132 	value &= ~TIMER_TCR_UNF;
133 
134 	if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT)))
135             hw_error("sh_timer_write: Reserved ICPF value\n");
136 
137 	value &= ~TIMER_TCR_ICPF; /* capture not supported */
138 
139 	if (value & TIMER_TCR_RESERVED)
140             hw_error("sh_timer_write: Reserved TCR bits set\n");
141         s->tcr = value;
142         ptimer_set_limit(s->timer, s->tcor, 0);
143         ptimer_set_freq(s->timer, freq);
144         if (s->enabled) {
145             /* Restart the timer if still enabled.  */
146             ptimer_run(s->timer, 0);
147         }
148         break;
149     case OFFSET_TCPR:
150         if (s->feat & TIMER_FEAT_CAPT) {
151             s->tcpr = value;
152 	    break;
153 	}
154     default:
155         hw_error("sh_timer_write: Bad offset %x\n", (int)offset);
156     }
157     sh_timer_update(s);
158 }
159 
160 static void sh_timer_start_stop(void *opaque, int enable)
161 {
162     sh_timer_state *s = (sh_timer_state *)opaque;
163 
164 #ifdef DEBUG_TIMER
165     printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
166 #endif
167 
168     if (s->enabled && !enable) {
169         ptimer_stop(s->timer);
170     }
171     if (!s->enabled && enable) {
172         ptimer_run(s->timer, 0);
173     }
174     s->enabled = !!enable;
175 
176 #ifdef DEBUG_TIMER
177     printf("sh_timer_start_stop done %d\n", s->enabled);
178 #endif
179 }
180 
181 static void sh_timer_tick(void *opaque)
182 {
183     sh_timer_state *s = (sh_timer_state *)opaque;
184     s->int_level = s->enabled;
185     sh_timer_update(s);
186 }
187 
188 static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
189 {
190     sh_timer_state *s;
191     QEMUBH *bh;
192 
193     s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state));
194     s->freq = freq;
195     s->feat = feat;
196     s->tcor = 0xffffffff;
197     s->tcnt = 0xffffffff;
198     s->tcpr = 0xdeadbeef;
199     s->tcr = 0;
200     s->enabled = 0;
201     s->irq = irq;
202 
203     bh = qemu_bh_new(sh_timer_tick, s);
204     s->timer = ptimer_init(bh);
205 
206     sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
207     sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
208     sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
209     sh_timer_write(s, OFFSET_TCR  >> 2, s->tcpr);
210     /* ??? Save/restore.  */
211     return s;
212 }
213 
214 typedef struct {
215     MemoryRegion iomem;
216     MemoryRegion iomem_p4;
217     MemoryRegion iomem_a7;
218     void *timer[3];
219     int level[3];
220     uint32_t tocr;
221     uint32_t tstr;
222     int feat;
223 } tmu012_state;
224 
225 static uint64_t tmu012_read(void *opaque, hwaddr offset,
226                             unsigned size)
227 {
228     tmu012_state *s = (tmu012_state *)opaque;
229 
230 #ifdef DEBUG_TIMER
231     printf("tmu012_read 0x%lx\n", (unsigned long) offset);
232 #endif
233 
234     if (offset >= 0x20) {
235         if (!(s->feat & TMU012_FEAT_3CHAN))
236 	    hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
237         return sh_timer_read(s->timer[2], offset - 0x20);
238     }
239 
240     if (offset >= 0x14)
241         return sh_timer_read(s->timer[1], offset - 0x14);
242 
243     if (offset >= 0x08)
244         return sh_timer_read(s->timer[0], offset - 0x08);
245 
246     if (offset == 4)
247         return s->tstr;
248 
249     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
250         return s->tocr;
251 
252     hw_error("tmu012_write: Bad offset %x\n", (int)offset);
253     return 0;
254 }
255 
256 static void tmu012_write(void *opaque, hwaddr offset,
257                         uint64_t value, unsigned size)
258 {
259     tmu012_state *s = (tmu012_state *)opaque;
260 
261 #ifdef DEBUG_TIMER
262     printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
263 #endif
264 
265     if (offset >= 0x20) {
266         if (!(s->feat & TMU012_FEAT_3CHAN))
267 	    hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
268         sh_timer_write(s->timer[2], offset - 0x20, value);
269 	return;
270     }
271 
272     if (offset >= 0x14) {
273         sh_timer_write(s->timer[1], offset - 0x14, value);
274 	return;
275     }
276 
277     if (offset >= 0x08) {
278         sh_timer_write(s->timer[0], offset - 0x08, value);
279 	return;
280     }
281 
282     if (offset == 4) {
283         sh_timer_start_stop(s->timer[0], value & (1 << 0));
284         sh_timer_start_stop(s->timer[1], value & (1 << 1));
285         if (s->feat & TMU012_FEAT_3CHAN)
286             sh_timer_start_stop(s->timer[2], value & (1 << 2));
287 	else
288             if (value & (1 << 2))
289                 hw_error("tmu012_write: Bad channel\n");
290 
291 	s->tstr = value;
292 	return;
293     }
294 
295     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
296         s->tocr = value & (1 << 0);
297     }
298 }
299 
300 static const MemoryRegionOps tmu012_ops = {
301     .read = tmu012_read,
302     .write = tmu012_write,
303     .endianness = DEVICE_NATIVE_ENDIAN,
304 };
305 
306 void tmu012_init(MemoryRegion *sysmem, hwaddr base,
307                  int feat, uint32_t freq,
308 		 qemu_irq ch0_irq, qemu_irq ch1_irq,
309 		 qemu_irq ch2_irq0, qemu_irq ch2_irq1)
310 {
311     tmu012_state *s;
312     int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
313 
314     s = (tmu012_state *)g_malloc0(sizeof(tmu012_state));
315     s->feat = feat;
316     s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
317     s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
318     if (feat & TMU012_FEAT_3CHAN)
319         s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
320 				    ch2_irq0); /* ch2_irq1 not supported */
321 
322     memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s,
323                           "timer", 0x100000000ULL);
324 
325     memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4",
326                              &s->iomem, 0, 0x1000);
327     memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
328 
329     memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7",
330                              &s->iomem, 0, 0x1000);
331     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
332     /* ??? Save/restore.  */
333 }
334