]> Git Repo - qemu.git/blob - hw/a9mpcore.c
arm: make the number of GIC interrupts configurable
[qemu.git] / hw / a9mpcore.c
1 /*
2  * Cortex-A9MPCore internal peripheral emulation.
3  *
4  * Copyright (c) 2009 CodeSourcery.
5  * Copyright (c) 2011 Linaro Limited.
6  * Written by Paul Brook, Peter Maydell.
7  *
8  * This code is licensed under the GPL.
9  */
10
11 #include "sysbus.h"
12
13 /* Configuration for arm_gic.c:
14  * max number of CPUs, how to ID current CPU
15  */
16 #define NCPU 4
17
18 static inline int
19 gic_get_current_cpu(void)
20 {
21   return cpu_single_env->cpu_index;
22 }
23
24 #include "arm_gic.c"
25
26 /* A9MP private memory region.  */
27
28 typedef struct a9mp_priv_state {
29     gic_state gic;
30     uint32_t scu_control;
31     uint32_t scu_status;
32     uint32_t old_timer_status[8];
33     uint32_t num_cpu;
34     qemu_irq *timer_irq;
35     MemoryRegion scu_iomem;
36     MemoryRegion ptimer_iomem;
37     MemoryRegion container;
38     DeviceState *mptimer;
39     uint32_t num_irq;
40 } a9mp_priv_state;
41
42 static uint64_t a9_scu_read(void *opaque, target_phys_addr_t offset,
43                             unsigned size)
44 {
45     a9mp_priv_state *s = (a9mp_priv_state *)opaque;
46     switch (offset) {
47     case 0x00: /* Control */
48         return s->scu_control;
49     case 0x04: /* Configuration */
50         return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1);
51     case 0x08: /* CPU Power Status */
52         return s->scu_status;
53     case 0x09: /* CPU status.  */
54         return s->scu_status >> 8;
55     case 0x0a: /* CPU status.  */
56         return s->scu_status >> 16;
57     case 0x0b: /* CPU status.  */
58         return s->scu_status >> 24;
59     case 0x0c: /* Invalidate All Registers In Secure State */
60         return 0;
61     case 0x40: /* Filtering Start Address Register */
62     case 0x44: /* Filtering End Address Register */
63         /* RAZ/WI, like an implementation with only one AXI master */
64         return 0;
65     case 0x50: /* SCU Access Control Register */
66     case 0x54: /* SCU Non-secure Access Control Register */
67         /* unimplemented, fall through */
68     default:
69         return 0;
70     }
71 }
72
73 static void a9_scu_write(void *opaque, target_phys_addr_t offset,
74                          uint64_t value, unsigned size)
75 {
76     a9mp_priv_state *s = (a9mp_priv_state *)opaque;
77     uint32_t mask;
78     uint32_t shift;
79     switch (size) {
80     case 1:
81         mask = 0xff;
82         break;
83     case 2:
84         mask = 0xffff;
85         break;
86     case 4:
87         mask = 0xffffffff;
88         break;
89     default:
90         fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n",
91                 size, offset);
92         return;
93     }
94
95     switch (offset) {
96     case 0x00: /* Control */
97         s->scu_control = value & 1;
98         break;
99     case 0x4: /* Configuration: RO */
100         break;
101     case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */
102         shift = (offset - 0x8) * 8;
103         s->scu_status &= ~(mask << shift);
104         s->scu_status |= ((value & mask) << shift);
105         break;
106     case 0x0c: /* Invalidate All Registers In Secure State */
107         /* no-op as we do not implement caches */
108         break;
109     case 0x40: /* Filtering Start Address Register */
110     case 0x44: /* Filtering End Address Register */
111         /* RAZ/WI, like an implementation with only one AXI master */
112         break;
113     case 0x50: /* SCU Access Control Register */
114     case 0x54: /* SCU Non-secure Access Control Register */
115         /* unimplemented, fall through */
116     default:
117         break;
118     }
119 }
120
121 static const MemoryRegionOps a9_scu_ops = {
122     .read = a9_scu_read,
123     .write = a9_scu_write,
124     .endianness = DEVICE_NATIVE_ENDIAN,
125 };
126
127 static void a9mpcore_timer_irq_handler(void *opaque, int irq, int level)
128 {
129     a9mp_priv_state *s = (a9mp_priv_state *)opaque;
130     if (level && !s->old_timer_status[irq]) {
131         gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1));
132     }
133     s->old_timer_status[irq] = level;
134 }
135
136 static void a9mp_priv_reset(DeviceState *dev)
137 {
138     a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, sysbus_from_qdev(dev));
139     int i;
140     s->scu_control = 0;
141     for (i = 0; i < ARRAY_SIZE(s->old_timer_status); i++) {
142         s->old_timer_status[i] = 0;
143     }
144 }
145
146 static int a9mp_priv_init(SysBusDevice *dev)
147 {
148     a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, dev);
149     SysBusDevice *busdev;
150     int i;
151
152     if (s->num_cpu > NCPU) {
153         hw_error("a9mp_priv_init: num-cpu may not be more than %d\n", NCPU);
154     }
155
156     gic_init(&s->gic, s->num_cpu, s->num_irq);
157
158     s->mptimer = qdev_create(NULL, "arm_mptimer");
159     qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
160     qdev_init_nofail(s->mptimer);
161     busdev = sysbus_from_qdev(s->mptimer);
162
163     /* Memory map (addresses are offsets from PERIPHBASE):
164      *  0x0000-0x00ff -- Snoop Control Unit
165      *  0x0100-0x01ff -- GIC CPU interface
166      *  0x0200-0x02ff -- Global Timer
167      *  0x0300-0x05ff -- nothing
168      *  0x0600-0x06ff -- private timers and watchdogs
169      *  0x0700-0x0fff -- nothing
170      *  0x1000-0x1fff -- GIC Distributor
171      *
172      * We should implement the global timer but don't currently do so.
173      */
174     memory_region_init(&s->container, "a9mp-priv-container", 0x2000);
175     memory_region_init_io(&s->scu_iomem, &a9_scu_ops, s, "a9mp-scu", 0x100);
176     memory_region_add_subregion(&s->container, 0, &s->scu_iomem);
177     /* GIC CPU interface */
178     memory_region_add_subregion(&s->container, 0x100, &s->gic.cpuiomem[0]);
179     /* Note that the A9 exposes only the "timer/watchdog for this core"
180      * memory region, not the "timer/watchdog for core X" ones 11MPcore has.
181      */
182     memory_region_add_subregion(&s->container, 0x600,
183                                 sysbus_mmio_get_region(busdev, 0));
184     memory_region_add_subregion(&s->container, 0x620,
185                                 sysbus_mmio_get_region(busdev, 1));
186     memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
187
188     sysbus_init_mmio(dev, &s->container);
189
190     /* Wire up the interrupt from each watchdog and timer. */
191     s->timer_irq = qemu_allocate_irqs(a9mpcore_timer_irq_handler,
192                                       s, (s->num_cpu + 1) * 2);
193     for (i = 0; i < s->num_cpu * 2; i++) {
194         sysbus_connect_irq(busdev, i, s->timer_irq[i]);
195     }
196     return 0;
197 }
198
199 static const VMStateDescription vmstate_a9mp_priv = {
200     .name = "a9mpcore_priv",
201     .version_id = 2,
202     .minimum_version_id = 1,
203     .fields = (VMStateField[]) {
204         VMSTATE_UINT32(scu_control, a9mp_priv_state),
205         VMSTATE_UINT32_ARRAY(old_timer_status, a9mp_priv_state, 8),
206         VMSTATE_UINT32_V(scu_status, a9mp_priv_state, 2),
207         VMSTATE_END_OF_LIST()
208     }
209 };
210
211 static SysBusDeviceInfo a9mp_priv_info = {
212     .init = a9mp_priv_init,
213     .qdev.name  = "a9mpcore_priv",
214     .qdev.size  = sizeof(a9mp_priv_state),
215     .qdev.vmsd = &vmstate_a9mp_priv,
216     .qdev.reset = a9mp_priv_reset,
217     .qdev.props = (Property[]) {
218         DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state, num_cpu, 1),
219         /* The Cortex-A9MP may have anything from 0 to 224 external interrupt
220          * IRQ lines (with another 32 internal). We default to 64+32, which
221          * is the number provided by the Cortex-A9MP test chip in the
222          * Realview PBX-A9 and Versatile Express A9 development boards.
223          * Other boards may differ and should set this property appropriately.
224          */
225         DEFINE_PROP_UINT32("num-irq", a9mp_priv_state, num_irq, 96),
226         DEFINE_PROP_END_OF_LIST(),
227     }
228 };
229
230 static void a9mp_register_devices(void)
231 {
232     sysbus_register_withprop(&a9mp_priv_info);
233 }
234
235 device_init(a9mp_register_devices)
This page took 0.037405 seconds and 4 git commands to generate.