xref: /qemu/hw/timer/omap_gptimer.c (revision 6402cbbb)
1 /*
2  * TI OMAP2 general purpose timers emulation.
3  *
4  * Copyright (C) 2007-2008 Nokia Corporation
5  * Written by Andrzej Zaborowski <andrew@openedhand.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 or
10  * (at your option) any later version of the License.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 #include "qemu/osdep.h"
21 #include "hw/hw.h"
22 #include "qemu/timer.h"
23 #include "hw/arm/omap.h"
24 
25 /* GP timers */
26 struct omap_gp_timer_s {
27     MemoryRegion iomem;
28     qemu_irq irq;
29     qemu_irq wkup;
30     qemu_irq in;
31     qemu_irq out;
32     omap_clk clk;
33     QEMUTimer *timer;
34     QEMUTimer *match;
35     struct omap_target_agent_s *ta;
36 
37     int in_val;
38     int out_val;
39     int64_t time;
40     int64_t rate;
41     int64_t ticks_per_sec;
42 
43     int16_t config;
44     int status;
45     int it_ena;
46     int wu_ena;
47     int enable;
48     int inout;
49     int capt2;
50     int pt;
51     enum {
52         gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both
53     } trigger;
54     enum {
55         gpt_capture_none, gpt_capture_rising,
56         gpt_capture_falling, gpt_capture_both
57     } capture;
58     int scpwm;
59     int ce;
60     int pre;
61     int ptv;
62     int ar;
63     int st;
64     int posted;
65     uint32_t val;
66     uint32_t load_val;
67     uint32_t capture_val[2];
68     uint32_t match_val;
69     int capt_num;
70 
71     uint16_t writeh;	/* LSB */
72     uint16_t readh;	/* MSB */
73 };
74 
75 #define GPT_TCAR_IT	(1 << 2)
76 #define GPT_OVF_IT	(1 << 1)
77 #define GPT_MAT_IT	(1 << 0)
78 
79 static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it)
80 {
81     if (timer->it_ena & it) {
82         if (!timer->status)
83             qemu_irq_raise(timer->irq);
84 
85         timer->status |= it;
86         /* Or are the status bits set even when masked?
87          * i.e. is masking applied before or after the status register?  */
88     }
89 
90     if (timer->wu_ena & it)
91         qemu_irq_pulse(timer->wkup);
92 }
93 
94 static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level)
95 {
96     if (!timer->inout && timer->out_val != level) {
97         timer->out_val = level;
98         qemu_set_irq(timer->out, level);
99     }
100 }
101 
102 static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer)
103 {
104     uint64_t distance;
105 
106     if (timer->st && timer->rate) {
107         distance = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->time;
108         distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
109 
110         if (distance >= 0xffffffff - timer->val)
111             return 0xffffffff;
112         else
113             return timer->val + distance;
114     } else
115         return timer->val;
116 }
117 
118 static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer)
119 {
120     if (timer->st) {
121         timer->val = omap_gp_timer_read(timer);
122         timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
123     }
124 }
125 
126 static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer)
127 {
128     int64_t expires, matches;
129 
130     if (timer->st && timer->rate) {
131         expires = muldiv64(0x100000000ll - timer->val,
132                         timer->ticks_per_sec, timer->rate);
133         timer_mod(timer->timer, timer->time + expires);
134 
135         if (timer->ce && timer->match_val >= timer->val) {
136             matches = muldiv64(timer->ticks_per_sec,
137                                timer->match_val - timer->val, timer->rate);
138             timer_mod(timer->match, timer->time + matches);
139         } else
140             timer_del(timer->match);
141     } else {
142         timer_del(timer->timer);
143         timer_del(timer->match);
144         omap_gp_timer_out(timer, timer->scpwm);
145     }
146 }
147 
148 static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer)
149 {
150     if (timer->pt)
151         /* TODO in overflow-and-match mode if the first event to
152          * occur is the match, don't toggle.  */
153         omap_gp_timer_out(timer, !timer->out_val);
154     else
155         /* TODO inverted pulse on timer->out_val == 1?  */
156         qemu_irq_pulse(timer->out);
157 }
158 
159 static void omap_gp_timer_tick(void *opaque)
160 {
161     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
162 
163     if (!timer->ar) {
164         timer->st = 0;
165         timer->val = 0;
166     } else {
167         timer->val = timer->load_val;
168         timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
169     }
170 
171     if (timer->trigger == gpt_trigger_overflow ||
172                     timer->trigger == gpt_trigger_both)
173         omap_gp_timer_trigger(timer);
174 
175     omap_gp_timer_intr(timer, GPT_OVF_IT);
176     omap_gp_timer_update(timer);
177 }
178 
179 static void omap_gp_timer_match(void *opaque)
180 {
181     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
182 
183     if (timer->trigger == gpt_trigger_both)
184         omap_gp_timer_trigger(timer);
185 
186     omap_gp_timer_intr(timer, GPT_MAT_IT);
187 }
188 
189 static void omap_gp_timer_input(void *opaque, int line, int on)
190 {
191     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
192     int trigger;
193 
194     switch (s->capture) {
195     default:
196     case gpt_capture_none:
197         trigger = 0;
198         break;
199     case gpt_capture_rising:
200         trigger = !s->in_val && on;
201         break;
202     case gpt_capture_falling:
203         trigger = s->in_val && !on;
204         break;
205     case gpt_capture_both:
206         trigger = (s->in_val == !on);
207         break;
208     }
209     s->in_val = on;
210 
211     if (s->inout && trigger && s->capt_num < 2) {
212         s->capture_val[s->capt_num] = omap_gp_timer_read(s);
213 
214         if (s->capt2 == s->capt_num ++)
215             omap_gp_timer_intr(s, GPT_TCAR_IT);
216     }
217 }
218 
219 static void omap_gp_timer_clk_update(void *opaque, int line, int on)
220 {
221     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
222 
223     omap_gp_timer_sync(timer);
224     timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
225     omap_gp_timer_update(timer);
226 }
227 
228 static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer)
229 {
230     omap_clk_adduser(timer->clk,
231                      qemu_allocate_irq(omap_gp_timer_clk_update, timer, 0));
232     timer->rate = omap_clk_getrate(timer->clk);
233 }
234 
235 void omap_gp_timer_reset(struct omap_gp_timer_s *s)
236 {
237     s->config = 0x000;
238     s->status = 0;
239     s->it_ena = 0;
240     s->wu_ena = 0;
241     s->inout = 0;
242     s->capt2 = 0;
243     s->capt_num = 0;
244     s->pt = 0;
245     s->trigger = gpt_trigger_none;
246     s->capture = gpt_capture_none;
247     s->scpwm = 0;
248     s->ce = 0;
249     s->pre = 0;
250     s->ptv = 0;
251     s->ar = 0;
252     s->st = 0;
253     s->posted = 1;
254     s->val = 0x00000000;
255     s->load_val = 0x00000000;
256     s->capture_val[0] = 0x00000000;
257     s->capture_val[1] = 0x00000000;
258     s->match_val = 0x00000000;
259     omap_gp_timer_update(s);
260 }
261 
262 static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr)
263 {
264     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
265 
266     switch (addr) {
267     case 0x00:	/* TIDR */
268         return 0x21;
269 
270     case 0x10:	/* TIOCP_CFG */
271         return s->config;
272 
273     case 0x14:	/* TISTAT */
274         /* ??? When's this bit reset? */
275         return 1;						/* RESETDONE */
276 
277     case 0x18:	/* TISR */
278         return s->status;
279 
280     case 0x1c:	/* TIER */
281         return s->it_ena;
282 
283     case 0x20:	/* TWER */
284         return s->wu_ena;
285 
286     case 0x24:	/* TCLR */
287         return (s->inout << 14) |
288                 (s->capt2 << 13) |
289                 (s->pt << 12) |
290                 (s->trigger << 10) |
291                 (s->capture << 8) |
292                 (s->scpwm << 7) |
293                 (s->ce << 6) |
294                 (s->pre << 5) |
295                 (s->ptv << 2) |
296                 (s->ar << 1) |
297                 (s->st << 0);
298 
299     case 0x28:	/* TCRR */
300         return omap_gp_timer_read(s);
301 
302     case 0x2c:	/* TLDR */
303         return s->load_val;
304 
305     case 0x30:	/* TTGR */
306         return 0xffffffff;
307 
308     case 0x34:	/* TWPS */
309         return 0x00000000;	/* No posted writes pending.  */
310 
311     case 0x38:	/* TMAR */
312         return s->match_val;
313 
314     case 0x3c:	/* TCAR1 */
315         return s->capture_val[0];
316 
317     case 0x40:	/* TSICR */
318         return s->posted << 2;
319 
320     case 0x44:	/* TCAR2 */
321         return s->capture_val[1];
322     }
323 
324     OMAP_BAD_REG(addr);
325     return 0;
326 }
327 
328 static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr)
329 {
330     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
331     uint32_t ret;
332 
333     if (addr & 2)
334         return s->readh;
335     else {
336         ret = omap_gp_timer_readw(opaque, addr);
337         s->readh = ret >> 16;
338         return ret & 0xffff;
339     }
340 }
341 
342 static void omap_gp_timer_write(void *opaque, hwaddr addr,
343                 uint32_t value)
344 {
345     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
346 
347     switch (addr) {
348     case 0x00:	/* TIDR */
349     case 0x14:	/* TISTAT */
350     case 0x34:	/* TWPS */
351     case 0x3c:	/* TCAR1 */
352     case 0x44:	/* TCAR2 */
353         OMAP_RO_REG(addr);
354         break;
355 
356     case 0x10:	/* TIOCP_CFG */
357         s->config = value & 0x33d;
358         if (((value >> 3) & 3) == 3)				/* IDLEMODE */
359             fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
360                             __FUNCTION__);
361         if (value & 2)						/* SOFTRESET */
362             omap_gp_timer_reset(s);
363         break;
364 
365     case 0x18:	/* TISR */
366         if (value & GPT_TCAR_IT)
367             s->capt_num = 0;
368         if (s->status && !(s->status &= ~value))
369             qemu_irq_lower(s->irq);
370         break;
371 
372     case 0x1c:	/* TIER */
373         s->it_ena = value & 7;
374         break;
375 
376     case 0x20:	/* TWER */
377         s->wu_ena = value & 7;
378         break;
379 
380     case 0x24:	/* TCLR */
381         omap_gp_timer_sync(s);
382         s->inout = (value >> 14) & 1;
383         s->capt2 = (value >> 13) & 1;
384         s->pt = (value >> 12) & 1;
385         s->trigger = (value >> 10) & 3;
386         if (s->capture == gpt_capture_none &&
387                         ((value >> 8) & 3) != gpt_capture_none)
388             s->capt_num = 0;
389         s->capture = (value >> 8) & 3;
390         s->scpwm = (value >> 7) & 1;
391         s->ce = (value >> 6) & 1;
392         s->pre = (value >> 5) & 1;
393         s->ptv = (value >> 2) & 7;
394         s->ar = (value >> 1) & 1;
395         s->st = (value >> 0) & 1;
396         if (s->inout && s->trigger != gpt_trigger_none)
397             fprintf(stderr, "%s: GP timer pin must be an output "
398                             "for this trigger mode\n", __FUNCTION__);
399         if (!s->inout && s->capture != gpt_capture_none)
400             fprintf(stderr, "%s: GP timer pin must be an input "
401                             "for this capture mode\n", __FUNCTION__);
402         if (s->trigger == gpt_trigger_none)
403             omap_gp_timer_out(s, s->scpwm);
404         /* TODO: make sure this doesn't overflow 32-bits */
405         s->ticks_per_sec = NANOSECONDS_PER_SECOND << (s->pre ? s->ptv + 1 : 0);
406         omap_gp_timer_update(s);
407         break;
408 
409     case 0x28:	/* TCRR */
410         s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
411         s->val = value;
412         omap_gp_timer_update(s);
413         break;
414 
415     case 0x2c:	/* TLDR */
416         s->load_val = value;
417         break;
418 
419     case 0x30:	/* TTGR */
420         s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
421         s->val = s->load_val;
422         omap_gp_timer_update(s);
423         break;
424 
425     case 0x38:	/* TMAR */
426         omap_gp_timer_sync(s);
427         s->match_val = value;
428         omap_gp_timer_update(s);
429         break;
430 
431     case 0x40:	/* TSICR */
432         s->posted = (value >> 2) & 1;
433         if (value & 2)	/* How much exactly are we supposed to reset? */
434             omap_gp_timer_reset(s);
435         break;
436 
437     default:
438         OMAP_BAD_REG(addr);
439     }
440 }
441 
442 static void omap_gp_timer_writeh(void *opaque, hwaddr addr,
443                 uint32_t value)
444 {
445     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
446 
447     if (addr & 2)
448         omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
449     else
450         s->writeh = (uint16_t) value;
451 }
452 
453 static const MemoryRegionOps omap_gp_timer_ops = {
454     .old_mmio = {
455         .read = {
456             omap_badwidth_read32,
457             omap_gp_timer_readh,
458             omap_gp_timer_readw,
459         },
460         .write = {
461             omap_badwidth_write32,
462             omap_gp_timer_writeh,
463             omap_gp_timer_write,
464         },
465     },
466     .endianness = DEVICE_NATIVE_ENDIAN,
467 };
468 
469 struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
470                 qemu_irq irq, omap_clk fclk, omap_clk iclk)
471 {
472     struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1);
473 
474     s->ta = ta;
475     s->irq = irq;
476     s->clk = fclk;
477     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_tick, s);
478     s->match = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_match, s);
479     s->in = qemu_allocate_irq(omap_gp_timer_input, s, 0);
480     omap_gp_timer_reset(s);
481     omap_gp_timer_clk_setup(s);
482 
483     memory_region_init_io(&s->iomem, NULL, &omap_gp_timer_ops, s, "omap.gptimer",
484                           omap_l4_region_size(ta, 0));
485     omap_l4_attach(ta, 0, &s->iomem);
486 
487     return s;
488 }
489