1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8 #include "qemu/osdep.h"
9 #include "qapi/error.h"
10 #include "hw/misc/bcm2835_property.h"
11 #include "hw/qdev-properties.h"
12 #include "migration/vmstate.h"
13 #include "hw/irq.h"
14 #include "hw/misc/bcm2835_mbox_defs.h"
15 #include "hw/arm/raspberrypi-fw-defs.h"
16 #include "sysemu/dma.h"
17 #include "qemu/log.h"
18 #include "qemu/module.h"
19 #include "trace.h"
20 #include "hw/arm/raspi_platform.h"
21
22 #define VCHI_BUSADDR_SIZE sizeof(uint32_t)
23
24 /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
25
bcm2835_property_mbox_push(BCM2835PropertyState * s,uint32_t value)26 static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
27 {
28 uint32_t tot_len;
29
30 /*
31 * Copy the current state of the framebuffer config; we will update
32 * this copy as we process tags and then ask the framebuffer to use
33 * it at the end.
34 */
35 BCM2835FBConfig fbconfig = s->fbdev->config;
36 bool fbconfig_updated = false;
37
38 value &= ~0xf;
39
40 s->addr = value;
41
42 tot_len = ldl_le_phys(&s->dma_as, value);
43
44 /* @(addr + 4) : Buffer response code */
45 value = s->addr + 8;
46 while (value + 8 <= s->addr + tot_len) {
47 uint32_t tag = ldl_le_phys(&s->dma_as, value);
48 uint32_t bufsize = ldl_le_phys(&s->dma_as, value + 4);
49 /* @(value + 8) : Request/response indicator */
50 size_t resplen = 0;
51 switch (tag) {
52 case RPI_FWREQ_PROPERTY_END:
53 break;
54 case RPI_FWREQ_GET_FIRMWARE_REVISION:
55 stl_le_phys(&s->dma_as, value + 12, 346337);
56 resplen = 4;
57 break;
58 case RPI_FWREQ_GET_BOARD_MODEL:
59 qemu_log_mask(LOG_UNIMP,
60 "bcm2835_property: 0x%08x get board model NYI\n",
61 tag);
62 resplen = 4;
63 break;
64 case RPI_FWREQ_GET_BOARD_REVISION:
65 stl_le_phys(&s->dma_as, value + 12, s->board_rev);
66 resplen = 4;
67 break;
68 case RPI_FWREQ_GET_BOARD_MAC_ADDRESS:
69 resplen = sizeof(s->macaddr.a);
70 dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen,
71 MEMTXATTRS_UNSPECIFIED);
72 break;
73 case RPI_FWREQ_GET_BOARD_SERIAL:
74 qemu_log_mask(LOG_UNIMP,
75 "bcm2835_property: 0x%08x get board serial NYI\n",
76 tag);
77 resplen = 8;
78 break;
79 case RPI_FWREQ_GET_ARM_MEMORY:
80 /* base */
81 stl_le_phys(&s->dma_as, value + 12, 0);
82 /* size */
83 stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
84 resplen = 8;
85 break;
86 case RPI_FWREQ_GET_VC_MEMORY:
87 /* base */
88 stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
89 /* size */
90 stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
91 resplen = 8;
92 break;
93 case RPI_FWREQ_SET_POWER_STATE:
94 {
95 /*
96 * Assume that whatever device they asked for exists,
97 * and we'll just claim we set it to the desired state.
98 */
99 uint32_t state = ldl_le_phys(&s->dma_as, value + 16);
100 stl_le_phys(&s->dma_as, value + 16, (state & 1));
101 resplen = 8;
102 break;
103 }
104
105 /* Clocks */
106
107 case RPI_FWREQ_GET_CLOCK_STATE:
108 stl_le_phys(&s->dma_as, value + 16, 0x1);
109 resplen = 8;
110 break;
111
112 case RPI_FWREQ_SET_CLOCK_STATE:
113 qemu_log_mask(LOG_UNIMP,
114 "bcm2835_property: 0x%08x set clock state NYI\n",
115 tag);
116 resplen = 8;
117 break;
118
119 case RPI_FWREQ_GET_CLOCK_RATE:
120 case RPI_FWREQ_GET_MAX_CLOCK_RATE:
121 case RPI_FWREQ_GET_MIN_CLOCK_RATE:
122 switch (ldl_le_phys(&s->dma_as, value + 12)) {
123 case RPI_FIRMWARE_EMMC_CLK_ID:
124 stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_EMMC_CLK_RATE);
125 break;
126 case RPI_FIRMWARE_UART_CLK_ID:
127 stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE);
128 break;
129 case RPI_FIRMWARE_CORE_CLK_ID:
130 stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_CORE_CLK_RATE);
131 break;
132 default:
133 stl_le_phys(&s->dma_as, value + 16,
134 RPI_FIRMWARE_DEFAULT_CLK_RATE);
135 break;
136 }
137 resplen = 8;
138 break;
139
140 case RPI_FWREQ_GET_CLOCKS:
141 /* TODO: add more clock IDs if needed */
142 stl_le_phys(&s->dma_as, value + 12, 0);
143 stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID);
144 resplen = 8;
145 break;
146
147 case RPI_FWREQ_SET_CLOCK_RATE:
148 case RPI_FWREQ_SET_MAX_CLOCK_RATE:
149 case RPI_FWREQ_SET_MIN_CLOCK_RATE:
150 qemu_log_mask(LOG_UNIMP,
151 "bcm2835_property: 0x%08x set clock rate NYI\n",
152 tag);
153 resplen = 8;
154 break;
155
156 /* Temperature */
157
158 case RPI_FWREQ_GET_TEMPERATURE:
159 stl_le_phys(&s->dma_as, value + 16, 25000);
160 resplen = 8;
161 break;
162
163 case RPI_FWREQ_GET_MAX_TEMPERATURE:
164 stl_le_phys(&s->dma_as, value + 16, 99000);
165 resplen = 8;
166 break;
167
168 /* Frame buffer */
169
170 case RPI_FWREQ_FRAMEBUFFER_ALLOCATE:
171 stl_le_phys(&s->dma_as, value + 12, fbconfig.base);
172 stl_le_phys(&s->dma_as, value + 16,
173 bcm2835_fb_get_size(&fbconfig));
174 resplen = 8;
175 break;
176 case RPI_FWREQ_FRAMEBUFFER_RELEASE:
177 resplen = 0;
178 break;
179 case RPI_FWREQ_FRAMEBUFFER_BLANK:
180 resplen = 4;
181 break;
182 case RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT:
183 case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT:
184 resplen = 8;
185 break;
186 case RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT:
187 fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12);
188 fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16);
189 bcm2835_fb_validate_config(&fbconfig);
190 fbconfig_updated = true;
191 /* fall through */
192 case RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT:
193 stl_le_phys(&s->dma_as, value + 12, fbconfig.xres);
194 stl_le_phys(&s->dma_as, value + 16, fbconfig.yres);
195 resplen = 8;
196 break;
197 case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT:
198 fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12);
199 fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16);
200 bcm2835_fb_validate_config(&fbconfig);
201 fbconfig_updated = true;
202 /* fall through */
203 case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT:
204 stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual);
205 stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual);
206 resplen = 8;
207 break;
208 case RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH:
209 resplen = 4;
210 break;
211 case RPI_FWREQ_FRAMEBUFFER_SET_DEPTH:
212 fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12);
213 bcm2835_fb_validate_config(&fbconfig);
214 fbconfig_updated = true;
215 /* fall through */
216 case RPI_FWREQ_FRAMEBUFFER_GET_DEPTH:
217 stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp);
218 resplen = 4;
219 break;
220 case RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER:
221 resplen = 4;
222 break;
223 case RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER:
224 fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12);
225 bcm2835_fb_validate_config(&fbconfig);
226 fbconfig_updated = true;
227 /* fall through */
228 case RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER:
229 stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo);
230 resplen = 4;
231 break;
232 case RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE:
233 resplen = 4;
234 break;
235 case RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE:
236 fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12);
237 bcm2835_fb_validate_config(&fbconfig);
238 fbconfig_updated = true;
239 /* fall through */
240 case RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE:
241 stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha);
242 resplen = 4;
243 break;
244 case RPI_FWREQ_FRAMEBUFFER_GET_PITCH:
245 stl_le_phys(&s->dma_as, value + 12,
246 bcm2835_fb_get_pitch(&fbconfig));
247 resplen = 4;
248 break;
249 case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET:
250 resplen = 8;
251 break;
252 case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET:
253 fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12);
254 fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16);
255 bcm2835_fb_validate_config(&fbconfig);
256 fbconfig_updated = true;
257 /* fall through */
258 case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET:
259 stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset);
260 stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset);
261 resplen = 8;
262 break;
263 case RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN:
264 case RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN:
265 case RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN:
266 stl_le_phys(&s->dma_as, value + 12, 0);
267 stl_le_phys(&s->dma_as, value + 16, 0);
268 stl_le_phys(&s->dma_as, value + 20, 0);
269 stl_le_phys(&s->dma_as, value + 24, 0);
270 resplen = 16;
271 break;
272 case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE:
273 {
274 uint32_t offset = ldl_le_phys(&s->dma_as, value + 12);
275 uint32_t length = ldl_le_phys(&s->dma_as, value + 16);
276 int resp;
277
278 if (offset > 255 || length < 1 || length > 256) {
279 resp = 1; /* invalid request */
280 } else {
281 for (uint32_t e = 0; e < length; e++) {
282 uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2));
283 stl_le_phys(&s->dma_as,
284 s->fbdev->vcram_base + ((offset + e) << 2), color);
285 }
286 resp = 0;
287 }
288 stl_le_phys(&s->dma_as, value + 12, resp);
289 resplen = 4;
290 break;
291 }
292 case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS:
293 stl_le_phys(&s->dma_as, value + 12, 1);
294 resplen = 4;
295 break;
296
297 case RPI_FWREQ_GET_DMA_CHANNELS:
298 /* channels 2-5 */
299 stl_le_phys(&s->dma_as, value + 12, 0x003C);
300 resplen = 4;
301 break;
302
303 case RPI_FWREQ_GET_COMMAND_LINE:
304 /*
305 * We follow the firmware behaviour: no NUL terminator is
306 * written to the buffer, and if the buffer is too short
307 * we report the required length in the response header
308 * and copy nothing to the buffer.
309 */
310 resplen = strlen(s->command_line);
311 if (bufsize >= resplen)
312 address_space_write(&s->dma_as, value + 12,
313 MEMTXATTRS_UNSPECIFIED, s->command_line,
314 resplen);
315 break;
316
317 case RPI_FWREQ_GET_THROTTLED:
318 stl_le_phys(&s->dma_as, value + 12, 0);
319 resplen = 4;
320 break;
321
322 case RPI_FWREQ_VCHIQ_INIT:
323 stl_le_phys(&s->dma_as,
324 value + offsetof(rpi_firmware_prop_request_t, payload),
325 0);
326 resplen = VCHI_BUSADDR_SIZE;
327 break;
328
329 /* Customer OTP */
330
331 case RPI_FWREQ_GET_CUSTOMER_OTP:
332 {
333 uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12);
334 uint32_t number = ldl_le_phys(&s->dma_as, value + 16);
335
336 resplen = 8 + 4 * number;
337
338 for (uint32_t n = start_num; n < start_num + number &&
339 n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) {
340 uint32_t otp_row = bcm2835_otp_get_row(s->otp,
341 BCM2835_OTP_CUSTOMER_OTP + n);
342 stl_le_phys(&s->dma_as,
343 value + 20 + ((n - start_num) << 2), otp_row);
344 }
345 break;
346 }
347 case RPI_FWREQ_SET_CUSTOMER_OTP:
348 {
349 uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12);
350 uint32_t number = ldl_le_phys(&s->dma_as, value + 16);
351
352 resplen = 4;
353
354 /* Magic numbers to permanently lock customer OTP */
355 if (start_num == BCM2835_OTP_LOCK_NUM1 &&
356 number == BCM2835_OTP_LOCK_NUM2) {
357 bcm2835_otp_set_row(s->otp,
358 BCM2835_OTP_ROW_32,
359 BCM2835_OTP_ROW_32_LOCK);
360 break;
361 }
362
363 /* If row 32 has the lock bit, don't allow further writes */
364 if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) &
365 BCM2835_OTP_ROW_32_LOCK) {
366 break;
367 }
368
369 for (uint32_t n = start_num; n < start_num + number &&
370 n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) {
371 uint32_t otp_row = ldl_le_phys(&s->dma_as,
372 value + 20 + ((n - start_num) << 2));
373 bcm2835_otp_set_row(s->otp,
374 BCM2835_OTP_CUSTOMER_OTP + n, otp_row);
375 }
376 break;
377 }
378
379 /* Device-specific private key */
380 case RPI_FWREQ_GET_PRIVATE_KEY:
381 {
382 uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12);
383 uint32_t number = ldl_le_phys(&s->dma_as, value + 16);
384
385 resplen = 8 + 4 * number;
386
387 for (uint32_t n = start_num; n < start_num + number &&
388 n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) {
389 uint32_t otp_row = bcm2835_otp_get_row(s->otp,
390 BCM2835_OTP_PRIVATE_KEY + n);
391 stl_le_phys(&s->dma_as,
392 value + 20 + ((n - start_num) << 2), otp_row);
393 }
394 break;
395 }
396 case RPI_FWREQ_SET_PRIVATE_KEY:
397 {
398 uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12);
399 uint32_t number = ldl_le_phys(&s->dma_as, value + 16);
400
401 resplen = 4;
402
403 /* If row 32 has the lock bit, don't allow further writes */
404 if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) &
405 BCM2835_OTP_ROW_32_LOCK) {
406 break;
407 }
408
409 for (uint32_t n = start_num; n < start_num + number &&
410 n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) {
411 uint32_t otp_row = ldl_le_phys(&s->dma_as,
412 value + 20 + ((n - start_num) << 2));
413 bcm2835_otp_set_row(s->otp,
414 BCM2835_OTP_PRIVATE_KEY + n, otp_row);
415 }
416 break;
417 }
418 default:
419 qemu_log_mask(LOG_UNIMP,
420 "bcm2835_property: unhandled tag 0x%08x\n", tag);
421 break;
422 }
423
424 trace_bcm2835_mbox_property(tag, bufsize, resplen);
425 if (tag == 0) {
426 break;
427 }
428
429 stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
430 value += bufsize + 12;
431 }
432
433 /* Reconfigure framebuffer if required */
434 if (fbconfig_updated) {
435 bcm2835_fb_reconfigure(s->fbdev, &fbconfig);
436 }
437
438 /* Buffer response code */
439 stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
440 }
441
bcm2835_property_read(void * opaque,hwaddr offset,unsigned size)442 static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
443 unsigned size)
444 {
445 BCM2835PropertyState *s = opaque;
446 uint32_t res = 0;
447
448 switch (offset) {
449 case MBOX_AS_DATA:
450 res = MBOX_CHAN_PROPERTY | s->addr;
451 s->pending = false;
452 qemu_set_irq(s->mbox_irq, 0);
453 break;
454
455 case MBOX_AS_PENDING:
456 res = s->pending;
457 break;
458
459 default:
460 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
461 __func__, offset);
462 return 0;
463 }
464
465 return res;
466 }
467
bcm2835_property_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)468 static void bcm2835_property_write(void *opaque, hwaddr offset,
469 uint64_t value, unsigned size)
470 {
471 BCM2835PropertyState *s = opaque;
472
473 switch (offset) {
474 case MBOX_AS_DATA:
475 /* bcm2835_mbox should check our pending status before pushing */
476 assert(!s->pending);
477 s->pending = true;
478 bcm2835_property_mbox_push(s, value);
479 qemu_set_irq(s->mbox_irq, 1);
480 break;
481
482 default:
483 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
484 __func__, offset);
485 return;
486 }
487 }
488
489 static const MemoryRegionOps bcm2835_property_ops = {
490 .read = bcm2835_property_read,
491 .write = bcm2835_property_write,
492 .endianness = DEVICE_NATIVE_ENDIAN,
493 .valid.min_access_size = 4,
494 .valid.max_access_size = 4,
495 };
496
497 static const VMStateDescription vmstate_bcm2835_property = {
498 .name = TYPE_BCM2835_PROPERTY,
499 .version_id = 1,
500 .minimum_version_id = 1,
501 .fields = (const VMStateField[]) {
502 VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
503 VMSTATE_UINT32(addr, BCM2835PropertyState),
504 VMSTATE_BOOL(pending, BCM2835PropertyState),
505 VMSTATE_END_OF_LIST()
506 }
507 };
508
bcm2835_property_init(Object * obj)509 static void bcm2835_property_init(Object *obj)
510 {
511 BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
512
513 memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
514 TYPE_BCM2835_PROPERTY, 0x10);
515
516 /*
517 * bcm2835_property_ops call into bcm2835_mbox, which in-turn reads from
518 * iomem. As such, mark iomem as re-entracy safe.
519 */
520 s->iomem.disable_reentrancy_guard = true;
521
522 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
523 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
524 }
525
bcm2835_property_reset(DeviceState * dev)526 static void bcm2835_property_reset(DeviceState *dev)
527 {
528 BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
529
530 s->pending = false;
531 }
532
bcm2835_property_realize(DeviceState * dev,Error ** errp)533 static void bcm2835_property_realize(DeviceState *dev, Error **errp)
534 {
535 BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
536 Object *obj;
537
538 obj = object_property_get_link(OBJECT(dev), "fb", &error_abort);
539 s->fbdev = BCM2835_FB(obj);
540
541 obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort);
542 s->dma_mr = MEMORY_REGION(obj);
543 address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory");
544
545 obj = object_property_get_link(OBJECT(dev), "otp", &error_abort);
546 s->otp = BCM2835_OTP(obj);
547
548 /* TODO: connect to MAC address of USB NIC device, once we emulate it */
549 qemu_macaddr_default_if_unset(&s->macaddr);
550
551 bcm2835_property_reset(dev);
552 }
553
554 static Property bcm2835_property_props[] = {
555 DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
556 DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line),
557 DEFINE_PROP_END_OF_LIST()
558 };
559
bcm2835_property_class_init(ObjectClass * klass,void * data)560 static void bcm2835_property_class_init(ObjectClass *klass, void *data)
561 {
562 DeviceClass *dc = DEVICE_CLASS(klass);
563
564 device_class_set_props(dc, bcm2835_property_props);
565 dc->realize = bcm2835_property_realize;
566 dc->vmsd = &vmstate_bcm2835_property;
567 }
568
569 static const TypeInfo bcm2835_property_info = {
570 .name = TYPE_BCM2835_PROPERTY,
571 .parent = TYPE_SYS_BUS_DEVICE,
572 .instance_size = sizeof(BCM2835PropertyState),
573 .class_init = bcm2835_property_class_init,
574 .instance_init = bcm2835_property_init,
575 };
576
bcm2835_property_register_types(void)577 static void bcm2835_property_register_types(void)
578 {
579 type_register_static(&bcm2835_property_info);
580 }
581
582 type_init(bcm2835_property_register_types)
583