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