xref: /qemu/hw/timer/imx_gpt.c (revision a50c0d6f72830ca3016e63ded6ccfd4e951c172f)
1*a50c0d6fSJean-Christophe DUBOIS /*
2*a50c0d6fSJean-Christophe DUBOIS  * IMX GPT Timer
3*a50c0d6fSJean-Christophe DUBOIS  *
4*a50c0d6fSJean-Christophe DUBOIS  * Copyright (c) 2008 OK Labs
5*a50c0d6fSJean-Christophe DUBOIS  * Copyright (c) 2011 NICTA Pty Ltd
6*a50c0d6fSJean-Christophe DUBOIS  * Originally written by Hans Jiang
7*a50c0d6fSJean-Christophe DUBOIS  * Updated by Peter Chubb
8*a50c0d6fSJean-Christophe DUBOIS  *
9*a50c0d6fSJean-Christophe DUBOIS  * This code is licensed under GPL version 2 or later.  See
10*a50c0d6fSJean-Christophe DUBOIS  * the COPYING file in the top-level directory.
11*a50c0d6fSJean-Christophe DUBOIS  *
12*a50c0d6fSJean-Christophe DUBOIS  */
13*a50c0d6fSJean-Christophe DUBOIS 
14*a50c0d6fSJean-Christophe DUBOIS #include "hw/hw.h"
15*a50c0d6fSJean-Christophe DUBOIS #include "qemu/bitops.h"
16*a50c0d6fSJean-Christophe DUBOIS #include "qemu/timer.h"
17*a50c0d6fSJean-Christophe DUBOIS #include "hw/ptimer.h"
18*a50c0d6fSJean-Christophe DUBOIS #include "hw/sysbus.h"
19*a50c0d6fSJean-Christophe DUBOIS #include "hw/arm/imx.h"
20*a50c0d6fSJean-Christophe DUBOIS 
21*a50c0d6fSJean-Christophe DUBOIS //#define DEBUG_TIMER 1
22*a50c0d6fSJean-Christophe DUBOIS #ifdef DEBUG_TIMER
23*a50c0d6fSJean-Christophe DUBOIS #  define DPRINTF(fmt, args...) \
24*a50c0d6fSJean-Christophe DUBOIS       do { printf("imx_timer: " fmt , ##args); } while (0)
25*a50c0d6fSJean-Christophe DUBOIS #else
26*a50c0d6fSJean-Christophe DUBOIS #  define DPRINTF(fmt, args...) do {} while (0)
27*a50c0d6fSJean-Christophe DUBOIS #endif
28*a50c0d6fSJean-Christophe DUBOIS 
29*a50c0d6fSJean-Christophe DUBOIS /*
30*a50c0d6fSJean-Christophe DUBOIS  * Define to 1 for messages about attempts to
31*a50c0d6fSJean-Christophe DUBOIS  * access unimplemented registers or similar.
32*a50c0d6fSJean-Christophe DUBOIS  */
33*a50c0d6fSJean-Christophe DUBOIS #define DEBUG_IMPLEMENTATION 1
34*a50c0d6fSJean-Christophe DUBOIS #if DEBUG_IMPLEMENTATION
35*a50c0d6fSJean-Christophe DUBOIS #  define IPRINTF(fmt, args...)                                         \
36*a50c0d6fSJean-Christophe DUBOIS     do  { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0)
37*a50c0d6fSJean-Christophe DUBOIS #else
38*a50c0d6fSJean-Christophe DUBOIS #  define IPRINTF(fmt, args...) do {} while (0)
39*a50c0d6fSJean-Christophe DUBOIS #endif
40*a50c0d6fSJean-Christophe DUBOIS 
41*a50c0d6fSJean-Christophe DUBOIS /*
42*a50c0d6fSJean-Christophe DUBOIS  * GPT : General purpose timer
43*a50c0d6fSJean-Christophe DUBOIS  *
44*a50c0d6fSJean-Christophe DUBOIS  * This timer counts up continuously while it is enabled, resetting itself
45*a50c0d6fSJean-Christophe DUBOIS  * to 0 when it reaches TIMER_MAX (in freerun mode) or when it
46*a50c0d6fSJean-Christophe DUBOIS  * reaches the value of ocr1 (in periodic mode).  WE simulate this using a
47*a50c0d6fSJean-Christophe DUBOIS  * QEMU ptimer counting down from ocr1 and reloading from ocr1 in
48*a50c0d6fSJean-Christophe DUBOIS  * periodic mode, or counting from ocr1 to zero, then TIMER_MAX - ocr1.
49*a50c0d6fSJean-Christophe DUBOIS  * waiting_rov is set when counting from TIMER_MAX.
50*a50c0d6fSJean-Christophe DUBOIS  *
51*a50c0d6fSJean-Christophe DUBOIS  * In the real hardware, there are three comparison registers that can
52*a50c0d6fSJean-Christophe DUBOIS  * trigger interrupts, and compare channel 1 can be used to
53*a50c0d6fSJean-Christophe DUBOIS  * force-reset the timer. However, this is a `bare-bones'
54*a50c0d6fSJean-Christophe DUBOIS  * implementation: only what Linux 3.x uses has been implemented
55*a50c0d6fSJean-Christophe DUBOIS  * (free-running timer from 0 to OCR1 or TIMER_MAX) .
56*a50c0d6fSJean-Christophe DUBOIS  */
57*a50c0d6fSJean-Christophe DUBOIS 
58*a50c0d6fSJean-Christophe DUBOIS #define TIMER_MAX  0XFFFFFFFFUL
59*a50c0d6fSJean-Christophe DUBOIS 
60*a50c0d6fSJean-Christophe DUBOIS /* Control register.  Not all of these bits have any effect (yet) */
61*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_EN     (1 << 0)  /* GPT Enable */
62*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_ENMOD  (1 << 1)  /* GPT Enable Mode */
63*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_DBGEN  (1 << 2)  /* GPT Debug mode enable */
64*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_WAITEN (1 << 3)  /* GPT Wait Mode Enable  */
65*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_DOZEN  (1 << 4)  /* GPT Doze mode enable */
66*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_STOPEN (1 << 5)  /* GPT Stop Mode Enable */
67*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_CLKSRC_SHIFT (6)
68*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_CLKSRC_MASK  (0x7)
69*a50c0d6fSJean-Christophe DUBOIS 
70*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FRR    (1 << 9)  /* Freerun or Restart */
71*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_SWR    (1 << 15) /* Software Reset */
72*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_IM1    (3 << 16) /* Input capture channel 1 mode (2 bits) */
73*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_IM2    (3 << 18) /* Input capture channel 2 mode (2 bits) */
74*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_OM1    (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
75*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_OM2    (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
76*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_OM3    (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
77*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FO1    (1 << 29) /* Force Output Compare Channel 1 */
78*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FO2    (1 << 30) /* Force Output Compare Channel 2 */
79*a50c0d6fSJean-Christophe DUBOIS #define GPT_CR_FO3    (1 << 31) /* Force Output Compare Channel 3 */
80*a50c0d6fSJean-Christophe DUBOIS 
81*a50c0d6fSJean-Christophe DUBOIS #define GPT_SR_OF1  (1 << 0)
82*a50c0d6fSJean-Christophe DUBOIS #define GPT_SR_ROV  (1 << 5)
83*a50c0d6fSJean-Christophe DUBOIS 
84*a50c0d6fSJean-Christophe DUBOIS #define GPT_IR_OF1IE  (1 << 0)
85*a50c0d6fSJean-Christophe DUBOIS #define GPT_IR_ROVIE  (1 << 5)
86*a50c0d6fSJean-Christophe DUBOIS 
87*a50c0d6fSJean-Christophe DUBOIS typedef struct {
88*a50c0d6fSJean-Christophe DUBOIS     SysBusDevice busdev;
89*a50c0d6fSJean-Christophe DUBOIS     ptimer_state *timer;
90*a50c0d6fSJean-Christophe DUBOIS     MemoryRegion iomem;
91*a50c0d6fSJean-Christophe DUBOIS     DeviceState *ccm;
92*a50c0d6fSJean-Christophe DUBOIS 
93*a50c0d6fSJean-Christophe DUBOIS     uint32_t cr;
94*a50c0d6fSJean-Christophe DUBOIS     uint32_t pr;
95*a50c0d6fSJean-Christophe DUBOIS     uint32_t sr;
96*a50c0d6fSJean-Christophe DUBOIS     uint32_t ir;
97*a50c0d6fSJean-Christophe DUBOIS     uint32_t ocr1;
98*a50c0d6fSJean-Christophe DUBOIS     uint32_t ocr2;
99*a50c0d6fSJean-Christophe DUBOIS     uint32_t ocr3;
100*a50c0d6fSJean-Christophe DUBOIS     uint32_t icr1;
101*a50c0d6fSJean-Christophe DUBOIS     uint32_t icr2;
102*a50c0d6fSJean-Christophe DUBOIS     uint32_t cnt;
103*a50c0d6fSJean-Christophe DUBOIS 
104*a50c0d6fSJean-Christophe DUBOIS     uint32_t waiting_rov;
105*a50c0d6fSJean-Christophe DUBOIS     qemu_irq irq;
106*a50c0d6fSJean-Christophe DUBOIS } IMXTimerGState;
107*a50c0d6fSJean-Christophe DUBOIS 
108*a50c0d6fSJean-Christophe DUBOIS static const VMStateDescription vmstate_imx_timerg = {
109*a50c0d6fSJean-Christophe DUBOIS     .name = "imx-timerg",
110*a50c0d6fSJean-Christophe DUBOIS     .version_id = 2,
111*a50c0d6fSJean-Christophe DUBOIS     .minimum_version_id = 2,
112*a50c0d6fSJean-Christophe DUBOIS     .minimum_version_id_old = 2,
113*a50c0d6fSJean-Christophe DUBOIS     .fields      = (VMStateField[]) {
114*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(cr, IMXTimerGState),
115*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(pr, IMXTimerGState),
116*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(sr, IMXTimerGState),
117*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ir, IMXTimerGState),
118*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ocr1, IMXTimerGState),
119*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ocr2, IMXTimerGState),
120*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(ocr3, IMXTimerGState),
121*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(icr1, IMXTimerGState),
122*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(icr2, IMXTimerGState),
123*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(cnt, IMXTimerGState),
124*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_UINT32(waiting_rov, IMXTimerGState),
125*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_PTIMER(timer, IMXTimerGState),
126*a50c0d6fSJean-Christophe DUBOIS         VMSTATE_END_OF_LIST()
127*a50c0d6fSJean-Christophe DUBOIS     }
128*a50c0d6fSJean-Christophe DUBOIS };
129*a50c0d6fSJean-Christophe DUBOIS 
130*a50c0d6fSJean-Christophe DUBOIS static const IMXClk imx_timerg_clocks[] = {
131*a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 000 No clock source */
132*a50c0d6fSJean-Christophe DUBOIS     IPG,      /* 001 ipg_clk, 532MHz*/
133*a50c0d6fSJean-Christophe DUBOIS     IPG,      /* 010 ipg_clk_highfreq */
134*a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 011 not defined */
135*a50c0d6fSJean-Christophe DUBOIS     CLK_32k,  /* 100 ipg_clk_32k */
136*a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 101 not defined */
137*a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 110 not defined */
138*a50c0d6fSJean-Christophe DUBOIS     NOCLK,    /* 111 not defined */
139*a50c0d6fSJean-Christophe DUBOIS };
140*a50c0d6fSJean-Christophe DUBOIS 
141*a50c0d6fSJean-Christophe DUBOIS 
142*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_set_freq(IMXTimerGState *s)
143*a50c0d6fSJean-Christophe DUBOIS {
144*a50c0d6fSJean-Christophe DUBOIS     int clksrc;
145*a50c0d6fSJean-Christophe DUBOIS     uint32_t freq;
146*a50c0d6fSJean-Christophe DUBOIS 
147*a50c0d6fSJean-Christophe DUBOIS     clksrc = (s->cr >> GPT_CR_CLKSRC_SHIFT) & GPT_CR_CLKSRC_MASK;
148*a50c0d6fSJean-Christophe DUBOIS     freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc]) / (1 + s->pr);
149*a50c0d6fSJean-Christophe DUBOIS 
150*a50c0d6fSJean-Christophe DUBOIS     DPRINTF("Setting gtimer clksrc %d to frequency %d\n", clksrc, freq);
151*a50c0d6fSJean-Christophe DUBOIS 
152*a50c0d6fSJean-Christophe DUBOIS     if (freq) {
153*a50c0d6fSJean-Christophe DUBOIS         ptimer_set_freq(s->timer, freq);
154*a50c0d6fSJean-Christophe DUBOIS     }
155*a50c0d6fSJean-Christophe DUBOIS }
156*a50c0d6fSJean-Christophe DUBOIS 
157*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_update(IMXTimerGState *s)
158*a50c0d6fSJean-Christophe DUBOIS {
159*a50c0d6fSJean-Christophe DUBOIS     uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV);
160*a50c0d6fSJean-Christophe DUBOIS 
161*a50c0d6fSJean-Christophe DUBOIS     DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n",
162*a50c0d6fSJean-Christophe DUBOIS             s->sr & GPT_SR_OF1 ? "OF1" : "",
163*a50c0d6fSJean-Christophe DUBOIS             s->sr & GPT_SR_ROV ? "ROV" : "",
164*a50c0d6fSJean-Christophe DUBOIS             s->ir & GPT_SR_OF1 ? "OF1" : "",
165*a50c0d6fSJean-Christophe DUBOIS             s->ir & GPT_SR_ROV ? "ROV" : "",
166*a50c0d6fSJean-Christophe DUBOIS             s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled");
167*a50c0d6fSJean-Christophe DUBOIS 
168*a50c0d6fSJean-Christophe DUBOIS     qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags);
169*a50c0d6fSJean-Christophe DUBOIS }
170*a50c0d6fSJean-Christophe DUBOIS 
171*a50c0d6fSJean-Christophe DUBOIS static uint32_t imx_timerg_update_counts(IMXTimerGState *s)
172*a50c0d6fSJean-Christophe DUBOIS {
173*a50c0d6fSJean-Christophe DUBOIS     uint64_t target = s->waiting_rov ? TIMER_MAX : s->ocr1;
174*a50c0d6fSJean-Christophe DUBOIS     uint64_t cnt = ptimer_get_count(s->timer);
175*a50c0d6fSJean-Christophe DUBOIS     s->cnt = target - cnt;
176*a50c0d6fSJean-Christophe DUBOIS     return s->cnt;
177*a50c0d6fSJean-Christophe DUBOIS }
178*a50c0d6fSJean-Christophe DUBOIS 
179*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_reload(IMXTimerGState *s, uint32_t timeout)
180*a50c0d6fSJean-Christophe DUBOIS {
181*a50c0d6fSJean-Christophe DUBOIS     uint64_t diff_cnt;
182*a50c0d6fSJean-Christophe DUBOIS 
183*a50c0d6fSJean-Christophe DUBOIS     if (!(s->cr & GPT_CR_FRR)) {
184*a50c0d6fSJean-Christophe DUBOIS         IPRINTF("IMX_timerg_reload --- called in reset-mode\n");
185*a50c0d6fSJean-Christophe DUBOIS         return;
186*a50c0d6fSJean-Christophe DUBOIS     }
187*a50c0d6fSJean-Christophe DUBOIS 
188*a50c0d6fSJean-Christophe DUBOIS     /*
189*a50c0d6fSJean-Christophe DUBOIS      * For small timeouts, qemu sometimes runs too slow.
190*a50c0d6fSJean-Christophe DUBOIS      * Better deliver a late interrupt than none.
191*a50c0d6fSJean-Christophe DUBOIS      *
192*a50c0d6fSJean-Christophe DUBOIS      * In Reset mode (FRR bit clear)
193*a50c0d6fSJean-Christophe DUBOIS      * the ptimer reloads itself from OCR1;
194*a50c0d6fSJean-Christophe DUBOIS      * in free-running mode we need to fake
195*a50c0d6fSJean-Christophe DUBOIS      * running from 0 to ocr1 to TIMER_MAX
196*a50c0d6fSJean-Christophe DUBOIS      */
197*a50c0d6fSJean-Christophe DUBOIS     if (timeout > s->cnt) {
198*a50c0d6fSJean-Christophe DUBOIS         diff_cnt = timeout - s->cnt;
199*a50c0d6fSJean-Christophe DUBOIS     } else {
200*a50c0d6fSJean-Christophe DUBOIS         diff_cnt = 0;
201*a50c0d6fSJean-Christophe DUBOIS     }
202*a50c0d6fSJean-Christophe DUBOIS     ptimer_set_count(s->timer, diff_cnt);
203*a50c0d6fSJean-Christophe DUBOIS }
204*a50c0d6fSJean-Christophe DUBOIS 
205*a50c0d6fSJean-Christophe DUBOIS static uint64_t imx_timerg_read(void *opaque, hwaddr offset,
206*a50c0d6fSJean-Christophe DUBOIS                                 unsigned size)
207*a50c0d6fSJean-Christophe DUBOIS {
208*a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = (IMXTimerGState *)opaque;
209*a50c0d6fSJean-Christophe DUBOIS 
210*a50c0d6fSJean-Christophe DUBOIS     DPRINTF("g-read(offset=%x)", (unsigned int)(offset >> 2));
211*a50c0d6fSJean-Christophe DUBOIS     switch (offset >> 2) {
212*a50c0d6fSJean-Christophe DUBOIS     case 0: /* Control Register */
213*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" cr = %x\n", s->cr);
214*a50c0d6fSJean-Christophe DUBOIS         return s->cr;
215*a50c0d6fSJean-Christophe DUBOIS 
216*a50c0d6fSJean-Christophe DUBOIS     case 1: /* prescaler */
217*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" pr = %x\n", s->pr);
218*a50c0d6fSJean-Christophe DUBOIS         return s->pr;
219*a50c0d6fSJean-Christophe DUBOIS 
220*a50c0d6fSJean-Christophe DUBOIS     case 2: /* Status Register */
221*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" sr = %x\n", s->sr);
222*a50c0d6fSJean-Christophe DUBOIS         return s->sr;
223*a50c0d6fSJean-Christophe DUBOIS 
224*a50c0d6fSJean-Christophe DUBOIS     case 3: /* Interrupt Register */
225*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" ir = %x\n", s->ir);
226*a50c0d6fSJean-Christophe DUBOIS         return s->ir;
227*a50c0d6fSJean-Christophe DUBOIS 
228*a50c0d6fSJean-Christophe DUBOIS     case 4: /* Output Compare Register 1 */
229*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" ocr1 = %x\n", s->ocr1);
230*a50c0d6fSJean-Christophe DUBOIS         return s->ocr1;
231*a50c0d6fSJean-Christophe DUBOIS 
232*a50c0d6fSJean-Christophe DUBOIS     case 5: /* Output Compare Register 2 */
233*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" ocr2 = %x\n", s->ocr2);
234*a50c0d6fSJean-Christophe DUBOIS         return s->ocr2;
235*a50c0d6fSJean-Christophe DUBOIS 
236*a50c0d6fSJean-Christophe DUBOIS     case 6: /* Output Compare Register 3 */
237*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" ocr3 = %x\n", s->ocr3);
238*a50c0d6fSJean-Christophe DUBOIS         return s->ocr3;
239*a50c0d6fSJean-Christophe DUBOIS 
240*a50c0d6fSJean-Christophe DUBOIS     case 7: /* input Capture Register 1 */
241*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" icr1 = %x\n", s->icr1);
242*a50c0d6fSJean-Christophe DUBOIS         return s->icr1;
243*a50c0d6fSJean-Christophe DUBOIS 
244*a50c0d6fSJean-Christophe DUBOIS     case 8: /* input Capture Register 2 */
245*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" icr2 = %x\n", s->icr2);
246*a50c0d6fSJean-Christophe DUBOIS         return s->icr2;
247*a50c0d6fSJean-Christophe DUBOIS 
248*a50c0d6fSJean-Christophe DUBOIS     case 9: /* cnt */
249*a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update_counts(s);
250*a50c0d6fSJean-Christophe DUBOIS         DPRINTF(" cnt = %x\n", s->cnt);
251*a50c0d6fSJean-Christophe DUBOIS         return s->cnt;
252*a50c0d6fSJean-Christophe DUBOIS     }
253*a50c0d6fSJean-Christophe DUBOIS 
254*a50c0d6fSJean-Christophe DUBOIS     IPRINTF("imx_timerg_read: Bad offset %x\n",
255*a50c0d6fSJean-Christophe DUBOIS             (int)offset >> 2);
256*a50c0d6fSJean-Christophe DUBOIS 
257*a50c0d6fSJean-Christophe DUBOIS     return 0;
258*a50c0d6fSJean-Christophe DUBOIS }
259*a50c0d6fSJean-Christophe DUBOIS 
260*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_reset(DeviceState *dev)
261*a50c0d6fSJean-Christophe DUBOIS {
262*a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = container_of(dev, IMXTimerGState, busdev.qdev);
263*a50c0d6fSJean-Christophe DUBOIS 
264*a50c0d6fSJean-Christophe DUBOIS     /*
265*a50c0d6fSJean-Christophe DUBOIS      * Soft reset doesn't touch some bits; hard reset clears them
266*a50c0d6fSJean-Christophe DUBOIS      */
267*a50c0d6fSJean-Christophe DUBOIS     s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN|
268*a50c0d6fSJean-Christophe DUBOIS                GPT_CR_WAITEN|GPT_CR_DBGEN);
269*a50c0d6fSJean-Christophe DUBOIS     s->sr = 0;
270*a50c0d6fSJean-Christophe DUBOIS     s->pr = 0;
271*a50c0d6fSJean-Christophe DUBOIS     s->ir = 0;
272*a50c0d6fSJean-Christophe DUBOIS     s->cnt = 0;
273*a50c0d6fSJean-Christophe DUBOIS     s->ocr1 = TIMER_MAX;
274*a50c0d6fSJean-Christophe DUBOIS     s->ocr2 = TIMER_MAX;
275*a50c0d6fSJean-Christophe DUBOIS     s->ocr3 = TIMER_MAX;
276*a50c0d6fSJean-Christophe DUBOIS     s->icr1 = 0;
277*a50c0d6fSJean-Christophe DUBOIS     s->icr2 = 0;
278*a50c0d6fSJean-Christophe DUBOIS     ptimer_stop(s->timer);
279*a50c0d6fSJean-Christophe DUBOIS     ptimer_set_limit(s->timer, TIMER_MAX, 1);
280*a50c0d6fSJean-Christophe DUBOIS     ptimer_set_count(s->timer, TIMER_MAX);
281*a50c0d6fSJean-Christophe DUBOIS     imx_timerg_set_freq(s);
282*a50c0d6fSJean-Christophe DUBOIS }
283*a50c0d6fSJean-Christophe DUBOIS 
284*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_write(void *opaque, hwaddr offset,
285*a50c0d6fSJean-Christophe DUBOIS                              uint64_t value, unsigned size)
286*a50c0d6fSJean-Christophe DUBOIS {
287*a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = (IMXTimerGState *)opaque;
288*a50c0d6fSJean-Christophe DUBOIS     DPRINTF("g-write(offset=%x, value = 0x%x)\n", (unsigned int)offset >> 2,
289*a50c0d6fSJean-Christophe DUBOIS             (unsigned int)value);
290*a50c0d6fSJean-Christophe DUBOIS 
291*a50c0d6fSJean-Christophe DUBOIS     switch (offset >> 2) {
292*a50c0d6fSJean-Christophe DUBOIS     case 0: {
293*a50c0d6fSJean-Christophe DUBOIS         uint32_t oldcr = s->cr;
294*a50c0d6fSJean-Christophe DUBOIS         /* CR */
295*a50c0d6fSJean-Christophe DUBOIS         if (value & GPT_CR_SWR) { /* force reset */
296*a50c0d6fSJean-Christophe DUBOIS             value &= ~GPT_CR_SWR;
297*a50c0d6fSJean-Christophe DUBOIS             imx_timerg_reset(&s->busdev.qdev);
298*a50c0d6fSJean-Christophe DUBOIS             imx_timerg_update(s);
299*a50c0d6fSJean-Christophe DUBOIS         }
300*a50c0d6fSJean-Christophe DUBOIS 
301*a50c0d6fSJean-Christophe DUBOIS         s->cr = value & ~0x7c00;
302*a50c0d6fSJean-Christophe DUBOIS         imx_timerg_set_freq(s);
303*a50c0d6fSJean-Christophe DUBOIS         if ((oldcr ^ value) & GPT_CR_EN) {
304*a50c0d6fSJean-Christophe DUBOIS             if (value & GPT_CR_EN) {
305*a50c0d6fSJean-Christophe DUBOIS                 if (value & GPT_CR_ENMOD) {
306*a50c0d6fSJean-Christophe DUBOIS                     ptimer_set_count(s->timer, s->ocr1);
307*a50c0d6fSJean-Christophe DUBOIS                     s->cnt = 0;
308*a50c0d6fSJean-Christophe DUBOIS                 }
309*a50c0d6fSJean-Christophe DUBOIS                 ptimer_run(s->timer,
310*a50c0d6fSJean-Christophe DUBOIS                            (value & GPT_CR_FRR) && (s->ocr1 != TIMER_MAX));
311*a50c0d6fSJean-Christophe DUBOIS             } else {
312*a50c0d6fSJean-Christophe DUBOIS                 ptimer_stop(s->timer);
313*a50c0d6fSJean-Christophe DUBOIS             };
314*a50c0d6fSJean-Christophe DUBOIS         }
315*a50c0d6fSJean-Christophe DUBOIS         return;
316*a50c0d6fSJean-Christophe DUBOIS     }
317*a50c0d6fSJean-Christophe DUBOIS 
318*a50c0d6fSJean-Christophe DUBOIS     case 1: /* Prescaler */
319*a50c0d6fSJean-Christophe DUBOIS         s->pr = value & 0xfff;
320*a50c0d6fSJean-Christophe DUBOIS         imx_timerg_set_freq(s);
321*a50c0d6fSJean-Christophe DUBOIS         return;
322*a50c0d6fSJean-Christophe DUBOIS 
323*a50c0d6fSJean-Christophe DUBOIS     case 2: /* SR */
324*a50c0d6fSJean-Christophe DUBOIS         /*
325*a50c0d6fSJean-Christophe DUBOIS          * No point in implementing the status register bits to do with
326*a50c0d6fSJean-Christophe DUBOIS          * external interrupt sources.
327*a50c0d6fSJean-Christophe DUBOIS          */
328*a50c0d6fSJean-Christophe DUBOIS         value &= GPT_SR_OF1 | GPT_SR_ROV;
329*a50c0d6fSJean-Christophe DUBOIS         s->sr &= ~value;
330*a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update(s);
331*a50c0d6fSJean-Christophe DUBOIS         return;
332*a50c0d6fSJean-Christophe DUBOIS 
333*a50c0d6fSJean-Christophe DUBOIS     case 3: /* IR -- interrupt register */
334*a50c0d6fSJean-Christophe DUBOIS         s->ir = value & 0x3f;
335*a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update(s);
336*a50c0d6fSJean-Christophe DUBOIS         return;
337*a50c0d6fSJean-Christophe DUBOIS 
338*a50c0d6fSJean-Christophe DUBOIS     case 4: /* OCR1 -- output compare register */
339*a50c0d6fSJean-Christophe DUBOIS         /* In non-freerun mode, reset count when this register is written */
340*a50c0d6fSJean-Christophe DUBOIS         if (!(s->cr & GPT_CR_FRR)) {
341*a50c0d6fSJean-Christophe DUBOIS             s->waiting_rov = 0;
342*a50c0d6fSJean-Christophe DUBOIS             ptimer_set_limit(s->timer, value, 1);
343*a50c0d6fSJean-Christophe DUBOIS         } else {
344*a50c0d6fSJean-Christophe DUBOIS             imx_timerg_update_counts(s);
345*a50c0d6fSJean-Christophe DUBOIS             if (value > s->cnt) {
346*a50c0d6fSJean-Christophe DUBOIS                 s->waiting_rov = 0;
347*a50c0d6fSJean-Christophe DUBOIS                 imx_timerg_reload(s, value);
348*a50c0d6fSJean-Christophe DUBOIS             } else {
349*a50c0d6fSJean-Christophe DUBOIS                 s->waiting_rov = 1;
350*a50c0d6fSJean-Christophe DUBOIS                 imx_timerg_reload(s, TIMER_MAX - s->cnt);
351*a50c0d6fSJean-Christophe DUBOIS             }
352*a50c0d6fSJean-Christophe DUBOIS         }
353*a50c0d6fSJean-Christophe DUBOIS         s->ocr1 = value;
354*a50c0d6fSJean-Christophe DUBOIS         return;
355*a50c0d6fSJean-Christophe DUBOIS 
356*a50c0d6fSJean-Christophe DUBOIS     case 5: /* OCR2 -- output compare register */
357*a50c0d6fSJean-Christophe DUBOIS     case 6: /* OCR3 -- output compare register */
358*a50c0d6fSJean-Christophe DUBOIS     default:
359*a50c0d6fSJean-Christophe DUBOIS         IPRINTF("imx_timerg_write: Bad offset %x\n",
360*a50c0d6fSJean-Christophe DUBOIS                 (int)offset >> 2);
361*a50c0d6fSJean-Christophe DUBOIS     }
362*a50c0d6fSJean-Christophe DUBOIS }
363*a50c0d6fSJean-Christophe DUBOIS 
364*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_timeout(void *opaque)
365*a50c0d6fSJean-Christophe DUBOIS {
366*a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = (IMXTimerGState *)opaque;
367*a50c0d6fSJean-Christophe DUBOIS 
368*a50c0d6fSJean-Christophe DUBOIS     DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
369*a50c0d6fSJean-Christophe DUBOIS     if (s->cr & GPT_CR_FRR) {
370*a50c0d6fSJean-Christophe DUBOIS         /*
371*a50c0d6fSJean-Christophe DUBOIS          * Free running timer from 0 -> TIMERMAX
372*a50c0d6fSJean-Christophe DUBOIS          * Generates interrupt at TIMER_MAX and at cnt==ocr1
373*a50c0d6fSJean-Christophe DUBOIS          * If ocr1 == TIMER_MAX, then no need to reload timer.
374*a50c0d6fSJean-Christophe DUBOIS          */
375*a50c0d6fSJean-Christophe DUBOIS         if (s->ocr1 == TIMER_MAX) {
376*a50c0d6fSJean-Christophe DUBOIS             DPRINTF("s->ocr1 == TIMER_MAX, FRR\n");
377*a50c0d6fSJean-Christophe DUBOIS             s->sr |= GPT_SR_OF1 | GPT_SR_ROV;
378*a50c0d6fSJean-Christophe DUBOIS             imx_timerg_update(s);
379*a50c0d6fSJean-Christophe DUBOIS             return;
380*a50c0d6fSJean-Christophe DUBOIS         }
381*a50c0d6fSJean-Christophe DUBOIS 
382*a50c0d6fSJean-Christophe DUBOIS         if (s->waiting_rov) {
383*a50c0d6fSJean-Christophe DUBOIS             /*
384*a50c0d6fSJean-Christophe DUBOIS              * We were waiting for cnt==TIMER_MAX
385*a50c0d6fSJean-Christophe DUBOIS              */
386*a50c0d6fSJean-Christophe DUBOIS             s->sr |= GPT_SR_ROV;
387*a50c0d6fSJean-Christophe DUBOIS             s->waiting_rov = 0;
388*a50c0d6fSJean-Christophe DUBOIS             s->cnt = 0;
389*a50c0d6fSJean-Christophe DUBOIS             imx_timerg_reload(s, s->ocr1);
390*a50c0d6fSJean-Christophe DUBOIS         } else {
391*a50c0d6fSJean-Christophe DUBOIS             /* Must have got a cnt==ocr1 timeout. */
392*a50c0d6fSJean-Christophe DUBOIS             s->sr |= GPT_SR_OF1;
393*a50c0d6fSJean-Christophe DUBOIS             s->cnt = s->ocr1;
394*a50c0d6fSJean-Christophe DUBOIS             s->waiting_rov = 1;
395*a50c0d6fSJean-Christophe DUBOIS             imx_timerg_reload(s, TIMER_MAX);
396*a50c0d6fSJean-Christophe DUBOIS         }
397*a50c0d6fSJean-Christophe DUBOIS         imx_timerg_update(s);
398*a50c0d6fSJean-Christophe DUBOIS         return;
399*a50c0d6fSJean-Christophe DUBOIS     }
400*a50c0d6fSJean-Christophe DUBOIS 
401*a50c0d6fSJean-Christophe DUBOIS     s->sr |= GPT_SR_OF1;
402*a50c0d6fSJean-Christophe DUBOIS     imx_timerg_update(s);
403*a50c0d6fSJean-Christophe DUBOIS }
404*a50c0d6fSJean-Christophe DUBOIS 
405*a50c0d6fSJean-Christophe DUBOIS static const MemoryRegionOps imx_timerg_ops = {
406*a50c0d6fSJean-Christophe DUBOIS     .read = imx_timerg_read,
407*a50c0d6fSJean-Christophe DUBOIS     .write = imx_timerg_write,
408*a50c0d6fSJean-Christophe DUBOIS     .endianness = DEVICE_NATIVE_ENDIAN,
409*a50c0d6fSJean-Christophe DUBOIS };
410*a50c0d6fSJean-Christophe DUBOIS 
411*a50c0d6fSJean-Christophe DUBOIS 
412*a50c0d6fSJean-Christophe DUBOIS static int imx_timerg_init(SysBusDevice *dev)
413*a50c0d6fSJean-Christophe DUBOIS {
414*a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev);
415*a50c0d6fSJean-Christophe DUBOIS     QEMUBH *bh;
416*a50c0d6fSJean-Christophe DUBOIS 
417*a50c0d6fSJean-Christophe DUBOIS     sysbus_init_irq(dev, &s->irq);
418*a50c0d6fSJean-Christophe DUBOIS     memory_region_init_io(&s->iomem, &imx_timerg_ops,
419*a50c0d6fSJean-Christophe DUBOIS                           s, "imxg-timer",
420*a50c0d6fSJean-Christophe DUBOIS                           0x00001000);
421*a50c0d6fSJean-Christophe DUBOIS     sysbus_init_mmio(dev, &s->iomem);
422*a50c0d6fSJean-Christophe DUBOIS 
423*a50c0d6fSJean-Christophe DUBOIS     bh = qemu_bh_new(imx_timerg_timeout, s);
424*a50c0d6fSJean-Christophe DUBOIS     s->timer = ptimer_init(bh);
425*a50c0d6fSJean-Christophe DUBOIS 
426*a50c0d6fSJean-Christophe DUBOIS     /* Hard reset resets extra bits in CR */
427*a50c0d6fSJean-Christophe DUBOIS     s->cr = 0;
428*a50c0d6fSJean-Christophe DUBOIS     return 0;
429*a50c0d6fSJean-Christophe DUBOIS }
430*a50c0d6fSJean-Christophe DUBOIS 
431*a50c0d6fSJean-Christophe DUBOIS void imx_timerg_create(const hwaddr addr,
432*a50c0d6fSJean-Christophe DUBOIS                               qemu_irq irq,
433*a50c0d6fSJean-Christophe DUBOIS                               DeviceState *ccm)
434*a50c0d6fSJean-Christophe DUBOIS {
435*a50c0d6fSJean-Christophe DUBOIS     IMXTimerGState *pp;
436*a50c0d6fSJean-Christophe DUBOIS     DeviceState *dev;
437*a50c0d6fSJean-Christophe DUBOIS 
438*a50c0d6fSJean-Christophe DUBOIS     dev = sysbus_create_simple("imx_timerg", addr, irq);
439*a50c0d6fSJean-Christophe DUBOIS     pp = container_of(dev, IMXTimerGState, busdev.qdev);
440*a50c0d6fSJean-Christophe DUBOIS     pp->ccm = ccm;
441*a50c0d6fSJean-Christophe DUBOIS }
442*a50c0d6fSJean-Christophe DUBOIS 
443*a50c0d6fSJean-Christophe DUBOIS static void imx_timerg_class_init(ObjectClass *klass, void *data)
444*a50c0d6fSJean-Christophe DUBOIS {
445*a50c0d6fSJean-Christophe DUBOIS     DeviceClass *dc  = DEVICE_CLASS(klass);
446*a50c0d6fSJean-Christophe DUBOIS     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
447*a50c0d6fSJean-Christophe DUBOIS     k->init = imx_timerg_init;
448*a50c0d6fSJean-Christophe DUBOIS     dc->vmsd = &vmstate_imx_timerg;
449*a50c0d6fSJean-Christophe DUBOIS     dc->reset = imx_timerg_reset;
450*a50c0d6fSJean-Christophe DUBOIS     dc->desc = "i.MX general timer";
451*a50c0d6fSJean-Christophe DUBOIS }
452*a50c0d6fSJean-Christophe DUBOIS 
453*a50c0d6fSJean-Christophe DUBOIS static const TypeInfo imx_timerg_info = {
454*a50c0d6fSJean-Christophe DUBOIS     .name = "imx_timerg",
455*a50c0d6fSJean-Christophe DUBOIS     .parent = TYPE_SYS_BUS_DEVICE,
456*a50c0d6fSJean-Christophe DUBOIS     .instance_size = sizeof(IMXTimerGState),
457*a50c0d6fSJean-Christophe DUBOIS     .class_init = imx_timerg_class_init,
458*a50c0d6fSJean-Christophe DUBOIS };
459*a50c0d6fSJean-Christophe DUBOIS 
460*a50c0d6fSJean-Christophe DUBOIS static void imx_timer_register_types(void)
461*a50c0d6fSJean-Christophe DUBOIS {
462*a50c0d6fSJean-Christophe DUBOIS     type_register_static(&imx_timerg_info);
463*a50c0d6fSJean-Christophe DUBOIS }
464*a50c0d6fSJean-Christophe DUBOIS 
465*a50c0d6fSJean-Christophe DUBOIS type_init(imx_timer_register_types)
466