]>
Commit | Line | Data |
---|---|---|
04f1ab15 AB |
1 | /* |
2 | * Raspberry Pi emulation (c) 2012 Gregory Estrade | |
3 | * This code is licensed under the GNU GPLv2 and later. | |
4 | */ | |
5 | ||
6 | #include "hw/misc/bcm2835_property.h" | |
7 | #include "hw/misc/bcm2835_mbox_defs.h" | |
8 | #include "sysemu/dma.h" | |
9 | ||
10 | /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ | |
11 | ||
12 | static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) | |
13 | { | |
14 | uint32_t tag; | |
15 | uint32_t bufsize; | |
16 | uint32_t tot_len; | |
17 | size_t resplen; | |
18 | uint32_t tmp; | |
19 | ||
20 | value &= ~0xf; | |
21 | ||
22 | s->addr = value; | |
23 | ||
24 | tot_len = ldl_phys(&s->dma_as, value); | |
25 | ||
26 | /* @(addr + 4) : Buffer response code */ | |
27 | value = s->addr + 8; | |
28 | while (value + 8 <= s->addr + tot_len) { | |
29 | tag = ldl_phys(&s->dma_as, value); | |
30 | bufsize = ldl_phys(&s->dma_as, value + 4); | |
31 | /* @(value + 8) : Request/response indicator */ | |
32 | resplen = 0; | |
33 | switch (tag) { | |
34 | case 0x00000000: /* End tag */ | |
35 | break; | |
36 | case 0x00000001: /* Get firmware revision */ | |
37 | stl_phys(&s->dma_as, value + 12, 346337); | |
38 | resplen = 4; | |
39 | break; | |
40 | case 0x00010001: /* Get board model */ | |
41 | qemu_log_mask(LOG_UNIMP, | |
42 | "bcm2835_property: %x get board model NYI\n", tag); | |
43 | resplen = 4; | |
44 | break; | |
45 | case 0x00010002: /* Get board revision */ | |
46 | qemu_log_mask(LOG_UNIMP, | |
47 | "bcm2835_property: %x get board revision NYI\n", tag); | |
48 | resplen = 4; | |
49 | break; | |
50 | case 0x00010003: /* Get board MAC address */ | |
51 | resplen = sizeof(s->macaddr.a); | |
52 | dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen); | |
53 | break; | |
54 | case 0x00010004: /* Get board serial */ | |
55 | qemu_log_mask(LOG_UNIMP, | |
56 | "bcm2835_property: %x get board serial NYI\n", tag); | |
57 | resplen = 8; | |
58 | break; | |
59 | case 0x00010005: /* Get ARM memory */ | |
60 | /* base */ | |
61 | stl_phys(&s->dma_as, value + 12, 0); | |
62 | /* size */ | |
63 | stl_phys(&s->dma_as, value + 16, s->ram_size); | |
64 | resplen = 8; | |
65 | break; | |
66 | case 0x00028001: /* Set power state */ | |
67 | /* Assume that whatever device they asked for exists, | |
68 | * and we'll just claim we set it to the desired state | |
69 | */ | |
70 | tmp = ldl_phys(&s->dma_as, value + 16); | |
71 | stl_phys(&s->dma_as, value + 16, (tmp & 1)); | |
72 | resplen = 8; | |
73 | break; | |
74 | ||
75 | /* Clocks */ | |
76 | ||
77 | case 0x00030001: /* Get clock state */ | |
78 | stl_phys(&s->dma_as, value + 16, 0x1); | |
79 | resplen = 8; | |
80 | break; | |
81 | ||
82 | case 0x00038001: /* Set clock state */ | |
83 | qemu_log_mask(LOG_UNIMP, | |
84 | "bcm2835_property: %x set clock state NYI\n", tag); | |
85 | resplen = 8; | |
86 | break; | |
87 | ||
88 | case 0x00030002: /* Get clock rate */ | |
89 | case 0x00030004: /* Get max clock rate */ | |
90 | case 0x00030007: /* Get min clock rate */ | |
91 | switch (ldl_phys(&s->dma_as, value + 12)) { | |
92 | case 1: /* EMMC */ | |
93 | stl_phys(&s->dma_as, value + 16, 50000000); | |
94 | break; | |
95 | case 2: /* UART */ | |
96 | stl_phys(&s->dma_as, value + 16, 3000000); | |
97 | break; | |
98 | default: | |
99 | stl_phys(&s->dma_as, value + 16, 700000000); | |
100 | break; | |
101 | } | |
102 | resplen = 8; | |
103 | break; | |
104 | ||
105 | case 0x00038002: /* Set clock rate */ | |
106 | case 0x00038004: /* Set max clock rate */ | |
107 | case 0x00038007: /* Set min clock rate */ | |
108 | qemu_log_mask(LOG_UNIMP, | |
109 | "bcm2835_property: %x set clock rates NYI\n", tag); | |
110 | resplen = 8; | |
111 | break; | |
112 | ||
113 | /* Temperature */ | |
114 | ||
115 | case 0x00030006: /* Get temperature */ | |
116 | stl_phys(&s->dma_as, value + 16, 25000); | |
117 | resplen = 8; | |
118 | break; | |
119 | ||
120 | case 0x0003000A: /* Get max temperature */ | |
121 | stl_phys(&s->dma_as, value + 16, 99000); | |
122 | resplen = 8; | |
123 | break; | |
124 | ||
125 | ||
126 | case 0x00060001: /* Get DMA channels */ | |
127 | /* channels 2-5 */ | |
128 | stl_phys(&s->dma_as, value + 12, 0x003C); | |
129 | resplen = 4; | |
130 | break; | |
131 | ||
132 | case 0x00050001: /* Get command line */ | |
133 | resplen = 0; | |
134 | break; | |
135 | ||
136 | default: | |
137 | qemu_log_mask(LOG_GUEST_ERROR, | |
138 | "bcm2835_property: unhandled tag %08x\n", tag); | |
139 | break; | |
140 | } | |
141 | ||
142 | if (tag == 0) { | |
143 | break; | |
144 | } | |
145 | ||
146 | stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen); | |
147 | value += bufsize + 12; | |
148 | } | |
149 | ||
150 | /* Buffer response code */ | |
151 | stl_phys(&s->dma_as, s->addr + 4, (1 << 31)); | |
152 | } | |
153 | ||
154 | static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, | |
155 | unsigned size) | |
156 | { | |
157 | BCM2835PropertyState *s = opaque; | |
158 | uint32_t res = 0; | |
159 | ||
160 | switch (offset) { | |
161 | case MBOX_AS_DATA: | |
162 | res = MBOX_CHAN_PROPERTY | s->addr; | |
163 | s->pending = false; | |
164 | qemu_set_irq(s->mbox_irq, 0); | |
165 | break; | |
166 | ||
167 | case MBOX_AS_PENDING: | |
168 | res = s->pending; | |
169 | break; | |
170 | ||
171 | default: | |
172 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", | |
173 | __func__, offset); | |
174 | return 0; | |
175 | } | |
176 | ||
177 | return res; | |
178 | } | |
179 | ||
180 | static void bcm2835_property_write(void *opaque, hwaddr offset, | |
181 | uint64_t value, unsigned size) | |
182 | { | |
183 | BCM2835PropertyState *s = opaque; | |
184 | ||
185 | switch (offset) { | |
186 | case MBOX_AS_DATA: | |
187 | /* bcm2835_mbox should check our pending status before pushing */ | |
188 | assert(!s->pending); | |
189 | s->pending = true; | |
190 | bcm2835_property_mbox_push(s, value); | |
191 | qemu_set_irq(s->mbox_irq, 1); | |
192 | break; | |
193 | ||
194 | default: | |
195 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", | |
196 | __func__, offset); | |
197 | return; | |
198 | } | |
199 | } | |
200 | ||
201 | static const MemoryRegionOps bcm2835_property_ops = { | |
202 | .read = bcm2835_property_read, | |
203 | .write = bcm2835_property_write, | |
204 | .endianness = DEVICE_NATIVE_ENDIAN, | |
205 | .valid.min_access_size = 4, | |
206 | .valid.max_access_size = 4, | |
207 | }; | |
208 | ||
209 | static const VMStateDescription vmstate_bcm2835_property = { | |
210 | .name = TYPE_BCM2835_PROPERTY, | |
211 | .version_id = 1, | |
212 | .minimum_version_id = 1, | |
213 | .fields = (VMStateField[]) { | |
214 | VMSTATE_MACADDR(macaddr, BCM2835PropertyState), | |
215 | VMSTATE_UINT32(addr, BCM2835PropertyState), | |
216 | VMSTATE_BOOL(pending, BCM2835PropertyState), | |
217 | VMSTATE_END_OF_LIST() | |
218 | } | |
219 | }; | |
220 | ||
221 | static void bcm2835_property_init(Object *obj) | |
222 | { | |
223 | BCM2835PropertyState *s = BCM2835_PROPERTY(obj); | |
224 | ||
225 | memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, | |
226 | TYPE_BCM2835_PROPERTY, 0x10); | |
227 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); | |
228 | sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); | |
229 | } | |
230 | ||
231 | static void bcm2835_property_reset(DeviceState *dev) | |
232 | { | |
233 | BCM2835PropertyState *s = BCM2835_PROPERTY(dev); | |
234 | ||
235 | s->pending = false; | |
236 | } | |
237 | ||
238 | static void bcm2835_property_realize(DeviceState *dev, Error **errp) | |
239 | { | |
240 | BCM2835PropertyState *s = BCM2835_PROPERTY(dev); | |
241 | Object *obj; | |
242 | Error *err = NULL; | |
243 | ||
244 | obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); | |
245 | if (obj == NULL) { | |
246 | error_setg(errp, "%s: required dma-mr link not found: %s", | |
247 | __func__, error_get_pretty(err)); | |
248 | return; | |
249 | } | |
250 | ||
251 | s->dma_mr = MEMORY_REGION(obj); | |
252 | address_space_init(&s->dma_as, s->dma_mr, NULL); | |
253 | ||
254 | /* TODO: connect to MAC address of USB NIC device, once we emulate it */ | |
255 | qemu_macaddr_default_if_unset(&s->macaddr); | |
256 | ||
257 | bcm2835_property_reset(dev); | |
258 | } | |
259 | ||
260 | static Property bcm2835_property_props[] = { | |
261 | DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0), | |
262 | DEFINE_PROP_END_OF_LIST() | |
263 | }; | |
264 | ||
265 | static void bcm2835_property_class_init(ObjectClass *klass, void *data) | |
266 | { | |
267 | DeviceClass *dc = DEVICE_CLASS(klass); | |
268 | ||
269 | dc->props = bcm2835_property_props; | |
270 | dc->realize = bcm2835_property_realize; | |
271 | dc->vmsd = &vmstate_bcm2835_property; | |
272 | } | |
273 | ||
274 | static TypeInfo bcm2835_property_info = { | |
275 | .name = TYPE_BCM2835_PROPERTY, | |
276 | .parent = TYPE_SYS_BUS_DEVICE, | |
277 | .instance_size = sizeof(BCM2835PropertyState), | |
278 | .class_init = bcm2835_property_class_init, | |
279 | .instance_init = bcm2835_property_init, | |
280 | }; | |
281 | ||
282 | static void bcm2835_property_register_types(void) | |
283 | { | |
284 | type_register_static(&bcm2835_property_info); | |
285 | } | |
286 | ||
287 | type_init(bcm2835_property_register_types) |