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