]>
Commit | Line | Data |
---|---|---|
353575f0 PC |
1 | /* |
2 | * Cortex-A9MPCore Snoop Control Unit (SCU) 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 | ||
83c9f4ca | 11 | #include "hw/sysbus.h" |
353575f0 PC |
12 | |
13 | /* A9MP private memory region. */ | |
14 | ||
15 | typedef struct A9SCUState { | |
16 | SysBusDevice busdev; | |
17 | MemoryRegion iomem; | |
18 | uint32_t control; | |
19 | uint32_t status; | |
20 | uint32_t num_cpu; | |
21 | } A9SCUState; | |
22 | ||
23 | #define TYPE_A9_SCU "a9-scu" | |
24 | #define A9_SCU(obj) OBJECT_CHECK(A9SCUState, (obj), TYPE_A9_SCU) | |
25 | ||
26 | static uint64_t a9_scu_read(void *opaque, hwaddr offset, | |
27 | unsigned size) | |
28 | { | |
29 | A9SCUState *s = (A9SCUState *)opaque; | |
30 | switch (offset) { | |
31 | case 0x00: /* Control */ | |
32 | return s->control; | |
33 | case 0x04: /* Configuration */ | |
34 | return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1); | |
35 | case 0x08: /* CPU Power Status */ | |
36 | return s->status; | |
37 | case 0x09: /* CPU status. */ | |
38 | return s->status >> 8; | |
39 | case 0x0a: /* CPU status. */ | |
40 | return s->status >> 16; | |
41 | case 0x0b: /* CPU status. */ | |
42 | return s->status >> 24; | |
43 | case 0x0c: /* Invalidate All Registers In Secure State */ | |
44 | return 0; | |
45 | case 0x40: /* Filtering Start Address Register */ | |
46 | case 0x44: /* Filtering End Address Register */ | |
47 | /* RAZ/WI, like an implementation with only one AXI master */ | |
48 | return 0; | |
49 | case 0x50: /* SCU Access Control Register */ | |
50 | case 0x54: /* SCU Non-secure Access Control Register */ | |
51 | /* unimplemented, fall through */ | |
52 | default: | |
53 | return 0; | |
54 | } | |
55 | } | |
56 | ||
57 | static void a9_scu_write(void *opaque, hwaddr offset, | |
58 | uint64_t value, unsigned size) | |
59 | { | |
60 | A9SCUState *s = (A9SCUState *)opaque; | |
61 | uint32_t mask; | |
62 | uint32_t shift; | |
63 | switch (size) { | |
64 | case 1: | |
65 | mask = 0xff; | |
66 | break; | |
67 | case 2: | |
68 | mask = 0xffff; | |
69 | break; | |
70 | case 4: | |
71 | mask = 0xffffffff; | |
72 | break; | |
73 | default: | |
74 | fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", | |
75 | size, (unsigned)offset); | |
76 | return; | |
77 | } | |
78 | ||
79 | switch (offset) { | |
80 | case 0x00: /* Control */ | |
81 | s->control = value & 1; | |
82 | break; | |
83 | case 0x4: /* Configuration: RO */ | |
84 | break; | |
85 | case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */ | |
86 | shift = (offset - 0x8) * 8; | |
87 | s->status &= ~(mask << shift); | |
88 | s->status |= ((value & mask) << shift); | |
89 | break; | |
90 | case 0x0c: /* Invalidate All Registers In Secure State */ | |
91 | /* no-op as we do not implement caches */ | |
92 | break; | |
93 | case 0x40: /* Filtering Start Address Register */ | |
94 | case 0x44: /* Filtering End Address Register */ | |
95 | /* RAZ/WI, like an implementation with only one AXI master */ | |
96 | break; | |
97 | case 0x50: /* SCU Access Control Register */ | |
98 | case 0x54: /* SCU Non-secure Access Control Register */ | |
99 | /* unimplemented, fall through */ | |
100 | default: | |
101 | break; | |
102 | } | |
103 | } | |
104 | ||
105 | static const MemoryRegionOps a9_scu_ops = { | |
106 | .read = a9_scu_read, | |
107 | .write = a9_scu_write, | |
108 | .endianness = DEVICE_NATIVE_ENDIAN, | |
109 | }; | |
110 | ||
111 | static void a9_scu_reset(DeviceState *dev) | |
112 | { | |
113 | A9SCUState *s = A9_SCU(dev); | |
114 | s->control = 0; | |
115 | } | |
116 | ||
117 | static void a9_scu_realize(DeviceState *dev, Error ** errp) | |
118 | { | |
119 | A9SCUState *s = A9_SCU(dev); | |
120 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
121 | ||
122 | memory_region_init_io(&s->iomem, &a9_scu_ops, s, "a9-scu", 0x100); | |
123 | sysbus_init_mmio(sbd, &s->iomem); | |
124 | } | |
125 | ||
126 | static const VMStateDescription vmstate_a9_scu = { | |
127 | .name = "a9-scu", | |
128 | .version_id = 1, | |
129 | .minimum_version_id = 1, | |
130 | .fields = (VMStateField[]) { | |
131 | VMSTATE_UINT32(control, A9SCUState), | |
132 | VMSTATE_UINT32(status, A9SCUState), | |
133 | VMSTATE_END_OF_LIST() | |
134 | } | |
135 | }; | |
136 | ||
137 | static Property a9_scu_properties[] = { | |
138 | DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), | |
139 | DEFINE_PROP_END_OF_LIST(), | |
140 | }; | |
141 | ||
142 | static void a9_scu_class_init(ObjectClass *klass, void *data) | |
143 | { | |
144 | DeviceClass *dc = DEVICE_CLASS(klass); | |
145 | ||
146 | dc->realize = a9_scu_realize; | |
147 | dc->props = a9_scu_properties; | |
148 | dc->vmsd = &vmstate_a9_scu; | |
149 | dc->reset = a9_scu_reset; | |
150 | } | |
151 | ||
152 | static const TypeInfo a9_scu_info = { | |
153 | .name = TYPE_A9_SCU, | |
154 | .parent = TYPE_SYS_BUS_DEVICE, | |
155 | .instance_size = sizeof(A9SCUState), | |
156 | .class_init = a9_scu_class_init, | |
157 | }; | |
158 | ||
159 | static void a9mp_register_types(void) | |
160 | { | |
161 | type_register_static(&a9_scu_info); | |
162 | } | |
163 | ||
164 | type_init(a9mp_register_types) |