]>
Commit | Line | Data |
---|---|---|
f7c70325 PB |
1 | /* |
2 | * ARM11MPCore internal peripheral emulation. | |
3 | * | |
4 | * Copyright (c) 2006-2007 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
8e31bf38 | 7 | * This code is licensed under the GPL. |
f7c70325 PB |
8 | */ |
9 | ||
2a6ab1e3 PM |
10 | #include "sysbus.h" |
11 | #include "qemu-timer.h" | |
12 | ||
f7c70325 PB |
13 | /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines |
14 | (+ 32 internal). However my test chip only exposes/reports 32. | |
15 | More importantly Linux falls over if more than 32 are present! */ | |
16 | #define GIC_NIRQ 64 | |
2a6ab1e3 PM |
17 | |
18 | #define NCPU 4 | |
19 | ||
20 | static inline int | |
21 | gic_get_current_cpu(void) | |
22 | { | |
23 | return cpu_single_env->cpu_index; | |
24 | } | |
25 | ||
26 | #include "arm_gic.c" | |
27 | ||
28 | /* MPCore private memory region. */ | |
29 | ||
30 | typedef struct mpcore_priv_state { | |
31 | gic_state gic; | |
32 | uint32_t scu_control; | |
33 | int iomemtype; | |
34 | uint32_t old_timer_status[8]; | |
35 | uint32_t num_cpu; | |
36 | qemu_irq *timer_irq; | |
37 | MemoryRegion iomem; | |
38 | MemoryRegion container; | |
39 | DeviceState *mptimer; | |
40 | } mpcore_priv_state; | |
41 | ||
42 | /* Per-CPU private memory mapped IO. */ | |
43 | ||
44 | static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset, | |
45 | unsigned size) | |
46 | { | |
47 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
48 | int id; | |
49 | offset &= 0xff; | |
50 | /* SCU */ | |
51 | switch (offset) { | |
52 | case 0x00: /* Control. */ | |
53 | return s->scu_control; | |
54 | case 0x04: /* Configuration. */ | |
55 | id = ((1 << s->num_cpu) - 1) << 4; | |
56 | return id | (s->num_cpu - 1); | |
57 | case 0x08: /* CPU status. */ | |
58 | return 0; | |
59 | case 0x0c: /* Invalidate all. */ | |
60 | return 0; | |
61 | default: | |
62 | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); | |
63 | } | |
64 | } | |
65 | ||
66 | static void mpcore_scu_write(void *opaque, target_phys_addr_t offset, | |
67 | uint64_t value, unsigned size) | |
68 | { | |
69 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
70 | offset &= 0xff; | |
71 | /* SCU */ | |
72 | switch (offset) { | |
73 | case 0: /* Control register. */ | |
74 | s->scu_control = value & 1; | |
75 | break; | |
76 | case 0x0c: /* Invalidate all. */ | |
77 | /* This is a no-op as cache is not emulated. */ | |
78 | break; | |
79 | default: | |
80 | hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); | |
81 | } | |
82 | } | |
83 | ||
84 | static const MemoryRegionOps mpcore_scu_ops = { | |
85 | .read = mpcore_scu_read, | |
86 | .write = mpcore_scu_write, | |
87 | .endianness = DEVICE_NATIVE_ENDIAN, | |
88 | }; | |
89 | ||
90 | static void mpcore_timer_irq_handler(void *opaque, int irq, int level) | |
91 | { | |
92 | mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
93 | if (level && !s->old_timer_status[irq]) { | |
94 | gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1)); | |
95 | } | |
96 | s->old_timer_status[irq] = level; | |
97 | } | |
98 | ||
99 | static void mpcore_priv_map_setup(mpcore_priv_state *s) | |
100 | { | |
101 | int i; | |
102 | SysBusDevice *busdev = sysbus_from_qdev(s->mptimer); | |
103 | memory_region_init(&s->container, "mpcode-priv-container", 0x2000); | |
104 | memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100); | |
105 | memory_region_add_subregion(&s->container, 0, &s->iomem); | |
106 | /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs | |
107 | * at 0x200, 0x300... | |
108 | */ | |
109 | for (i = 0; i < (s->num_cpu + 1); i++) { | |
110 | target_phys_addr_t offset = 0x100 + (i * 0x100); | |
111 | memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]); | |
112 | } | |
113 | /* Add the regions for timer and watchdog for "current CPU" and | |
114 | * for each specific CPU. | |
115 | */ | |
116 | s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler, | |
117 | s, (s->num_cpu + 1) * 2); | |
118 | for (i = 0; i < (s->num_cpu + 1) * 2; i++) { | |
119 | /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */ | |
120 | target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20; | |
121 | memory_region_add_subregion(&s->container, offset, | |
122 | sysbus_mmio_get_region(busdev, i)); | |
123 | } | |
124 | memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); | |
125 | /* Wire up the interrupt from each watchdog and timer. */ | |
126 | for (i = 0; i < s->num_cpu * 2; i++) { | |
127 | sysbus_connect_irq(busdev, i, s->timer_irq[i]); | |
128 | } | |
129 | } | |
130 | ||
131 | static int mpcore_priv_init(SysBusDevice *dev) | |
132 | { | |
133 | mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); | |
134 | ||
135 | gic_init(&s->gic, s->num_cpu); | |
136 | s->mptimer = qdev_create(NULL, "arm_mptimer"); | |
137 | qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); | |
138 | qdev_init_nofail(s->mptimer); | |
139 | mpcore_priv_map_setup(s); | |
140 | sysbus_init_mmio(dev, &s->container); | |
141 | return 0; | |
142 | } | |
f7c70325 PB |
143 | |
144 | /* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ | |
145 | controllers. The output of these, plus some of the raw input lines | |
146 | are fed into a single SMP-aware interrupt controller on the CPU. */ | |
147 | typedef struct { | |
148 | SysBusDevice busdev; | |
149 | SysBusDevice *priv; | |
150 | qemu_irq cpuic[32]; | |
151 | qemu_irq rvic[4][64]; | |
152 | uint32_t num_cpu; | |
153 | } mpcore_rirq_state; | |
154 | ||
155 | /* Map baseboard IRQs onto CPU IRQ lines. */ | |
156 | static const int mpcore_irq_map[32] = { | |
157 | -1, -1, -1, -1, 1, 2, -1, -1, | |
158 | -1, -1, 6, -1, 4, 5, -1, -1, | |
159 | -1, 14, 15, 0, 7, 8, -1, -1, | |
160 | -1, -1, -1, -1, 9, 3, -1, -1, | |
161 | }; | |
162 | ||
163 | static void mpcore_rirq_set_irq(void *opaque, int irq, int level) | |
164 | { | |
165 | mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; | |
166 | int i; | |
167 | ||
168 | for (i = 0; i < 4; i++) { | |
169 | qemu_set_irq(s->rvic[i][irq], level); | |
170 | } | |
171 | if (irq < 32) { | |
172 | irq = mpcore_irq_map[irq]; | |
173 | if (irq >= 0) { | |
174 | qemu_set_irq(s->cpuic[irq], level); | |
175 | } | |
176 | } | |
177 | } | |
178 | ||
f7c70325 PB |
179 | static int realview_mpcore_init(SysBusDevice *dev) |
180 | { | |
181 | mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev); | |
182 | DeviceState *gic; | |
183 | DeviceState *priv; | |
184 | int n; | |
185 | int i; | |
186 | ||
187 | priv = qdev_create(NULL, "arm11mpcore_priv"); | |
188 | qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); | |
189 | qdev_init_nofail(priv); | |
190 | s->priv = sysbus_from_qdev(priv); | |
191 | sysbus_pass_irq(dev, s->priv); | |
192 | for (i = 0; i < 32; i++) { | |
193 | s->cpuic[i] = qdev_get_gpio_in(priv, i); | |
194 | } | |
195 | /* ??? IRQ routing is hardcoded to "normal" mode. */ | |
196 | for (n = 0; n < 4; n++) { | |
197 | gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000, | |
198 | s->cpuic[10 + n]); | |
199 | for (i = 0; i < 64; i++) { | |
200 | s->rvic[n][i] = qdev_get_gpio_in(gic, i); | |
201 | } | |
202 | } | |
203 | qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64); | |
750ecd44 | 204 | sysbus_init_mmio(dev, sysbus_mmio_get_region(s->priv, 0)); |
f7c70325 PB |
205 | return 0; |
206 | } | |
207 | ||
208 | static SysBusDeviceInfo mpcore_rirq_info = { | |
209 | .init = realview_mpcore_init, | |
210 | .qdev.name = "realview_mpcore", | |
211 | .qdev.size = sizeof(mpcore_rirq_state), | |
212 | .qdev.props = (Property[]) { | |
213 | DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), | |
214 | DEFINE_PROP_END_OF_LIST(), | |
215 | } | |
216 | }; | |
217 | ||
218 | static SysBusDeviceInfo mpcore_priv_info = { | |
219 | .init = mpcore_priv_init, | |
220 | .qdev.name = "arm11mpcore_priv", | |
221 | .qdev.size = sizeof(mpcore_priv_state), | |
222 | .qdev.props = (Property[]) { | |
223 | DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), | |
224 | DEFINE_PROP_END_OF_LIST(), | |
225 | } | |
226 | }; | |
227 | ||
228 | static void arm11mpcore_register_devices(void) | |
229 | { | |
230 | sysbus_register_withprop(&mpcore_rirq_info); | |
231 | sysbus_register_withprop(&mpcore_priv_info); | |
232 | } | |
233 | ||
234 | device_init(arm11mpcore_register_devices) |