|
|
1.1 root 1: /*
2: * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
3: *
4: * Copyright (c) 2006-2007 CodeSourcery.
5: * Copyright (c) 2011 Linaro Limited
6: * Written by Paul Brook, Peter Maydell
7: *
8: * This program is free software; you can redistribute it and/or
9: * modify it under the terms of the GNU General Public License
10: * as published by the Free Software Foundation; either version
11: * 2 of the License, or (at your option) any later version.
12: *
13: * This program is distributed in the hope that it will be useful,
14: * but WITHOUT ANY WARRANTY; without even the implied warranty of
15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: * GNU General Public License for more details.
17: *
18: * You should have received a copy of the GNU General Public License along
19: * with this program; if not, see <http://www.gnu.org/licenses/>.
20: */
21:
22: #include "sysbus.h"
23: #include "qemu-timer.h"
24:
25: /* This device implements the per-cpu private timer and watchdog block
26: * which is used in both the ARM11MPCore and Cortex-A9MP.
27: */
28:
29: #define MAX_CPUS 4
30:
31: /* State of a single timer or watchdog block */
32: typedef struct {
33: uint32_t count;
34: uint32_t load;
35: uint32_t control;
36: uint32_t status;
37: int64_t tick;
38: QEMUTimer *timer;
39: qemu_irq irq;
40: MemoryRegion iomem;
41: } timerblock;
42:
43: typedef struct {
44: SysBusDevice busdev;
45: uint32_t num_cpu;
46: timerblock timerblock[MAX_CPUS * 2];
47: MemoryRegion iomem[2];
48: } arm_mptimer_state;
49:
50: static inline int get_current_cpu(arm_mptimer_state *s)
51: {
52: if (cpu_single_env->cpu_index >= s->num_cpu) {
53: hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
54: s->num_cpu, cpu_single_env->cpu_index);
55: }
56: return cpu_single_env->cpu_index;
57: }
58:
59: static inline void timerblock_update_irq(timerblock *tb)
60: {
61: qemu_set_irq(tb->irq, tb->status);
62: }
63:
64: /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
65: static inline uint32_t timerblock_scale(timerblock *tb)
66: {
67: return (((tb->control >> 8) & 0xff) + 1) * 10;
68: }
69:
70: static void timerblock_reload(timerblock *tb, int restart)
71: {
72: if (tb->count == 0) {
73: return;
74: }
75: if (restart) {
76: tb->tick = qemu_get_clock_ns(vm_clock);
77: }
78: tb->tick += (int64_t)tb->count * timerblock_scale(tb);
79: qemu_mod_timer(tb->timer, tb->tick);
80: }
81:
82: static void timerblock_tick(void *opaque)
83: {
84: timerblock *tb = (timerblock *)opaque;
85: tb->status = 1;
86: if (tb->control & 2) {
87: tb->count = tb->load;
88: timerblock_reload(tb, 0);
89: } else {
90: tb->count = 0;
91: }
92: timerblock_update_irq(tb);
93: }
94:
95: static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr,
96: unsigned size)
97: {
98: timerblock *tb = (timerblock *)opaque;
99: int64_t val;
100: switch (addr) {
101: case 0: /* Load */
102: return tb->load;
103: case 4: /* Counter. */
104: if (((tb->control & 1) == 0) || (tb->count == 0)) {
105: return 0;
106: }
107: /* Slow and ugly, but hopefully won't happen too often. */
108: val = tb->tick - qemu_get_clock_ns(vm_clock);
109: val /= timerblock_scale(tb);
110: if (val < 0) {
111: val = 0;
112: }
113: return val;
114: case 8: /* Control. */
115: return tb->control;
116: case 12: /* Interrupt status. */
117: return tb->status;
118: default:
119: return 0;
120: }
121: }
122:
123: static void timerblock_write(void *opaque, target_phys_addr_t addr,
124: uint64_t value, unsigned size)
125: {
126: timerblock *tb = (timerblock *)opaque;
127: int64_t old;
128: switch (addr) {
129: case 0: /* Load */
130: tb->load = value;
131: /* Fall through. */
132: case 4: /* Counter. */
133: if ((tb->control & 1) && tb->count) {
134: /* Cancel the previous timer. */
135: qemu_del_timer(tb->timer);
136: }
137: tb->count = value;
138: if (tb->control & 1) {
139: timerblock_reload(tb, 1);
140: }
141: break;
142: case 8: /* Control. */
143: old = tb->control;
144: tb->control = value;
145: if (((old & 1) == 0) && (value & 1)) {
146: if (tb->count == 0 && (tb->control & 2)) {
147: tb->count = tb->load;
148: }
149: timerblock_reload(tb, 1);
150: }
151: break;
152: case 12: /* Interrupt status. */
153: tb->status &= ~value;
154: timerblock_update_irq(tb);
155: break;
156: }
157: }
158:
159: /* Wrapper functions to implement the "read timer/watchdog for
160: * the current CPU" memory regions.
161: */
162: static uint64_t arm_thistimer_read(void *opaque, target_phys_addr_t addr,
163: unsigned size)
164: {
165: arm_mptimer_state *s = (arm_mptimer_state *)opaque;
166: int id = get_current_cpu(s);
167: return timerblock_read(&s->timerblock[id * 2], addr, size);
168: }
169:
170: static void arm_thistimer_write(void *opaque, target_phys_addr_t addr,
171: uint64_t value, unsigned size)
172: {
173: arm_mptimer_state *s = (arm_mptimer_state *)opaque;
174: int id = get_current_cpu(s);
175: timerblock_write(&s->timerblock[id * 2], addr, value, size);
176: }
177:
178: static uint64_t arm_thiswdog_read(void *opaque, target_phys_addr_t addr,
179: unsigned size)
180: {
181: arm_mptimer_state *s = (arm_mptimer_state *)opaque;
182: int id = get_current_cpu(s);
183: return timerblock_read(&s->timerblock[id * 2 + 1], addr, size);
184: }
185:
186: static void arm_thiswdog_write(void *opaque, target_phys_addr_t addr,
187: uint64_t value, unsigned size)
188: {
189: arm_mptimer_state *s = (arm_mptimer_state *)opaque;
190: int id = get_current_cpu(s);
191: timerblock_write(&s->timerblock[id * 2 + 1], addr, value, size);
192: }
193:
194: static const MemoryRegionOps arm_thistimer_ops = {
195: .read = arm_thistimer_read,
196: .write = arm_thistimer_write,
197: .valid = {
198: .min_access_size = 4,
199: .max_access_size = 4,
200: },
201: .endianness = DEVICE_NATIVE_ENDIAN,
202: };
203:
204: static const MemoryRegionOps arm_thiswdog_ops = {
205: .read = arm_thiswdog_read,
206: .write = arm_thiswdog_write,
207: .valid = {
208: .min_access_size = 4,
209: .max_access_size = 4,
210: },
211: .endianness = DEVICE_NATIVE_ENDIAN,
212: };
213:
214: static const MemoryRegionOps timerblock_ops = {
215: .read = timerblock_read,
216: .write = timerblock_write,
217: .valid = {
218: .min_access_size = 4,
219: .max_access_size = 4,
220: },
221: .endianness = DEVICE_NATIVE_ENDIAN,
222: };
223:
224: static void timerblock_reset(timerblock *tb)
225: {
226: tb->count = 0;
227: tb->load = 0;
228: tb->control = 0;
229: tb->status = 0;
230: tb->tick = 0;
231: if (tb->timer) {
232: qemu_del_timer(tb->timer);
233: }
234: }
235:
236: static void arm_mptimer_reset(DeviceState *dev)
237: {
238: arm_mptimer_state *s =
239: FROM_SYSBUS(arm_mptimer_state, sysbus_from_qdev(dev));
240: int i;
241: /* We reset every timer in the array, not just the ones we're using,
242: * because vmsave will look at every array element.
243: */
244: for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
245: timerblock_reset(&s->timerblock[i]);
246: }
247: }
248:
249: static int arm_mptimer_init(SysBusDevice *dev)
250: {
251: arm_mptimer_state *s = FROM_SYSBUS(arm_mptimer_state, dev);
252: int i;
253: if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) {
254: hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS);
255: }
256: /* We implement one timer and one watchdog block per CPU, and
257: * expose multiple MMIO regions:
258: * * region 0 is "timer for this core"
259: * * region 1 is "watchdog for this core"
260: * * region 2 is "timer for core 0"
261: * * region 3 is "watchdog for core 0"
262: * * region 4 is "timer for core 1"
263: * * region 5 is "watchdog for core 1"
264: * and so on.
265: * The outgoing interrupt lines are
266: * * timer for core 0
267: * * watchdog for core 0
268: * * timer for core 1
269: * * watchdog for core 1
270: * and so on.
271: */
272: memory_region_init_io(&s->iomem[0], &arm_thistimer_ops, s,
273: "arm_mptimer_timer", 0x20);
274: sysbus_init_mmio(dev, &s->iomem[0]);
275: memory_region_init_io(&s->iomem[1], &arm_thiswdog_ops, s,
276: "arm_mptimer_wdog", 0x20);
277: sysbus_init_mmio(dev, &s->iomem[1]);
278: for (i = 0; i < (s->num_cpu * 2); i++) {
279: timerblock *tb = &s->timerblock[i];
280: tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb);
281: sysbus_init_irq(dev, &tb->irq);
282: memory_region_init_io(&tb->iomem, &timerblock_ops, tb,
283: "arm_mptimer_timerblock", 0x20);
284: sysbus_init_mmio(dev, &tb->iomem);
285: }
286:
287: return 0;
288: }
289:
290: static const VMStateDescription vmstate_timerblock = {
291: .name = "arm_mptimer_timerblock",
292: .version_id = 1,
293: .minimum_version_id = 1,
294: .fields = (VMStateField[]) {
295: VMSTATE_UINT32(count, timerblock),
296: VMSTATE_UINT32(load, timerblock),
297: VMSTATE_UINT32(control, timerblock),
298: VMSTATE_UINT32(status, timerblock),
299: VMSTATE_INT64(tick, timerblock),
300: VMSTATE_END_OF_LIST()
301: }
302: };
303:
304: static const VMStateDescription vmstate_arm_mptimer = {
305: .name = "arm_mptimer",
306: .version_id = 1,
307: .minimum_version_id = 1,
308: .fields = (VMStateField[]) {
309: VMSTATE_STRUCT_ARRAY(timerblock, arm_mptimer_state, (MAX_CPUS * 2),
310: 1, vmstate_timerblock, timerblock),
311: VMSTATE_END_OF_LIST()
312: }
313: };
314:
315: static Property arm_mptimer_properties[] = {
316: DEFINE_PROP_UINT32("num-cpu", arm_mptimer_state, num_cpu, 0),
317: DEFINE_PROP_END_OF_LIST()
318: };
319:
320: static void arm_mptimer_class_init(ObjectClass *klass, void *data)
321: {
322: DeviceClass *dc = DEVICE_CLASS(klass);
323: SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
324:
325: sbc->init = arm_mptimer_init;
326: dc->vmsd = &vmstate_arm_mptimer;
327: dc->reset = arm_mptimer_reset;
328: dc->no_user = 1;
329: dc->props = arm_mptimer_properties;
330: }
331:
332: static TypeInfo arm_mptimer_info = {
333: .name = "arm_mptimer",
334: .parent = TYPE_SYS_BUS_DEVICE,
335: .instance_size = sizeof(arm_mptimer_state),
336: .class_init = arm_mptimer_class_init,
337: };
338:
339: static void arm_mptimer_register_types(void)
340: {
341: type_register_static(&arm_mptimer_info);
342: }
343:
344: type_init(arm_mptimer_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.