]>
Commit | Line | Data |
---|---|---|
0f3a4a01 FC |
1 | /* |
2 | * QEMU GRLIB GPTimer Emulator | |
3 | * | |
4 | * Copyright (c) 2010-2011 AdaCore | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "sysbus.h" | |
26 | #include "qemu-timer.h" | |
27 | ||
28 | #include "trace.h" | |
29 | ||
30 | #define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ | |
31 | #define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ | |
32 | ||
33 | #define GPTIMER_MAX_TIMERS 8 | |
34 | ||
35 | /* GPTimer Config register fields */ | |
36 | #define GPTIMER_ENABLE (1 << 0) | |
37 | #define GPTIMER_RESTART (1 << 1) | |
38 | #define GPTIMER_LOAD (1 << 2) | |
39 | #define GPTIMER_INT_ENABLE (1 << 3) | |
40 | #define GPTIMER_INT_PENDING (1 << 4) | |
41 | #define GPTIMER_CHAIN (1 << 5) /* Not supported */ | |
42 | #define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ | |
43 | ||
44 | /* Memory mapped register offsets */ | |
45 | #define SCALER_OFFSET 0x00 | |
46 | #define SCALER_RELOAD_OFFSET 0x04 | |
47 | #define CONFIG_OFFSET 0x08 | |
48 | #define COUNTER_OFFSET 0x00 | |
49 | #define COUNTER_RELOAD_OFFSET 0x04 | |
50 | #define TIMER_BASE 0x10 | |
51 | ||
52 | typedef struct GPTimer GPTimer; | |
53 | typedef struct GPTimerUnit GPTimerUnit; | |
54 | ||
55 | struct GPTimer { | |
56 | QEMUBH *bh; | |
57 | struct ptimer_state *ptimer; | |
58 | ||
59 | qemu_irq irq; | |
60 | int id; | |
61 | GPTimerUnit *unit; | |
62 | ||
63 | /* registers */ | |
64 | uint32_t counter; | |
65 | uint32_t reload; | |
66 | uint32_t config; | |
67 | }; | |
68 | ||
69 | struct GPTimerUnit { | |
70 | SysBusDevice busdev; | |
71 | ||
72 | uint32_t nr_timers; /* Number of timers available */ | |
73 | uint32_t freq_hz; /* System frequency */ | |
74 | uint32_t irq_line; /* Base irq line */ | |
75 | ||
76 | GPTimer *timers; | |
77 | ||
78 | /* registers */ | |
79 | uint32_t scaler; | |
80 | uint32_t reload; | |
81 | uint32_t config; | |
82 | }; | |
83 | ||
84 | static void grlib_gptimer_enable(GPTimer *timer) | |
85 | { | |
86 | assert(timer != NULL); | |
87 | ||
88 | ||
89 | ptimer_stop(timer->ptimer); | |
90 | ||
91 | if (!(timer->config & GPTIMER_ENABLE)) { | |
92 | /* Timer disabled */ | |
93 | trace_grlib_gptimer_disabled(timer->id, timer->config); | |
94 | return; | |
95 | } | |
96 | ||
97 | /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at | |
98 | underflow. Set count + 1 to simulate the GPTimer behavior. */ | |
99 | ||
100 | trace_grlib_gptimer_enable(timer->id, timer->counter + 1); | |
101 | ||
102 | ptimer_set_count(timer->ptimer, timer->counter + 1); | |
103 | ptimer_run(timer->ptimer, 1); | |
104 | } | |
105 | ||
106 | static void grlib_gptimer_restart(GPTimer *timer) | |
107 | { | |
108 | assert(timer != NULL); | |
109 | ||
110 | trace_grlib_gptimer_restart(timer->id, timer->reload); | |
111 | ||
112 | timer->counter = timer->reload; | |
113 | grlib_gptimer_enable(timer); | |
114 | } | |
115 | ||
116 | static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) | |
117 | { | |
118 | int i = 0; | |
119 | uint32_t value = 0; | |
120 | ||
121 | assert(unit != NULL); | |
122 | ||
123 | if (scaler > 0) { | |
124 | value = unit->freq_hz / (scaler + 1); | |
125 | } else { | |
126 | value = unit->freq_hz; | |
127 | } | |
128 | ||
129 | trace_grlib_gptimer_set_scaler(scaler, value); | |
130 | ||
131 | for (i = 0; i < unit->nr_timers; i++) { | |
132 | ptimer_set_freq(unit->timers[i].ptimer, value); | |
133 | } | |
134 | } | |
135 | ||
136 | static void grlib_gptimer_hit(void *opaque) | |
137 | { | |
138 | GPTimer *timer = opaque; | |
139 | assert(timer != NULL); | |
140 | ||
141 | trace_grlib_gptimer_hit(timer->id); | |
142 | ||
143 | /* Timer expired */ | |
144 | ||
145 | if (timer->config & GPTIMER_INT_ENABLE) { | |
146 | /* Set the pending bit (only unset by write in the config register) */ | |
147 | timer->config |= GPTIMER_INT_PENDING; | |
148 | qemu_irq_pulse(timer->irq); | |
149 | } | |
150 | ||
151 | if (timer->config & GPTIMER_RESTART) { | |
152 | grlib_gptimer_restart(timer); | |
153 | } | |
154 | } | |
155 | ||
156 | static uint32_t grlib_gptimer_readl(void *opaque, target_phys_addr_t addr) | |
157 | { | |
158 | GPTimerUnit *unit = opaque; | |
159 | target_phys_addr_t timer_addr; | |
160 | int id; | |
161 | uint32_t value = 0; | |
162 | ||
163 | addr &= 0xff; | |
164 | ||
165 | /* Unit registers */ | |
166 | switch (addr) { | |
167 | case SCALER_OFFSET: | |
168 | trace_grlib_gptimer_readl(-1, "scaler:", unit->scaler); | |
169 | return unit->scaler; | |
170 | ||
171 | case SCALER_RELOAD_OFFSET: | |
172 | trace_grlib_gptimer_readl(-1, "reload:", unit->reload); | |
173 | return unit->reload; | |
174 | ||
175 | case CONFIG_OFFSET: | |
176 | trace_grlib_gptimer_readl(-1, "config:", unit->config); | |
177 | return unit->config; | |
178 | ||
179 | default: | |
180 | break; | |
181 | } | |
182 | ||
183 | timer_addr = (addr % TIMER_BASE); | |
184 | id = (addr - TIMER_BASE) / TIMER_BASE; | |
185 | ||
186 | if (id >= 0 && id < unit->nr_timers) { | |
187 | ||
188 | /* GPTimer registers */ | |
189 | switch (timer_addr) { | |
190 | case COUNTER_OFFSET: | |
191 | value = ptimer_get_count(unit->timers[id].ptimer); | |
192 | trace_grlib_gptimer_readl(id, "counter value:", value); | |
193 | return value; | |
194 | ||
195 | case COUNTER_RELOAD_OFFSET: | |
196 | value = unit->timers[id].reload; | |
197 | trace_grlib_gptimer_readl(id, "reload value:", value); | |
198 | return value; | |
199 | ||
200 | case CONFIG_OFFSET: | |
201 | trace_grlib_gptimer_readl(id, "scaler value:", | |
202 | unit->timers[id].config); | |
203 | return unit->timers[id].config; | |
204 | ||
205 | default: | |
206 | break; | |
207 | } | |
208 | ||
209 | } | |
210 | ||
211 | trace_grlib_gptimer_unknown_register("read", addr); | |
212 | return 0; | |
213 | } | |
214 | ||
215 | static void | |
216 | grlib_gptimer_writel(void *opaque, target_phys_addr_t addr, uint32_t value) | |
217 | { | |
218 | GPTimerUnit *unit = opaque; | |
219 | target_phys_addr_t timer_addr; | |
220 | int id; | |
221 | ||
222 | addr &= 0xff; | |
223 | ||
224 | /* Unit registers */ | |
225 | switch (addr) { | |
226 | case SCALER_OFFSET: | |
227 | value &= 0xFFFF; /* clean up the value */ | |
228 | unit->scaler = value; | |
229 | trace_grlib_gptimer_writel(-1, "scaler:", unit->scaler); | |
230 | return; | |
231 | ||
232 | case SCALER_RELOAD_OFFSET: | |
233 | value &= 0xFFFF; /* clean up the value */ | |
234 | unit->reload = value; | |
235 | trace_grlib_gptimer_writel(-1, "reload:", unit->reload); | |
236 | grlib_gptimer_set_scaler(unit, value); | |
237 | return; | |
238 | ||
239 | case CONFIG_OFFSET: | |
240 | /* Read Only (disable timer freeze not supported) */ | |
241 | trace_grlib_gptimer_writel(-1, "config (Read Only):", 0); | |
242 | return; | |
243 | ||
244 | default: | |
245 | break; | |
246 | } | |
247 | ||
248 | timer_addr = (addr % TIMER_BASE); | |
249 | id = (addr - TIMER_BASE) / TIMER_BASE; | |
250 | ||
251 | if (id >= 0 && id < unit->nr_timers) { | |
252 | ||
253 | /* GPTimer registers */ | |
254 | switch (timer_addr) { | |
255 | case COUNTER_OFFSET: | |
256 | trace_grlib_gptimer_writel(id, "counter:", value); | |
257 | unit->timers[id].counter = value; | |
258 | grlib_gptimer_enable(&unit->timers[id]); | |
259 | return; | |
260 | ||
261 | case COUNTER_RELOAD_OFFSET: | |
262 | trace_grlib_gptimer_writel(id, "reload:", value); | |
263 | unit->timers[id].reload = value; | |
264 | return; | |
265 | ||
266 | case CONFIG_OFFSET: | |
267 | trace_grlib_gptimer_writel(id, "config:", value); | |
268 | ||
269 | if (value & GPTIMER_INT_PENDING) { | |
270 | /* clear pending bit */ | |
271 | value &= ~GPTIMER_INT_PENDING; | |
272 | } else { | |
273 | /* keep pending bit */ | |
274 | value |= unit->timers[id].config & GPTIMER_INT_PENDING; | |
275 | } | |
276 | ||
277 | unit->timers[id].config = value; | |
278 | ||
279 | /* gptimer_restart calls gptimer_enable, so if "enable" and "load" | |
280 | bits are present, we just have to call restart. */ | |
281 | ||
282 | if (value & GPTIMER_LOAD) { | |
283 | grlib_gptimer_restart(&unit->timers[id]); | |
284 | } else if (value & GPTIMER_ENABLE) { | |
285 | grlib_gptimer_enable(&unit->timers[id]); | |
286 | } | |
287 | ||
288 | /* These fields must always be read as 0 */ | |
289 | value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); | |
290 | ||
291 | unit->timers[id].config = value; | |
292 | return; | |
293 | ||
294 | default: | |
295 | break; | |
296 | } | |
297 | ||
298 | } | |
299 | ||
300 | trace_grlib_gptimer_unknown_register("write", addr); | |
301 | } | |
302 | ||
303 | static CPUReadMemoryFunc * const grlib_gptimer_read[] = { | |
304 | NULL, NULL, grlib_gptimer_readl, | |
305 | }; | |
306 | ||
307 | static CPUWriteMemoryFunc * const grlib_gptimer_write[] = { | |
308 | NULL, NULL, grlib_gptimer_writel, | |
309 | }; | |
310 | ||
311 | static void grlib_gptimer_reset(DeviceState *d) | |
312 | { | |
313 | GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev); | |
314 | int i = 0; | |
315 | ||
316 | assert(unit != NULL); | |
317 | ||
318 | unit->scaler = 0; | |
319 | unit->reload = 0; | |
320 | unit->config = 0; | |
321 | ||
322 | unit->config = unit->nr_timers; | |
323 | unit->config |= unit->irq_line << 3; | |
324 | unit->config |= 1 << 8; /* separate interrupt */ | |
325 | unit->config |= 1 << 9; /* Disable timer freeze */ | |
326 | ||
327 | ||
328 | for (i = 0; i < unit->nr_timers; i++) { | |
329 | GPTimer *timer = &unit->timers[i]; | |
330 | ||
331 | timer->counter = 0; | |
332 | timer->reload = 0; | |
333 | timer->config = 0; | |
334 | ptimer_stop(timer->ptimer); | |
335 | ptimer_set_count(timer->ptimer, 0); | |
336 | ptimer_set_freq(timer->ptimer, unit->freq_hz); | |
337 | } | |
338 | } | |
339 | ||
340 | static int grlib_gptimer_init(SysBusDevice *dev) | |
341 | { | |
342 | GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev); | |
343 | unsigned int i; | |
344 | int timer_regs; | |
345 | ||
346 | assert(unit->nr_timers > 0); | |
347 | assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); | |
348 | ||
349 | unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers); | |
350 | ||
351 | for (i = 0; i < unit->nr_timers; i++) { | |
352 | GPTimer *timer = &unit->timers[i]; | |
353 | ||
354 | timer->unit = unit; | |
355 | timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); | |
356 | timer->ptimer = ptimer_init(timer->bh); | |
357 | timer->id = i; | |
358 | ||
359 | /* One IRQ line for each timer */ | |
360 | sysbus_init_irq(dev, &timer->irq); | |
361 | ||
362 | ptimer_set_freq(timer->ptimer, unit->freq_hz); | |
363 | } | |
364 | ||
365 | timer_regs = cpu_register_io_memory(grlib_gptimer_read, | |
366 | grlib_gptimer_write, | |
367 | unit, DEVICE_NATIVE_ENDIAN); | |
368 | if (timer_regs < 0) { | |
369 | return -1; | |
370 | } | |
371 | ||
372 | sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers, | |
373 | timer_regs); | |
374 | return 0; | |
375 | } | |
376 | ||
377 | static SysBusDeviceInfo grlib_gptimer_info = { | |
378 | .init = grlib_gptimer_init, | |
379 | .qdev.name = "grlib,gptimer", | |
380 | .qdev.reset = grlib_gptimer_reset, | |
381 | .qdev.size = sizeof(GPTimerUnit), | |
382 | .qdev.props = (Property[]) { | |
383 | DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), | |
384 | DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), | |
385 | DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), | |
386 | DEFINE_PROP_END_OF_LIST() | |
387 | } | |
388 | }; | |
389 | ||
390 | static void grlib_gptimer_register(void) | |
391 | { | |
392 | sysbus_register_withprop(&grlib_gptimer_info); | |
393 | } | |
394 | ||
395 | device_init(grlib_gptimer_register) |