]>
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: | |
b4548fcc | 168 | trace_grlib_gptimer_readl(-1, addr, unit->scaler); |
0f3a4a01 FC |
169 | return unit->scaler; |
170 | ||
171 | case SCALER_RELOAD_OFFSET: | |
b4548fcc | 172 | trace_grlib_gptimer_readl(-1, addr, unit->reload); |
0f3a4a01 FC |
173 | return unit->reload; |
174 | ||
175 | case CONFIG_OFFSET: | |
b4548fcc | 176 | trace_grlib_gptimer_readl(-1, addr, unit->config); |
0f3a4a01 FC |
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); | |
b4548fcc | 192 | trace_grlib_gptimer_readl(id, addr, value); |
0f3a4a01 FC |
193 | return value; |
194 | ||
195 | case COUNTER_RELOAD_OFFSET: | |
196 | value = unit->timers[id].reload; | |
b4548fcc | 197 | trace_grlib_gptimer_readl(id, addr, value); |
0f3a4a01 FC |
198 | return value; |
199 | ||
200 | case CONFIG_OFFSET: | |
b4548fcc | 201 | trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); |
0f3a4a01 FC |
202 | return unit->timers[id].config; |
203 | ||
204 | default: | |
205 | break; | |
206 | } | |
207 | ||
208 | } | |
209 | ||
b4548fcc | 210 | trace_grlib_gptimer_readl(-1, addr, 0); |
0f3a4a01 FC |
211 | return 0; |
212 | } | |
213 | ||
214 | static void | |
215 | grlib_gptimer_writel(void *opaque, target_phys_addr_t addr, uint32_t value) | |
216 | { | |
217 | GPTimerUnit *unit = opaque; | |
218 | target_phys_addr_t timer_addr; | |
219 | int id; | |
220 | ||
221 | addr &= 0xff; | |
222 | ||
223 | /* Unit registers */ | |
224 | switch (addr) { | |
225 | case SCALER_OFFSET: | |
226 | value &= 0xFFFF; /* clean up the value */ | |
227 | unit->scaler = value; | |
b4548fcc | 228 | trace_grlib_gptimer_writel(-1, addr, unit->scaler); |
0f3a4a01 FC |
229 | return; |
230 | ||
231 | case SCALER_RELOAD_OFFSET: | |
232 | value &= 0xFFFF; /* clean up the value */ | |
233 | unit->reload = value; | |
b4548fcc | 234 | trace_grlib_gptimer_writel(-1, addr, unit->reload); |
0f3a4a01 FC |
235 | grlib_gptimer_set_scaler(unit, value); |
236 | return; | |
237 | ||
238 | case CONFIG_OFFSET: | |
239 | /* Read Only (disable timer freeze not supported) */ | |
b4548fcc | 240 | trace_grlib_gptimer_writel(-1, addr, 0); |
0f3a4a01 FC |
241 | return; |
242 | ||
243 | default: | |
244 | break; | |
245 | } | |
246 | ||
247 | timer_addr = (addr % TIMER_BASE); | |
248 | id = (addr - TIMER_BASE) / TIMER_BASE; | |
249 | ||
250 | if (id >= 0 && id < unit->nr_timers) { | |
251 | ||
252 | /* GPTimer registers */ | |
253 | switch (timer_addr) { | |
254 | case COUNTER_OFFSET: | |
b4548fcc | 255 | trace_grlib_gptimer_writel(id, addr, value); |
0f3a4a01 FC |
256 | unit->timers[id].counter = value; |
257 | grlib_gptimer_enable(&unit->timers[id]); | |
258 | return; | |
259 | ||
260 | case COUNTER_RELOAD_OFFSET: | |
b4548fcc | 261 | trace_grlib_gptimer_writel(id, addr, value); |
0f3a4a01 FC |
262 | unit->timers[id].reload = value; |
263 | return; | |
264 | ||
265 | case CONFIG_OFFSET: | |
b4548fcc | 266 | trace_grlib_gptimer_writel(id, addr, value); |
0f3a4a01 FC |
267 | |
268 | if (value & GPTIMER_INT_PENDING) { | |
269 | /* clear pending bit */ | |
270 | value &= ~GPTIMER_INT_PENDING; | |
271 | } else { | |
272 | /* keep pending bit */ | |
273 | value |= unit->timers[id].config & GPTIMER_INT_PENDING; | |
274 | } | |
275 | ||
276 | unit->timers[id].config = value; | |
277 | ||
278 | /* gptimer_restart calls gptimer_enable, so if "enable" and "load" | |
279 | bits are present, we just have to call restart. */ | |
280 | ||
281 | if (value & GPTIMER_LOAD) { | |
282 | grlib_gptimer_restart(&unit->timers[id]); | |
283 | } else if (value & GPTIMER_ENABLE) { | |
284 | grlib_gptimer_enable(&unit->timers[id]); | |
285 | } | |
286 | ||
287 | /* These fields must always be read as 0 */ | |
288 | value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); | |
289 | ||
290 | unit->timers[id].config = value; | |
291 | return; | |
292 | ||
293 | default: | |
294 | break; | |
295 | } | |
296 | ||
297 | } | |
298 | ||
b4548fcc | 299 | trace_grlib_gptimer_writel(-1, addr, value); |
0f3a4a01 FC |
300 | } |
301 | ||
302 | static CPUReadMemoryFunc * const grlib_gptimer_read[] = { | |
303 | NULL, NULL, grlib_gptimer_readl, | |
304 | }; | |
305 | ||
306 | static CPUWriteMemoryFunc * const grlib_gptimer_write[] = { | |
307 | NULL, NULL, grlib_gptimer_writel, | |
308 | }; | |
309 | ||
310 | static void grlib_gptimer_reset(DeviceState *d) | |
311 | { | |
312 | GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev); | |
313 | int i = 0; | |
314 | ||
315 | assert(unit != NULL); | |
316 | ||
317 | unit->scaler = 0; | |
318 | unit->reload = 0; | |
319 | unit->config = 0; | |
320 | ||
321 | unit->config = unit->nr_timers; | |
322 | unit->config |= unit->irq_line << 3; | |
323 | unit->config |= 1 << 8; /* separate interrupt */ | |
324 | unit->config |= 1 << 9; /* Disable timer freeze */ | |
325 | ||
326 | ||
327 | for (i = 0; i < unit->nr_timers; i++) { | |
328 | GPTimer *timer = &unit->timers[i]; | |
329 | ||
330 | timer->counter = 0; | |
331 | timer->reload = 0; | |
332 | timer->config = 0; | |
333 | ptimer_stop(timer->ptimer); | |
334 | ptimer_set_count(timer->ptimer, 0); | |
335 | ptimer_set_freq(timer->ptimer, unit->freq_hz); | |
336 | } | |
337 | } | |
338 | ||
339 | static int grlib_gptimer_init(SysBusDevice *dev) | |
340 | { | |
341 | GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev); | |
342 | unsigned int i; | |
343 | int timer_regs; | |
344 | ||
345 | assert(unit->nr_timers > 0); | |
346 | assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); | |
347 | ||
7267c094 | 348 | unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); |
0f3a4a01 FC |
349 | |
350 | for (i = 0; i < unit->nr_timers; i++) { | |
351 | GPTimer *timer = &unit->timers[i]; | |
352 | ||
353 | timer->unit = unit; | |
354 | timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); | |
355 | timer->ptimer = ptimer_init(timer->bh); | |
356 | timer->id = i; | |
357 | ||
358 | /* One IRQ line for each timer */ | |
359 | sysbus_init_irq(dev, &timer->irq); | |
360 | ||
361 | ptimer_set_freq(timer->ptimer, unit->freq_hz); | |
362 | } | |
363 | ||
364 | timer_regs = cpu_register_io_memory(grlib_gptimer_read, | |
365 | grlib_gptimer_write, | |
366 | unit, DEVICE_NATIVE_ENDIAN); | |
367 | if (timer_regs < 0) { | |
368 | return -1; | |
369 | } | |
370 | ||
371 | sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers, | |
372 | timer_regs); | |
373 | return 0; | |
374 | } | |
375 | ||
376 | static SysBusDeviceInfo grlib_gptimer_info = { | |
377 | .init = grlib_gptimer_init, | |
378 | .qdev.name = "grlib,gptimer", | |
379 | .qdev.reset = grlib_gptimer_reset, | |
380 | .qdev.size = sizeof(GPTimerUnit), | |
381 | .qdev.props = (Property[]) { | |
382 | DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), | |
383 | DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), | |
384 | DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), | |
385 | DEFINE_PROP_END_OF_LIST() | |
386 | } | |
387 | }; | |
388 | ||
389 | static void grlib_gptimer_register(void) | |
390 | { | |
391 | sysbus_register_withprop(&grlib_gptimer_info); | |
392 | } | |
393 | ||
394 | device_init(grlib_gptimer_register) |