2 * Cortex-A9MPCore internal peripheral emulation.
4 * Copyright (c) 2009 CodeSourcery.
5 * Copyright (c) 2011 Linaro Limited.
6 * Written by Paul Brook, Peter Maydell.
8 * This code is licensed under the GPL.
13 /* Configuration for arm_gic.c:
14 * number of external IRQ lines, max number of CPUs, how to ID current CPU
20 gic_get_current_cpu(void)
22 return cpu_single_env
->cpu_index
;
27 /* A9MP private memory region. */
29 typedef struct a9mp_priv_state
{
32 uint32_t old_timer_status
[8];
35 MemoryRegion scu_iomem
;
36 MemoryRegion ptimer_iomem
;
37 MemoryRegion container
;
41 static uint64_t a9_scu_read(void *opaque
, target_phys_addr_t offset
,
44 a9mp_priv_state
*s
= (a9mp_priv_state
*)opaque
;
46 case 0x00: /* Control */
47 return s
->scu_control
;
48 case 0x04: /* Configuration */
49 return (((1 << s
->num_cpu
) - 1) << 4) | (s
->num_cpu
- 1);
50 case 0x08: /* CPU Power Status */
52 case 0x0c: /* Invalidate All Registers In Secure State */
54 case 0x40: /* Filtering Start Address Register */
55 case 0x44: /* Filtering End Address Register */
56 /* RAZ/WI, like an implementation with only one AXI master */
58 case 0x50: /* SCU Access Control Register */
59 case 0x54: /* SCU Non-secure Access Control Register */
60 /* unimplemented, fall through */
66 static void a9_scu_write(void *opaque
, target_phys_addr_t offset
,
67 uint64_t value
, unsigned size
)
69 a9mp_priv_state
*s
= (a9mp_priv_state
*)opaque
;
71 case 0x00: /* Control */
72 s
->scu_control
= value
& 1;
74 case 0x4: /* Configuration: RO */
76 case 0x0c: /* Invalidate All Registers In Secure State */
77 /* no-op as we do not implement caches */
79 case 0x40: /* Filtering Start Address Register */
80 case 0x44: /* Filtering End Address Register */
81 /* RAZ/WI, like an implementation with only one AXI master */
83 case 0x8: /* CPU Power Status */
84 case 0x50: /* SCU Access Control Register */
85 case 0x54: /* SCU Non-secure Access Control Register */
86 /* unimplemented, fall through */
92 static const MemoryRegionOps a9_scu_ops
= {
94 .write
= a9_scu_write
,
95 .endianness
= DEVICE_NATIVE_ENDIAN
,
98 static void a9mpcore_timer_irq_handler(void *opaque
, int irq
, int level
)
100 a9mp_priv_state
*s
= (a9mp_priv_state
*)opaque
;
101 if (level
&& !s
->old_timer_status
[irq
]) {
102 gic_set_pending_private(&s
->gic
, irq
>> 1, 29 + (irq
& 1));
104 s
->old_timer_status
[irq
] = level
;
107 static void a9mp_priv_reset(DeviceState
*dev
)
109 a9mp_priv_state
*s
= FROM_SYSBUSGIC(a9mp_priv_state
, sysbus_from_qdev(dev
));
112 for (i
= 0; i
< ARRAY_SIZE(s
->old_timer_status
); i
++) {
113 s
->old_timer_status
[i
] = 0;
117 static int a9mp_priv_init(SysBusDevice
*dev
)
119 a9mp_priv_state
*s
= FROM_SYSBUSGIC(a9mp_priv_state
, dev
);
120 SysBusDevice
*busdev
;
123 if (s
->num_cpu
> NCPU
) {
124 hw_error("a9mp_priv_init: num-cpu may not be more than %d\n", NCPU
);
127 gic_init(&s
->gic
, s
->num_cpu
);
129 s
->mptimer
= qdev_create(NULL
, "arm_mptimer");
130 qdev_prop_set_uint32(s
->mptimer
, "num-cpu", s
->num_cpu
);
131 qdev_init_nofail(s
->mptimer
);
132 busdev
= sysbus_from_qdev(s
->mptimer
);
134 /* Memory map (addresses are offsets from PERIPHBASE):
135 * 0x0000-0x00ff -- Snoop Control Unit
136 * 0x0100-0x01ff -- GIC CPU interface
137 * 0x0200-0x02ff -- Global Timer
138 * 0x0300-0x05ff -- nothing
139 * 0x0600-0x06ff -- private timers and watchdogs
140 * 0x0700-0x0fff -- nothing
141 * 0x1000-0x1fff -- GIC Distributor
143 * We should implement the global timer but don't currently do so.
145 memory_region_init(&s
->container
, "a9mp-priv-container", 0x2000);
146 memory_region_init_io(&s
->scu_iomem
, &a9_scu_ops
, s
, "a9mp-scu", 0x100);
147 memory_region_add_subregion(&s
->container
, 0, &s
->scu_iomem
);
148 /* GIC CPU interface */
149 memory_region_add_subregion(&s
->container
, 0x100, &s
->gic
.cpuiomem
[0]);
150 /* Note that the A9 exposes only the "timer/watchdog for this core"
151 * memory region, not the "timer/watchdog for core X" ones 11MPcore has.
153 memory_region_add_subregion(&s
->container
, 0x600,
154 sysbus_mmio_get_region(busdev
, 0));
155 memory_region_add_subregion(&s
->container
, 0x620,
156 sysbus_mmio_get_region(busdev
, 1));
157 memory_region_add_subregion(&s
->container
, 0x1000, &s
->gic
.iomem
);
159 sysbus_init_mmio(dev
, &s
->container
);
161 /* Wire up the interrupt from each watchdog and timer. */
162 s
->timer_irq
= qemu_allocate_irqs(a9mpcore_timer_irq_handler
,
163 s
, (s
->num_cpu
+ 1) * 2);
164 for (i
= 0; i
< s
->num_cpu
* 2; i
++) {
165 sysbus_connect_irq(busdev
, i
, s
->timer_irq
[i
]);
170 static const VMStateDescription vmstate_a9mp_priv
= {
171 .name
= "a9mpcore_priv",
173 .minimum_version_id
= 1,
174 .fields
= (VMStateField
[]) {
175 VMSTATE_UINT32(scu_control
, a9mp_priv_state
),
176 VMSTATE_UINT32_ARRAY(old_timer_status
, a9mp_priv_state
, 8),
177 VMSTATE_END_OF_LIST()
181 static SysBusDeviceInfo a9mp_priv_info
= {
182 .init
= a9mp_priv_init
,
183 .qdev
.name
= "a9mpcore_priv",
184 .qdev
.size
= sizeof(a9mp_priv_state
),
185 .qdev
.vmsd
= &vmstate_a9mp_priv
,
186 .qdev
.reset
= a9mp_priv_reset
,
187 .qdev
.props
= (Property
[]) {
188 DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state
, num_cpu
, 1),
189 DEFINE_PROP_END_OF_LIST(),
193 static void a9mp_register_devices(void)
195 sysbus_register_withprop(&a9mp_priv_info
);
198 device_init(a9mp_register_devices
)