xref: /qemu/hw/timer/imx_gpt.c (revision 5ec694b52a3a486fbc5a6eb6d6da4692c27d6575)
1a50c0d6fSJean-Christophe DUBOIS /*
2a50c0d6fSJean-Christophe DUBOIS  * IMX GPT Timer
3a50c0d6fSJean-Christophe DUBOIS  *
4a50c0d6fSJean-Christophe DUBOIS  * Copyright (c) 2008 OK Labs
5a50c0d6fSJean-Christophe DUBOIS  * Copyright (c) 2011 NICTA Pty Ltd
6a50c0d6fSJean-Christophe DUBOIS  * Originally written by Hans Jiang
7a50c0d6fSJean-Christophe DUBOIS  * Updated by Peter Chubb
8*5ec694b5SJean-Christophe DUBOIS  * Updated by Jean-Christophe Dubois
9a50c0d6fSJean-Christophe DUBOIS  *
10a50c0d6fSJean-Christophe DUBOIS  * This code is licensed under GPL version 2 or later.  See
11a50c0d6fSJean-Christophe DUBOIS  * the COPYING file in the top-level directory.
12a50c0d6fSJean-Christophe DUBOIS  *
13a50c0d6fSJean-Christophe DUBOIS  */
14a50c0d6fSJean-Christophe DUBOIS 
15a50c0d6fSJean-Christophe DUBOIS #include "hw/hw.h"
16a50c0d6fSJean-Christophe DUBOIS #include "qemu/bitops.h"
17a50c0d6fSJean-Christophe DUBOIS #include "qemu/timer.h"
18a50c0d6fSJean-Christophe DUBOIS #include "hw/ptimer.h"
19a50c0d6fSJean-Christophe DUBOIS #include "hw/sysbus.h"
20a50c0d6fSJean-Christophe DUBOIS #include "hw/arm/imx.h"
21a50c0d6fSJean-Christophe DUBOIS 
22*5ec694b5SJean-Christophe DUBOIS #define TYPE_IMX_GPT "imx.gpt"
23*5ec694b5SJean-Christophe DUBOIS 
24*5ec694b5SJean-Christophe DUBOIS /*
25*5ec694b5SJean-Christophe DUBOIS  * Define to 1 for debug messages
26*5ec694b5SJean-Christophe DUBOIS  */
27*5ec694b5SJean-Christophe DUBOIS #define DEBUG_TIMER 0
28*5ec694b5SJean-Christophe DUBOIS #if DEBUG_TIMER
29*5ec694b5SJean-Christophe DUBOIS 
30*5ec694b5SJean-Christophe DUBOIS static char const *imx_timerg_reg_name(uint32_t reg)
31*5ec694b5SJean-Christophe DUBOIS {
32*5ec694b5SJean-Christophe DUBOIS     switch (reg) {
33*5ec694b5SJean-Christophe DUBOIS     case 0:
34*5ec694b5SJean-Christophe DUBOIS         return "CR";
35*5ec694b5SJean-Christophe DUBOIS     case 1:
36*5ec694b5SJean-Christophe DUBOIS         return "PR";
37*5ec694b5SJean-Christophe DUBOIS     case 2:
38*5ec694b5SJean-Christophe DUBOIS         return "SR";
39*5ec694b5SJean-Christophe DUBOIS     case 3:
40*5ec694b5SJean-Christophe DUBOIS         return "IR";
41*5ec694b5SJean-Christophe DUBOIS     case 4:
42*5ec694b5SJean-Christophe DUBOIS         return "OCR1";
43*5ec694b5SJean-Christophe DUBOIS     case 5:
44*5ec694b5SJean-Christophe DUBOIS         return "OCR2";
45*5ec694b5SJean-Christophe DUBOIS     case 6:
46*5ec694b5SJean-Christophe DUBOIS         return "OCR3";
47*5ec694b5SJean-Christophe DUBOIS     case 7:
48*5ec694b5SJean-Christophe DUBOIS         return "ICR1";
49*5ec694b5SJean-Christophe DUBOIS     case 8:
50*5ec694b5SJean-Christophe DUBOIS         return "ICR2";
51*5ec694b5SJean-Christophe DUBOIS     case 9:
52*5ec694b5SJean-Christophe DUBOIS         return "CNT";
53*5ec694b5SJean-Christophe DUBOIS     default:
54*5ec694b5SJean-Christophe DUBOIS         return "[?]";
55*5ec694b5SJean-Christophe DUBOIS     }
56*5ec694b5SJean-Christophe DUBOIS }
57*5ec694b5SJean-Christophe DUBOIS 
58a50c0d6fSJean-Christophe DUBOIS #  define DPRINTF(fmt, args...) \
59*5ec694b5SJean-Christophe DUBOIS           do { printf("%s: " fmt , __func__, ##args); } while (0)
60a50c0d6fSJean-Christophe DUBOIS #else
61a50c0d6fSJean-Christophe DUBOIS #  define DPRINTF(fmt, args...) do {} while (0)
62a50c0d6fSJean-Christophe DUBOIS #endif
63a50c0d6fSJean-Christophe DUBOIS 
64a50c0d6fSJean-Christophe DUBOIS /*
65a50c0d6fSJean-Christophe DUBOIS  * Define to 1 for messages about attempts to
66a50c0d6fSJean-Christophe DUBOIS  * access unimplemented registers or similar.
67a50c0d6fSJean-Christophe DUBOIS  */
68a50c0d6fSJean-Christophe DUBOIS #define DEBUG_IMPLEMENTATION 1
69a50c0d6fSJean-Christophe DUBOIS #if DEBUG_IMPLEMENTATION
70a50c0d6fSJean-Christophe DUBOIS #  define IPRINTF(fmt, args...)                                         \
71*5ec694b5SJean-Christophe DUBOIS           do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0)
72a50c0d6fSJean-Christophe DUBOIS #else
73a50c0d6fSJean-Christophe DUBOIS #  define IPRINTF(fmt, args...) do {} while (0)
74a50c0d6fSJean-Christophe DUBOIS #endif
75a50c0d6fSJean-Christophe DUBOIS 
76a50c0d6fSJean-Christophe DUBOIS /*
77a50c0d6fSJean-Christophe DUBOIS  * GPT : General purpose timer
78a50c0d6fSJean-Christophe DUBOIS  *
79a50c0d6fSJean-Christophe DUBOIS  * This timer counts up continuously while it is enabled, resetting itself
80a50c0d6fSJean-Christophe DUBOIS  * to 0 when it reaches TIMER_MAX (in freerun mode) or when it
81*5ec694b5SJean-Christophe DUBOIS  * reaches the value of one of the ocrX (in periodic mode).
82a50c0d6fSJean-Christophe DUBOIS  */
83a50c0d6fSJean-Christophe DUBOIS 
84a50c0d6fSJean-Christophe DUBOIS #define TIMER_MAX  0XFFFFFFFFUL
85a50c0d6fSJean-Christophe DUBOIS 
86a50c0d6fSJean-Christophe DUBOIS /* Control register.  Not all of these bits have any effect (yet) */
87a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_EN     (1 << 0)  /* GPT Enable */
88a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_ENMOD  (1 << 1)  /* GPT Enable Mode */
89a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_DBGEN  (1 << 2)  /* GPT Debug mode enable */
90a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_WAITEN (1 << 3)  /* GPT Wait Mode Enable  */
91a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_DOZEN  (1 << 4)  /* GPT Doze mode enable */
92a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_STOPEN (1 << 5)  /* GPT Stop Mode Enable */
93a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_CLKSRC_SHIFT (6)
94a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_CLKSRC_MASK  (0x7)
95a50c0d6fSJean-Christophe DUBOIS 
96a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FRR    (1 << 9)  /* Freerun or Restart */
97a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_SWR    (1 << 15) /* Software Reset */
98a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_IM1    (3 << 16) /* Input capture channel 1 mode (2 bits) */
99a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_IM2    (3 << 18) /* Input capture channel 2 mode (2 bits) */
100a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_OM1    (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
101a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_OM2    (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
102a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_OM3    (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
103a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FO1    (1 << 29) /* Force Output Compare Channel 1 */
104a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FO2    (1 << 30) /* Force Output Compare Channel 2 */
105a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FO3    (1 << 31) /* Force Output Compare Channel 3 */
106a50c0d6fSJean-Christophe DUBOIS 
107a50c0d6fSJean-Christophe DUBOIS #define GPT_SR_OF1  (1 << 0)
108*5ec694b5SJean-Christophe DUBOIS #define GPT_SR_OF2  (1 << 1)
109*5ec694b5SJean-Christophe DUBOIS #define GPT_SR_OF3  (1 << 2)
110a50c0d6fSJean-Christophe DUBOIS #define GPT_SR_ROV  (1 << 5)
111a50c0d6fSJean-Christophe DUBOIS 
112a50c0d6fSJean-Christophe DUBOIS #define GPT_IR_OF1IE  (1 << 0)
113*5ec694b5SJean-Christophe DUBOIS #define GPT_IR_OF2IE  (1 << 1)
114*5ec694b5SJean-Christophe DUBOIS #define GPT_IR_OF3IE  (1 << 2)
115a50c0d6fSJean-Christophe DUBOIS #define GPT_IR_ROVIE  (1 << 5)
116a50c0d6fSJean-Christophe DUBOIS 
117a50c0d6fSJean-Christophe DUBOIS typedef struct {
118a50c0d6fSJean-Christophe DUBOIS     SysBusDevice busdev;
119a50c0d6fSJean-Christophe DUBOIS     ptimer_state *timer;
120a50c0d6fSJean-Christophe DUBOIS     MemoryRegion iomem;
121a50c0d6fSJean-Christophe DUBOIS     DeviceState *ccm;
122a50c0d6fSJean-Christophe DUBOIS 
123a50c0d6fSJean-Christophe DUBOIS     uint32_t cr;
124a50c0d6fSJean-Christophe DUBOIS     uint32_t pr;
125a50c0d6fSJean-Christophe DUBOIS     uint32_t sr;
126a50c0d6fSJean-Christophe DUBOIS     uint32_t ir;
127a50c0d6fSJean-Christophe DUBOIS     uint32_t ocr1;
128a50c0d6fSJean-Christophe DUBOIS     uint32_t ocr2;
129a50c0d6fSJean-Christophe DUBOIS     uint32_t ocr3;
130a50c0d6fSJean-Christophe DUBOIS     uint32_t icr1;
131a50c0d6fSJean-Christophe DUBOIS     uint32_t icr2;
132a50c0d6fSJean-Christophe DUBOIS     uint32_t cnt;
133a50c0d6fSJean-Christophe DUBOIS 
134*5ec694b5SJean-Christophe DUBOIS     uint32_t next_timeout;
135*5ec694b5SJean-Christophe DUBOIS     uint32_t next_int;
136*5ec694b5SJean-Christophe DUBOIS 
137*5ec694b5SJean-Christophe DUBOIS     uint32_t freq;
138*5ec694b5SJean-Christophe DUBOIS 
139a50c0d6fSJean-Christophe DUBOIS     qemu_irq irq;
140a50c0d6fSJean-Christophe DUBOIS } IMXTimerGState;
141a50c0d6fSJean-Christophe DUBOIS 
142a50c0d6fSJean-Christophe DUBOIS static const VMStateDescription vmstate_imx_timerg = {
143*5ec694b5SJean-Christophe DUBOIS     .name = TYPE_IMX_GPT,
144*5ec694b5SJean-Christophe DUBOIS     .version_id = 3,
145*5ec694b5SJean-Christophe DUBOIS     .minimum_version_id = 3,
146*5ec694b5SJean-Christophe DUBOIS     .minimum_version_id_old = 3,
147a50c0d6fSJean-Christophe DUBOIS     .fields      = (VMStateField[]) {
148a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(cr, IMXTimerGState),
149a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(pr, IMXTimerGState),
150a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(sr, IMXTimerGState),
151a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ir, IMXTimerGState),
152a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ocr1, IMXTimerGState),
153a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ocr2, IMXTimerGState),
154a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ocr3, IMXTimerGState),
155a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(icr1, IMXTimerGState),
156a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(icr2, IMXTimerGState),
157a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(cnt, IMXTimerGState),
158*5ec694b5SJean-Christophe DUBOIS         VMSTATE_UINT32(next_timeout, IMXTimerGState),
159*5ec694b5SJean-Christophe DUBOIS         VMSTATE_UINT32(next_int, IMXTimerGState),
160*5ec694b5SJean-Christophe DUBOIS         VMSTATE_UINT32(freq, IMXTimerGState),
161a50c0d6fSJean-Christophe DUBOIS         VMSTATE_PTIMER(timer, IMXTimerGState),
162a50c0d6fSJean-Christophe DUBOIS         VMSTATE_END_OF_LIST()
163a50c0d6fSJean-Christophe DUBOIS     }
164a50c0d6fSJean-Christophe DUBOIS };
165a50c0d6fSJean-Christophe DUBOIS 
166a50c0d6fSJean-Christophe DUBOIS static const IMXClk imx_timerg_clocks[] = {
167a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 000 No clock source */
168a50c0d6fSJean-Christophe DUBOIS     IPG,      /* 001 ipg_clk, 532MHz*/
169a50c0d6fSJean-Christophe DUBOIS     IPG,      /* 010 ipg_clk_highfreq */
170a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 011 not defined */
171a50c0d6fSJean-Christophe DUBOIS     CLK_32k,  /* 100 ipg_clk_32k */
172a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 101 not defined */
173a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 110 not defined */
174a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 111 not defined */
175a50c0d6fSJean-Christophe DUBOIS };
176a50c0d6fSJean-Christophe DUBOIS 
177a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_set_freq(IMXTimerGState *s)
178a50c0d6fSJean-Christophe DUBOIS {
179*5ec694b5SJean-Christophe DUBOIS     uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
180*5ec694b5SJean-Christophe DUBOIS     uint32_t freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc])
181*5ec694b5SJean-Christophe DUBOIS                                                 / (1 + s->pr);
182*5ec694b5SJean-Christophe DUBOIS     s->freq = freq;
183a50c0d6fSJean-Christophe DUBOIS 
184*5ec694b5SJean-Christophe DUBOIS     DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
185a50c0d6fSJean-Christophe DUBOIS 
186a50c0d6fSJean-Christophe DUBOIS     if (freq) {
187a50c0d6fSJean-Christophe DUBOIS         ptimer_set_freq(s->timer, freq);
188a50c0d6fSJean-Christophe DUBOIS     }
189a50c0d6fSJean-Christophe DUBOIS }
190a50c0d6fSJean-Christophe DUBOIS 
191a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_update(IMXTimerGState *s)
192a50c0d6fSJean-Christophe DUBOIS {
193*5ec694b5SJean-Christophe DUBOIS     if ((s->sr & s->ir) && (s->cr & GPT_CR_EN)) {
194*5ec694b5SJean-Christophe DUBOIS         qemu_irq_raise(s->irq);
195*5ec694b5SJean-Christophe DUBOIS     } else {
196*5ec694b5SJean-Christophe DUBOIS         qemu_irq_lower(s->irq);
197*5ec694b5SJean-Christophe DUBOIS     }
198a50c0d6fSJean-Christophe DUBOIS }
199a50c0d6fSJean-Christophe DUBOIS 
200a50c0d6fSJean-Christophe DUBOIS static uint32_t imx_timerg_update_counts(IMXTimerGState *s)
201a50c0d6fSJean-Christophe DUBOIS {
202*5ec694b5SJean-Christophe DUBOIS     s->cnt = s->next_timeout - (uint32_t)ptimer_get_count(s->timer);
203*5ec694b5SJean-Christophe DUBOIS 
204a50c0d6fSJean-Christophe DUBOIS     return s->cnt;
205a50c0d6fSJean-Christophe DUBOIS }
206a50c0d6fSJean-Christophe DUBOIS 
207*5ec694b5SJean-Christophe DUBOIS static inline uint32_t imx_timerg_find_limit(uint32_t count, uint32_t reg,
208*5ec694b5SJean-Christophe DUBOIS                                              uint32_t timeout)
209a50c0d6fSJean-Christophe DUBOIS {
210*5ec694b5SJean-Christophe DUBOIS     if ((count < reg) && (timeout > reg)) {
211*5ec694b5SJean-Christophe DUBOIS         timeout = reg;
212*5ec694b5SJean-Christophe DUBOIS     }
213a50c0d6fSJean-Christophe DUBOIS 
214*5ec694b5SJean-Christophe DUBOIS     return timeout;
215*5ec694b5SJean-Christophe DUBOIS }
216*5ec694b5SJean-Christophe DUBOIS 
217*5ec694b5SJean-Christophe DUBOIS static void imx_timerg_compute_next_timeout(IMXTimerGState *s, bool event)
218*5ec694b5SJean-Christophe DUBOIS {
219*5ec694b5SJean-Christophe DUBOIS     uint32_t timeout = TIMER_MAX;
220*5ec694b5SJean-Christophe DUBOIS     uint32_t count = 0;
221*5ec694b5SJean-Christophe DUBOIS     long long limit;
222*5ec694b5SJean-Christophe DUBOIS 
223*5ec694b5SJean-Christophe DUBOIS     if (!(s->cr & GPT_CR_EN)) {
224*5ec694b5SJean-Christophe DUBOIS         /* if not enabled just return */
225a50c0d6fSJean-Christophe DUBOIS         return;
226a50c0d6fSJean-Christophe DUBOIS     }
227a50c0d6fSJean-Christophe DUBOIS 
228*5ec694b5SJean-Christophe DUBOIS     if (event) {
229*5ec694b5SJean-Christophe DUBOIS         /* This is a timer event  */
230*5ec694b5SJean-Christophe DUBOIS 
231*5ec694b5SJean-Christophe DUBOIS         if ((s->cr & GPT_CR_FRR)  && (s->next_timeout != TIMER_MAX)) {
232a50c0d6fSJean-Christophe DUBOIS             /*
233*5ec694b5SJean-Christophe DUBOIS              * if we are in free running mode and we have not reached
234*5ec694b5SJean-Christophe DUBOIS              * the TIMER_MAX limit, then update the count
235a50c0d6fSJean-Christophe DUBOIS              */
236*5ec694b5SJean-Christophe DUBOIS             count = imx_timerg_update_counts(s);
237a50c0d6fSJean-Christophe DUBOIS         }
238*5ec694b5SJean-Christophe DUBOIS     } else {
239*5ec694b5SJean-Christophe DUBOIS         /* not a timer event, then just update the count */
240*5ec694b5SJean-Christophe DUBOIS 
241*5ec694b5SJean-Christophe DUBOIS         count = imx_timerg_update_counts(s);
242*5ec694b5SJean-Christophe DUBOIS     }
243*5ec694b5SJean-Christophe DUBOIS 
244*5ec694b5SJean-Christophe DUBOIS     /* now, find the next timeout related to count */
245*5ec694b5SJean-Christophe DUBOIS 
246*5ec694b5SJean-Christophe DUBOIS     if (s->ir & GPT_IR_OF1IE) {
247*5ec694b5SJean-Christophe DUBOIS         timeout = imx_timerg_find_limit(count, s->ocr1, timeout);
248*5ec694b5SJean-Christophe DUBOIS     }
249*5ec694b5SJean-Christophe DUBOIS     if (s->ir & GPT_IR_OF2IE) {
250*5ec694b5SJean-Christophe DUBOIS         timeout = imx_timerg_find_limit(count, s->ocr2, timeout);
251*5ec694b5SJean-Christophe DUBOIS     }
252*5ec694b5SJean-Christophe DUBOIS     if (s->ir & GPT_IR_OF3IE) {
253*5ec694b5SJean-Christophe DUBOIS         timeout = imx_timerg_find_limit(count, s->ocr3, timeout);
254*5ec694b5SJean-Christophe DUBOIS     }
255*5ec694b5SJean-Christophe DUBOIS 
256*5ec694b5SJean-Christophe DUBOIS     /* find the next set of interrupts to raise for next timer event */
257*5ec694b5SJean-Christophe DUBOIS 
258*5ec694b5SJean-Christophe DUBOIS     s->next_int = 0;
259*5ec694b5SJean-Christophe DUBOIS     if ((s->ir & GPT_IR_OF1IE) && (timeout == s->ocr1)) {
260*5ec694b5SJean-Christophe DUBOIS         s->next_int |= GPT_SR_OF1;
261*5ec694b5SJean-Christophe DUBOIS     }
262*5ec694b5SJean-Christophe DUBOIS     if ((s->ir & GPT_IR_OF2IE) && (timeout == s->ocr2)) {
263*5ec694b5SJean-Christophe DUBOIS         s->next_int |= GPT_SR_OF2;
264*5ec694b5SJean-Christophe DUBOIS     }
265*5ec694b5SJean-Christophe DUBOIS     if ((s->ir & GPT_IR_OF3IE) && (timeout == s->ocr3)) {
266*5ec694b5SJean-Christophe DUBOIS         s->next_int |= GPT_SR_OF3;
267*5ec694b5SJean-Christophe DUBOIS     }
268*5ec694b5SJean-Christophe DUBOIS     if ((s->ir & GPT_IR_ROVIE) && (timeout == TIMER_MAX)) {
269*5ec694b5SJean-Christophe DUBOIS         s->next_int |= GPT_SR_ROV;
270*5ec694b5SJean-Christophe DUBOIS     }
271*5ec694b5SJean-Christophe DUBOIS 
272*5ec694b5SJean-Christophe DUBOIS     /* the new range to count down from */
273*5ec694b5SJean-Christophe DUBOIS     limit = timeout - imx_timerg_update_counts(s);
274*5ec694b5SJean-Christophe DUBOIS 
275*5ec694b5SJean-Christophe DUBOIS     if (limit < 0) {
276*5ec694b5SJean-Christophe DUBOIS         /*
277*5ec694b5SJean-Christophe DUBOIS          * if we reach here, then QEMU is running too slow and we pass the
278*5ec694b5SJean-Christophe DUBOIS          * timeout limit while computing it. Let's deliver the interrupt
279*5ec694b5SJean-Christophe DUBOIS          * and compute a new limit.
280*5ec694b5SJean-Christophe DUBOIS          */
281*5ec694b5SJean-Christophe DUBOIS         s->sr |= s->next_int;
282*5ec694b5SJean-Christophe DUBOIS 
283*5ec694b5SJean-Christophe DUBOIS         imx_timerg_compute_next_timeout(s, event);
284*5ec694b5SJean-Christophe DUBOIS 
285*5ec694b5SJean-Christophe DUBOIS         imx_timerg_update(s);
286*5ec694b5SJean-Christophe DUBOIS     } else {
287*5ec694b5SJean-Christophe DUBOIS         /* New timeout value */
288*5ec694b5SJean-Christophe DUBOIS         s->next_timeout = timeout;
289*5ec694b5SJean-Christophe DUBOIS 
290*5ec694b5SJean-Christophe DUBOIS         /* reset the limit to the computed range */
291*5ec694b5SJean-Christophe DUBOIS         ptimer_set_limit(s->timer, limit, 1);
292*5ec694b5SJean-Christophe DUBOIS     }
293a50c0d6fSJean-Christophe DUBOIS }
294a50c0d6fSJean-Christophe DUBOIS 
295a50c0d6fSJean-Christophe DUBOIS static uint64_t imx_timerg_read(void *opaque, hwaddr offset,
296a50c0d6fSJean-Christophe DUBOIS                                 unsigned size)
297a50c0d6fSJean-Christophe DUBOIS {
298a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = (IMXTimerGState *)opaque;
299*5ec694b5SJean-Christophe DUBOIS     uint32_t reg_value = 0;
300*5ec694b5SJean-Christophe DUBOIS     uint32_t reg = offset >> 2;
301a50c0d6fSJean-Christophe DUBOIS 
302*5ec694b5SJean-Christophe DUBOIS     switch (reg) {
303a50c0d6fSJean-Christophe DUBOIS     case 0: /* Control Register */
304*5ec694b5SJean-Christophe DUBOIS         reg_value = s->cr;
305*5ec694b5SJean-Christophe DUBOIS         break;
306a50c0d6fSJean-Christophe DUBOIS 
307a50c0d6fSJean-Christophe DUBOIS     case 1: /* prescaler */
308*5ec694b5SJean-Christophe DUBOIS         reg_value = s->pr;
309*5ec694b5SJean-Christophe DUBOIS         break;
310a50c0d6fSJean-Christophe DUBOIS 
311a50c0d6fSJean-Christophe DUBOIS     case 2: /* Status Register */
312*5ec694b5SJean-Christophe DUBOIS         reg_value = s->sr;
313*5ec694b5SJean-Christophe DUBOIS         break;
314a50c0d6fSJean-Christophe DUBOIS 
315a50c0d6fSJean-Christophe DUBOIS     case 3: /* Interrupt Register */
316*5ec694b5SJean-Christophe DUBOIS         reg_value = s->ir;
317*5ec694b5SJean-Christophe DUBOIS         break;
318a50c0d6fSJean-Christophe DUBOIS 
319a50c0d6fSJean-Christophe DUBOIS     case 4: /* Output Compare Register 1 */
320*5ec694b5SJean-Christophe DUBOIS         reg_value = s->ocr1;
321*5ec694b5SJean-Christophe DUBOIS         break;
322a50c0d6fSJean-Christophe DUBOIS 
323a50c0d6fSJean-Christophe DUBOIS     case 5: /* Output Compare Register 2 */
324*5ec694b5SJean-Christophe DUBOIS         reg_value = s->ocr2;
325*5ec694b5SJean-Christophe DUBOIS         break;
326a50c0d6fSJean-Christophe DUBOIS 
327a50c0d6fSJean-Christophe DUBOIS     case 6: /* Output Compare Register 3 */
328*5ec694b5SJean-Christophe DUBOIS         reg_value = s->ocr3;
329*5ec694b5SJean-Christophe DUBOIS         break;
330a50c0d6fSJean-Christophe DUBOIS 
331a50c0d6fSJean-Christophe DUBOIS     case 7: /* input Capture Register 1 */
332*5ec694b5SJean-Christophe DUBOIS         qemu_log_mask(LOG_UNIMP, "icr1 feature is not implemented\n");
333*5ec694b5SJean-Christophe DUBOIS         reg_value = s->icr1;
334*5ec694b5SJean-Christophe DUBOIS         break;
335a50c0d6fSJean-Christophe DUBOIS 
336a50c0d6fSJean-Christophe DUBOIS     case 8: /* input Capture Register 2 */
337*5ec694b5SJean-Christophe DUBOIS         qemu_log_mask(LOG_UNIMP, "icr2 feature is not implemented\n");
338*5ec694b5SJean-Christophe DUBOIS         reg_value = s->icr2;
339*5ec694b5SJean-Christophe DUBOIS         break;
340a50c0d6fSJean-Christophe DUBOIS 
341a50c0d6fSJean-Christophe DUBOIS     case 9: /* cnt */
342a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update_counts(s);
343*5ec694b5SJean-Christophe DUBOIS         reg_value = s->cnt;
344*5ec694b5SJean-Christophe DUBOIS         break;
345*5ec694b5SJean-Christophe DUBOIS 
346*5ec694b5SJean-Christophe DUBOIS     default:
347*5ec694b5SJean-Christophe DUBOIS         IPRINTF("Bad offset %x\n", reg);
348*5ec694b5SJean-Christophe DUBOIS         break;
349a50c0d6fSJean-Christophe DUBOIS     }
350a50c0d6fSJean-Christophe DUBOIS 
351*5ec694b5SJean-Christophe DUBOIS     DPRINTF("(%s) = 0x%08x\n", imx_timerg_reg_name(reg), reg_value);
352a50c0d6fSJean-Christophe DUBOIS 
353*5ec694b5SJean-Christophe DUBOIS     return reg_value;
354a50c0d6fSJean-Christophe DUBOIS }
355a50c0d6fSJean-Christophe DUBOIS 
356a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_reset(DeviceState *dev)
357a50c0d6fSJean-Christophe DUBOIS {
358a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = container_of(dev, IMXTimerGState, busdev.qdev);
359a50c0d6fSJean-Christophe DUBOIS 
360*5ec694b5SJean-Christophe DUBOIS     /* stop timer */
361*5ec694b5SJean-Christophe DUBOIS     ptimer_stop(s->timer);
362*5ec694b5SJean-Christophe DUBOIS 
363a50c0d6fSJean-Christophe DUBOIS     /*
364a50c0d6fSJean-Christophe DUBOIS      * Soft reset doesn't touch some bits; hard reset clears them
365a50c0d6fSJean-Christophe DUBOIS      */
366a50c0d6fSJean-Christophe DUBOIS     s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN|
367a50c0d6fSJean-Christophe DUBOIS                GPT_CR_WAITEN|GPT_CR_DBGEN);
368a50c0d6fSJean-Christophe DUBOIS     s->sr = 0;
369a50c0d6fSJean-Christophe DUBOIS     s->pr = 0;
370a50c0d6fSJean-Christophe DUBOIS     s->ir = 0;
371a50c0d6fSJean-Christophe DUBOIS     s->cnt = 0;
372a50c0d6fSJean-Christophe DUBOIS     s->ocr1 = TIMER_MAX;
373a50c0d6fSJean-Christophe DUBOIS     s->ocr2 = TIMER_MAX;
374a50c0d6fSJean-Christophe DUBOIS     s->ocr3 = TIMER_MAX;
375a50c0d6fSJean-Christophe DUBOIS     s->icr1 = 0;
376a50c0d6fSJean-Christophe DUBOIS     s->icr2 = 0;
377*5ec694b5SJean-Christophe DUBOIS 
378*5ec694b5SJean-Christophe DUBOIS     s->next_timeout = TIMER_MAX;
379*5ec694b5SJean-Christophe DUBOIS     s->next_int = 0;
380*5ec694b5SJean-Christophe DUBOIS 
381*5ec694b5SJean-Christophe DUBOIS     /* compute new freq */
382a50c0d6fSJean-Christophe DUBOIS     imx_timerg_set_freq(s);
383*5ec694b5SJean-Christophe DUBOIS 
384*5ec694b5SJean-Christophe DUBOIS     /* reset the limit to TIMER_MAX */
385*5ec694b5SJean-Christophe DUBOIS     ptimer_set_limit(s->timer, TIMER_MAX, 1);
386*5ec694b5SJean-Christophe DUBOIS 
387*5ec694b5SJean-Christophe DUBOIS     /* if the timer is still enabled, restart it */
388*5ec694b5SJean-Christophe DUBOIS     if (s->freq && (s->cr & GPT_CR_EN)) {
389*5ec694b5SJean-Christophe DUBOIS         ptimer_run(s->timer, 1);
390*5ec694b5SJean-Christophe DUBOIS     }
391a50c0d6fSJean-Christophe DUBOIS }
392a50c0d6fSJean-Christophe DUBOIS 
393a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_write(void *opaque, hwaddr offset,
394a50c0d6fSJean-Christophe DUBOIS                              uint64_t value, unsigned size)
395a50c0d6fSJean-Christophe DUBOIS {
396a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = (IMXTimerGState *)opaque;
397*5ec694b5SJean-Christophe DUBOIS     uint32_t oldreg;
398*5ec694b5SJean-Christophe DUBOIS     uint32_t reg = offset >> 2;
399a50c0d6fSJean-Christophe DUBOIS 
400*5ec694b5SJean-Christophe DUBOIS     DPRINTF("(%s, value = 0x%08x)\n", imx_timerg_reg_name(reg),
401*5ec694b5SJean-Christophe DUBOIS             (uint32_t)value);
402a50c0d6fSJean-Christophe DUBOIS 
403*5ec694b5SJean-Christophe DUBOIS     switch (reg) {
404*5ec694b5SJean-Christophe DUBOIS     case 0:
405*5ec694b5SJean-Christophe DUBOIS         oldreg = s->cr;
406*5ec694b5SJean-Christophe DUBOIS         s->cr = value & ~0x7c14;
407*5ec694b5SJean-Christophe DUBOIS         if (s->cr & GPT_CR_SWR) { /* force reset */
408*5ec694b5SJean-Christophe DUBOIS             /* handle the reset */
409*5ec694b5SJean-Christophe DUBOIS             imx_timerg_reset(DEVICE(s));
410a50c0d6fSJean-Christophe DUBOIS         } else {
411*5ec694b5SJean-Christophe DUBOIS             /* set our freq, as the source might have changed */
412*5ec694b5SJean-Christophe DUBOIS             imx_timerg_set_freq(s);
413*5ec694b5SJean-Christophe DUBOIS 
414*5ec694b5SJean-Christophe DUBOIS             if ((oldreg ^ s->cr) & GPT_CR_EN) {
415*5ec694b5SJean-Christophe DUBOIS                 if (s->cr & GPT_CR_EN) {
416*5ec694b5SJean-Christophe DUBOIS                     if (s->cr & GPT_CR_ENMOD) {
417*5ec694b5SJean-Christophe DUBOIS                         s->next_timeout = TIMER_MAX;
418*5ec694b5SJean-Christophe DUBOIS                         ptimer_set_count(s->timer, TIMER_MAX);
419*5ec694b5SJean-Christophe DUBOIS                         imx_timerg_compute_next_timeout(s, false);
420*5ec694b5SJean-Christophe DUBOIS                     }
421*5ec694b5SJean-Christophe DUBOIS                     ptimer_run(s->timer, 1);
422*5ec694b5SJean-Christophe DUBOIS                 } else {
423*5ec694b5SJean-Christophe DUBOIS                     /* stop timer */
424a50c0d6fSJean-Christophe DUBOIS                     ptimer_stop(s->timer);
425a50c0d6fSJean-Christophe DUBOIS                 }
426a50c0d6fSJean-Christophe DUBOIS             }
427*5ec694b5SJean-Christophe DUBOIS         }
428*5ec694b5SJean-Christophe DUBOIS         break;
429a50c0d6fSJean-Christophe DUBOIS 
430a50c0d6fSJean-Christophe DUBOIS     case 1: /* Prescaler */
431a50c0d6fSJean-Christophe DUBOIS         s->pr = value & 0xfff;
432a50c0d6fSJean-Christophe DUBOIS         imx_timerg_set_freq(s);
433*5ec694b5SJean-Christophe DUBOIS         break;
434a50c0d6fSJean-Christophe DUBOIS 
435a50c0d6fSJean-Christophe DUBOIS     case 2: /* SR */
436*5ec694b5SJean-Christophe DUBOIS         s->sr &= ~(value & 0x3f);
437a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update(s);
438*5ec694b5SJean-Christophe DUBOIS         break;
439a50c0d6fSJean-Christophe DUBOIS 
440a50c0d6fSJean-Christophe DUBOIS     case 3: /* IR -- interrupt register */
441a50c0d6fSJean-Christophe DUBOIS         s->ir = value & 0x3f;
442a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update(s);
443*5ec694b5SJean-Christophe DUBOIS 
444*5ec694b5SJean-Christophe DUBOIS         imx_timerg_compute_next_timeout(s, false);
445*5ec694b5SJean-Christophe DUBOIS 
446*5ec694b5SJean-Christophe DUBOIS         break;
447a50c0d6fSJean-Christophe DUBOIS 
448a50c0d6fSJean-Christophe DUBOIS     case 4: /* OCR1 -- output compare register */
449*5ec694b5SJean-Christophe DUBOIS         s->ocr1 = value;
450*5ec694b5SJean-Christophe DUBOIS 
451a50c0d6fSJean-Christophe DUBOIS         /* In non-freerun mode, reset count when this register is written */
452a50c0d6fSJean-Christophe DUBOIS         if (!(s->cr & GPT_CR_FRR)) {
453*5ec694b5SJean-Christophe DUBOIS             s->next_timeout = TIMER_MAX;
454*5ec694b5SJean-Christophe DUBOIS             ptimer_set_limit(s->timer, TIMER_MAX, 1);
455a50c0d6fSJean-Christophe DUBOIS         }
456*5ec694b5SJean-Christophe DUBOIS 
457*5ec694b5SJean-Christophe DUBOIS         /* compute the new timeout */
458*5ec694b5SJean-Christophe DUBOIS         imx_timerg_compute_next_timeout(s, false);
459*5ec694b5SJean-Christophe DUBOIS 
460*5ec694b5SJean-Christophe DUBOIS         break;
461a50c0d6fSJean-Christophe DUBOIS 
462a50c0d6fSJean-Christophe DUBOIS     case 5: /* OCR2 -- output compare register */
463*5ec694b5SJean-Christophe DUBOIS         s->ocr2 = value;
464*5ec694b5SJean-Christophe DUBOIS 
465*5ec694b5SJean-Christophe DUBOIS         /* compute the new timeout */
466*5ec694b5SJean-Christophe DUBOIS         imx_timerg_compute_next_timeout(s, false);
467*5ec694b5SJean-Christophe DUBOIS 
468*5ec694b5SJean-Christophe DUBOIS         break;
469*5ec694b5SJean-Christophe DUBOIS 
470a50c0d6fSJean-Christophe DUBOIS     case 6: /* OCR3 -- output compare register */
471*5ec694b5SJean-Christophe DUBOIS         s->ocr3 = value;
472*5ec694b5SJean-Christophe DUBOIS 
473*5ec694b5SJean-Christophe DUBOIS         /* compute the new timeout */
474*5ec694b5SJean-Christophe DUBOIS         imx_timerg_compute_next_timeout(s, false);
475*5ec694b5SJean-Christophe DUBOIS 
476*5ec694b5SJean-Christophe DUBOIS         break;
477*5ec694b5SJean-Christophe DUBOIS 
478a50c0d6fSJean-Christophe DUBOIS     default:
479*5ec694b5SJean-Christophe DUBOIS         IPRINTF("Bad offset %x\n", reg);
480*5ec694b5SJean-Christophe DUBOIS         break;
481a50c0d6fSJean-Christophe DUBOIS     }
482a50c0d6fSJean-Christophe DUBOIS }
483a50c0d6fSJean-Christophe DUBOIS 
484a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_timeout(void *opaque)
485a50c0d6fSJean-Christophe DUBOIS {
486a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = (IMXTimerGState *)opaque;
487a50c0d6fSJean-Christophe DUBOIS 
488*5ec694b5SJean-Christophe DUBOIS     DPRINTF("\n");
489a50c0d6fSJean-Christophe DUBOIS 
490*5ec694b5SJean-Christophe DUBOIS     s->sr |= s->next_int;
491*5ec694b5SJean-Christophe DUBOIS     s->next_int = 0;
492a50c0d6fSJean-Christophe DUBOIS 
493*5ec694b5SJean-Christophe DUBOIS     imx_timerg_compute_next_timeout(s, true);
494*5ec694b5SJean-Christophe DUBOIS 
495a50c0d6fSJean-Christophe DUBOIS     imx_timerg_update(s);
496*5ec694b5SJean-Christophe DUBOIS 
497*5ec694b5SJean-Christophe DUBOIS     if (s->freq && (s->cr & GPT_CR_EN)) {
498*5ec694b5SJean-Christophe DUBOIS         ptimer_run(s->timer, 1);
499*5ec694b5SJean-Christophe DUBOIS     }
500a50c0d6fSJean-Christophe DUBOIS }
501a50c0d6fSJean-Christophe DUBOIS 
502a50c0d6fSJean-Christophe DUBOIS static const MemoryRegionOps imx_timerg_ops = {
503a50c0d6fSJean-Christophe DUBOIS     .read = imx_timerg_read,
504a50c0d6fSJean-Christophe DUBOIS     .write = imx_timerg_write,
505a50c0d6fSJean-Christophe DUBOIS     .endianness = DEVICE_NATIVE_ENDIAN,
506a50c0d6fSJean-Christophe DUBOIS };
507a50c0d6fSJean-Christophe DUBOIS 
508a50c0d6fSJean-Christophe DUBOIS 
509a50c0d6fSJean-Christophe DUBOIS static int imx_timerg_init(SysBusDevice *dev)
510a50c0d6fSJean-Christophe DUBOIS {
511a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev);
512a50c0d6fSJean-Christophe DUBOIS     QEMUBH *bh;
513a50c0d6fSJean-Christophe DUBOIS 
514a50c0d6fSJean-Christophe DUBOIS     sysbus_init_irq(dev, &s->irq);
515a50c0d6fSJean-Christophe DUBOIS     memory_region_init_io(&s->iomem, &imx_timerg_ops,
516*5ec694b5SJean-Christophe DUBOIS                           s, TYPE_IMX_GPT,
517a50c0d6fSJean-Christophe DUBOIS                           0x00001000);
518a50c0d6fSJean-Christophe DUBOIS     sysbus_init_mmio(dev, &s->iomem);
519a50c0d6fSJean-Christophe DUBOIS 
520a50c0d6fSJean-Christophe DUBOIS     bh = qemu_bh_new(imx_timerg_timeout, s);
521a50c0d6fSJean-Christophe DUBOIS     s->timer = ptimer_init(bh);
522a50c0d6fSJean-Christophe DUBOIS 
523a50c0d6fSJean-Christophe DUBOIS     /* Hard reset resets extra bits in CR */
524a50c0d6fSJean-Christophe DUBOIS     s->cr = 0;
525a50c0d6fSJean-Christophe DUBOIS     return 0;
526a50c0d6fSJean-Christophe DUBOIS }
527a50c0d6fSJean-Christophe DUBOIS 
528*5ec694b5SJean-Christophe DUBOIS void imx_timerg_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm)
529a50c0d6fSJean-Christophe DUBOIS {
530a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *pp;
531a50c0d6fSJean-Christophe DUBOIS     DeviceState *dev;
532a50c0d6fSJean-Christophe DUBOIS 
533*5ec694b5SJean-Christophe DUBOIS     dev = sysbus_create_simple(TYPE_IMX_GPT, addr, irq);
534a50c0d6fSJean-Christophe DUBOIS     pp = container_of(dev, IMXTimerGState, busdev.qdev);
535a50c0d6fSJean-Christophe DUBOIS     pp->ccm = ccm;
536a50c0d6fSJean-Christophe DUBOIS }
537a50c0d6fSJean-Christophe DUBOIS 
538a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_class_init(ObjectClass *klass, void *data)
539a50c0d6fSJean-Christophe DUBOIS {
540a50c0d6fSJean-Christophe DUBOIS     DeviceClass *dc  = DEVICE_CLASS(klass);
541a50c0d6fSJean-Christophe DUBOIS     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
542a50c0d6fSJean-Christophe DUBOIS     k->init = imx_timerg_init;
543a50c0d6fSJean-Christophe DUBOIS     dc->vmsd = &vmstate_imx_timerg;
544a50c0d6fSJean-Christophe DUBOIS     dc->reset = imx_timerg_reset;
545a50c0d6fSJean-Christophe DUBOIS     dc->desc = "i.MX general timer";
546a50c0d6fSJean-Christophe DUBOIS }
547a50c0d6fSJean-Christophe DUBOIS 
548a50c0d6fSJean-Christophe DUBOIS static const TypeInfo imx_timerg_info = {
549*5ec694b5SJean-Christophe DUBOIS     .name = TYPE_IMX_GPT,
550a50c0d6fSJean-Christophe DUBOIS     .parent = TYPE_SYS_BUS_DEVICE,
551a50c0d6fSJean-Christophe DUBOIS     .instance_size = sizeof(IMXTimerGState),
552a50c0d6fSJean-Christophe DUBOIS     .class_init = imx_timerg_class_init,
553a50c0d6fSJean-Christophe DUBOIS };
554a50c0d6fSJean-Christophe DUBOIS 
555a50c0d6fSJean-Christophe DUBOIS static void imx_timer_register_types(void)
556a50c0d6fSJean-Christophe DUBOIS {
557a50c0d6fSJean-Christophe DUBOIS     type_register_static(&imx_timerg_info);
558a50c0d6fSJean-Christophe DUBOIS }
559a50c0d6fSJean-Christophe DUBOIS 
560a50c0d6fSJean-Christophe DUBOIS type_init(imx_timer_register_types)
561