2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 #include <sys/systm.h>
32 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/mutex.h>
38 #include <machine/bus.h>
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_bus_subr.h>
43 #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
44 #include <arm/broadcom/bcm2835/bcm2835_mbox.h>
45 #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
46 #include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
52 #define REG_SENDER 0x14
53 #define REG_STATUS 0x18
54 #define STATUS_FULL 0x80000000
55 #define STATUS_EMPTY 0x40000000
56 #define REG_CONFIG 0x1C
57 #define CONFIG_DATA_IRQ 0x00000001
58 #define REG_WRITE 0x20 /* This is Mailbox 1 address */
60 #define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf))
61 #define MBOX_CHAN(msg) ((msg) & 0xf)
62 #define MBOX_DATA(msg) ((msg) & ~0xf)
64 #define MBOX_LOCK(sc) do { \
65 mtx_lock(&(sc)->lock); \
68 #define MBOX_UNLOCK(sc) do { \
69 mtx_unlock(&(sc)->lock); \
73 #define dprintf(fmt, args...) printf(fmt, ##args)
75 #define dprintf(fmt, args...)
78 struct bcm_mbox_softc
{
80 struct resource
* mem_res
;
81 struct resource
* irq_res
;
84 bus_space_handle_t bsh
;
85 int msg
[BCM2835_MBOX_CHANS
];
86 int have_message
[BCM2835_MBOX_CHANS
];
87 struct sx property_chan_lock
;
90 #define mbox_read_4(sc, reg) \
91 bus_space_read_4((sc)->bst, (sc)->bsh, reg)
92 #define mbox_write_4(sc, reg, val) \
93 bus_space_write_4((sc)->bst, (sc)->bsh, reg, val)
95 static struct ofw_compat_data compat_data
[] = {
96 {"broadcom,bcm2835-mbox", 1},
97 {"brcm,bcm2835-mbox", 1},
102 bcm_mbox_read_msg(struct bcm_mbox_softc
*sc
, int *ochan
)
110 msg
= mbox_read_4(sc
, REG_READ
);
111 dprintf("bcm_mbox_intr: raw data %08x\n", msg
);
112 chan
= MBOX_CHAN(msg
);
114 data
= MBOX_DATA(msg
);
117 printf("bcm_mbox_intr: channel %d oveflow\n", chan
);
120 dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan
, data
);
130 bcm_mbox_intr(void *arg
)
132 struct bcm_mbox_softc
*sc
= arg
;
136 while (!(mbox_read_4(sc
, REG_STATUS
) & STATUS_EMPTY
))
137 if (bcm_mbox_read_msg(sc
, &chan
) == 0) {
138 sc
->have_message
[chan
] = 1;
139 wakeup(&sc
->have_message
[chan
]);
145 bcm_mbox_probe(device_t dev
)
148 if (!ofw_bus_status_okay(dev
))
151 if (ofw_bus_search_compatible(dev
, compat_data
)->ocd_data
== 0)
154 device_set_desc(dev
, "BCM2835 VideoCore Mailbox");
156 return (BUS_PROBE_DEFAULT
);
160 bcm_mbox_attach(device_t dev
)
162 struct bcm_mbox_softc
*sc
= device_get_softc(dev
);
166 sc
->mem_res
= bus_alloc_resource_any(dev
, SYS_RES_MEMORY
, &rid
, RF_ACTIVE
);
167 if (sc
->mem_res
== NULL
) {
168 device_printf(dev
, "could not allocate memory resource\n");
172 sc
->bst
= rman_get_bustag(sc
->mem_res
);
173 sc
->bsh
= rman_get_bushandle(sc
->mem_res
);
176 sc
->irq_res
= bus_alloc_resource_any(dev
, SYS_RES_IRQ
, &rid
, RF_ACTIVE
);
177 if (sc
->irq_res
== NULL
) {
178 device_printf(dev
, "could not allocate interrupt resource\n");
182 /* Setup and enable the timer */
183 if (bus_setup_intr(dev
, sc
->irq_res
, INTR_MPSAFE
| INTR_TYPE_MISC
,
184 NULL
, bcm_mbox_intr
, sc
, &sc
->intr_hl
) != 0) {
185 bus_release_resource(dev
, SYS_RES_IRQ
, rid
, sc
->irq_res
);
186 device_printf(dev
, "Unable to setup the clock irq handler.\n");
190 mtx_init(&sc
->lock
, "vcio mbox", NULL
, MTX_DEF
);
191 for (i
= 0; i
< BCM2835_MBOX_CHANS
; i
++) {
193 sc
->have_message
[i
] = 0;
196 sx_init(&sc
->property_chan_lock
, "mboxprop");
198 /* Read all pending messages */
199 while ((mbox_read_4(sc
, REG_STATUS
) & STATUS_EMPTY
) == 0)
200 (void)mbox_read_4(sc
, REG_READ
);
202 mbox_write_4(sc
, REG_CONFIG
, CONFIG_DATA_IRQ
);
211 bcm_mbox_write(device_t dev
, int chan
, uint32_t data
)
214 struct bcm_mbox_softc
*sc
= device_get_softc(dev
);
216 dprintf("bcm_mbox_write: chan %d, data %08x\n", chan
, data
);
218 sc
->have_message
[chan
] = 0;
219 while ((mbox_read_4(sc
, REG_STATUS
) & STATUS_FULL
) && --limit
)
222 printf("bcm_mbox_write: STATUS_FULL stuck");
226 mbox_write_4(sc
, REG_WRITE
, MBOX_MSG(chan
, data
));
233 bcm_mbox_read(device_t dev
, int chan
, uint32_t *data
)
235 struct bcm_mbox_softc
*sc
= device_get_softc(dev
);
238 dprintf("bcm_mbox_read: chan %d\n", chan
);
243 if (sc
->have_message
[chan
] == 0) {
244 if (mtx_sleep(&sc
->have_message
[chan
], &sc
->lock
, 0,
245 "mbox", 10*hz
) != 0) {
246 device_printf(dev
, "timeout waiting for message on chan %d\n", chan
);
252 /* Wait for a message */
253 while ((mbox_read_4(sc
, REG_STATUS
) & STATUS_EMPTY
))
255 /* Read the message */
256 if (bcm_mbox_read_msg(sc
, &read_chan
) != 0) {
260 } while (read_chan
!= chan
);
263 * get data from intr handler, the same channel is never coming
264 * because of holding sc lock.
266 *data
= MBOX_DATA(sc
->msg
[chan
]);
268 sc
->have_message
[chan
] = 0;
271 dprintf("bcm_mbox_read: chan %d, data %08x\n", chan
, *data
);
276 static device_method_t bcm_mbox_methods
[] = {
277 DEVMETHOD(device_probe
, bcm_mbox_probe
),
278 DEVMETHOD(device_attach
, bcm_mbox_attach
),
280 DEVMETHOD(mbox_read
, bcm_mbox_read
),
281 DEVMETHOD(mbox_write
, bcm_mbox_write
),
286 static driver_t bcm_mbox_driver
= {
289 sizeof(struct bcm_mbox_softc
),
292 EARLY_DRIVER_MODULE(mbox
, simplebus
, bcm_mbox_driver
, 0, 0,
293 BUS_PASS_INTERRUPT
+ BUS_PASS_ORDER_LAST
);
296 bcm2835_mbox_dma_cb(void *arg
, bus_dma_segment_t
*segs
, int nseg
, int err
)
302 addr
= (bus_addr_t
*)arg
;
303 *addr
= ARMC_TO_VCBUS(segs
[0].ds_addr
);
307 bcm2835_mbox_init_dma(device_t dev
, size_t len
, bus_dma_tag_t
*tag
,
308 bus_dmamap_t
*map
, bus_addr_t
*phys
)
313 err
= bus_dma_tag_create(bus_get_dma_tag(dev
), 16, 0,
314 bcm283x_dmabus_peripheral_lowaddr(), BUS_SPACE_MAXADDR
, NULL
, NULL
,
315 len
, 1, len
, 0, NULL
, NULL
, tag
);
317 device_printf(dev
, "can't create DMA tag\n");
321 err
= bus_dmamem_alloc(*tag
, &buf
, 0, map
);
323 bus_dma_tag_destroy(*tag
);
324 device_printf(dev
, "can't allocate dmamem\n");
328 err
= bus_dmamap_load(*tag
, *map
, buf
, len
, bcm2835_mbox_dma_cb
,
331 bus_dmamem_free(*tag
, buf
, *map
);
332 bus_dma_tag_destroy(*tag
);
333 device_printf(dev
, "can't load DMA map\n");
341 bcm2835_mbox_err(device_t dev
, bus_addr_t msg_phys
, uint32_t resp_phys
,
342 struct bcm2835_mbox_hdr
*msg
, size_t len
)
345 struct bcm2835_mbox_tag_hdr
*tag
;
348 if ((uint32_t)msg_phys
!= resp_phys
) {
349 device_printf(dev
, "response channel mismatch\n");
352 if (msg
->code
!= BCM2835_MBOX_CODE_RESP_SUCCESS
) {
353 device_printf(dev
, "mbox response error\n");
357 /* Loop until the end tag. */
358 tag
= (struct bcm2835_mbox_tag_hdr
*)(msg
+ 1);
359 last
= (uint8_t *)msg
+ len
;
360 for (idx
= 0; tag
->tag
!= 0; idx
++) {
362 * When setting the GPIO config or state the firmware doesn't
363 * set tag->val_len correctly.
365 if ((tag
->tag
== BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG
||
366 tag
->tag
== BCM2835_FIRMWARE_TAG_SET_GPIO_STATE
) &&
368 tag
->val_len
= BCM2835_MBOX_TAG_VAL_LEN_RESPONSE
|
371 if ((tag
->val_len
& BCM2835_MBOX_TAG_VAL_LEN_RESPONSE
) == 0) {
372 device_printf(dev
, "tag %d response error\n", idx
);
375 /* Clear the response bit. */
376 tag
->val_len
&= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE
;
379 tag
= (struct bcm2835_mbox_tag_hdr
*)((uint8_t *)tag
+
380 sizeof(*tag
) + tag
->val_buf_size
);
382 if ((uint8_t *)tag
> last
) {
383 device_printf(dev
, "mbox buffer size error\n");
392 bcm2835_mbox_property(void *msg
, size_t msg_size
)
394 struct bcm_mbox_softc
*sc
;
395 bus_dma_tag_t msg_tag
;
396 bus_dmamap_t msg_map
;
403 /* get mbox device */
404 mbox
= devclass_get_device(devclass_find("mbox"), 0);
408 sc
= device_get_softc(mbox
);
409 sx_xlock(&sc
->property_chan_lock
);
411 /* Allocate memory for the message */
412 buf
= bcm2835_mbox_init_dma(mbox
, msg_size
, &msg_tag
, &msg_map
,
419 memcpy(buf
, msg
, msg_size
);
421 bus_dmamap_sync(msg_tag
, msg_map
,
422 BUS_DMASYNC_PREWRITE
);
424 MBOX_WRITE(mbox
, BCM2835_MBOX_CHAN_PROP
, (uint32_t)msg_phys
);
425 MBOX_READ(mbox
, BCM2835_MBOX_CHAN_PROP
, ®
);
427 bus_dmamap_sync(msg_tag
, msg_map
,
428 BUS_DMASYNC_PREREAD
);
430 memcpy(msg
, buf
, msg_size
);
432 err
= bcm2835_mbox_err(mbox
, msg_phys
, reg
,
433 (struct bcm2835_mbox_hdr
*)msg
, msg_size
);
435 bus_dmamap_unload(msg_tag
, msg_map
);
436 bus_dmamem_free(msg_tag
, buf
, msg_map
);
437 bus_dma_tag_destroy(msg_tag
);
439 sx_xunlock(&sc
->property_chan_lock
);
444 bcm2835_mbox_set_power_state(uint32_t device_id
, boolean_t on
)
446 struct msg_set_power_state msg
;
449 memset(&msg
, 0, sizeof(msg
));
450 msg
.hdr
.buf_size
= sizeof(msg
);
451 msg
.hdr
.code
= BCM2835_MBOX_CODE_REQ
;
452 msg
.tag_hdr
.tag
= BCM2835_MBOX_TAG_SET_POWER_STATE
;
453 msg
.tag_hdr
.val_buf_size
= sizeof(msg
.body
);
454 msg
.tag_hdr
.val_len
= sizeof(msg
.body
.req
);
455 msg
.body
.req
.device_id
= device_id
;
456 msg
.body
.req
.state
= (on
? BCM2835_MBOX_POWER_ON
: 0) |
457 BCM2835_MBOX_POWER_WAIT
;
460 err
= bcm2835_mbox_property(&msg
, sizeof(msg
));
466 bcm2835_mbox_notify_xhci_reset(uint32_t pci_dev_addr
)
468 struct msg_notify_xhci_reset msg
;
471 memset(&msg
, 0, sizeof(msg
));
472 msg
.hdr
.buf_size
= sizeof(msg
);
473 msg
.hdr
.code
= BCM2835_MBOX_CODE_REQ
;
474 msg
.tag_hdr
.tag
= BCM2835_MBOX_TAG_NOTIFY_XHCI_RESET
;
475 msg
.tag_hdr
.val_buf_size
= sizeof(msg
.body
);
476 msg
.tag_hdr
.val_len
= sizeof(msg
.body
.req
);
477 msg
.body
.req
.pci_device_addr
= pci_dev_addr
;
480 err
= bcm2835_mbox_property(&msg
, sizeof(msg
));
486 bcm2835_mbox_get_clock_rate(uint32_t clock_id
, uint32_t *hz
)
488 struct msg_get_clock_rate msg
;
491 memset(&msg
, 0, sizeof(msg
));
492 msg
.hdr
.buf_size
= sizeof(msg
);
493 msg
.hdr
.code
= BCM2835_MBOX_CODE_REQ
;
494 msg
.tag_hdr
.tag
= BCM2835_MBOX_TAG_GET_CLOCK_RATE
;
495 msg
.tag_hdr
.val_buf_size
= sizeof(msg
.body
);
496 msg
.tag_hdr
.val_len
= sizeof(msg
.body
.req
);
497 msg
.body
.req
.clock_id
= clock_id
;
500 err
= bcm2835_mbox_property(&msg
, sizeof(msg
));
501 *hz
= msg
.body
.resp
.rate_hz
;
507 bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config
*fb
)
510 struct msg_fb_get_w_h msg
;
512 memset(&msg
, 0, sizeof(msg
));
513 msg
.hdr
.buf_size
= sizeof(msg
);
514 msg
.hdr
.code
= BCM2835_MBOX_CODE_REQ
;
515 BCM2835_MBOX_INIT_TAG(&msg
.physical_w_h
, GET_PHYSICAL_W_H
);
516 msg
.physical_w_h
.tag_hdr
.val_len
= 0;
519 err
= bcm2835_mbox_property(&msg
, sizeof(msg
));
521 fb
->xres
= msg
.physical_w_h
.body
.resp
.width
;
522 fb
->yres
= msg
.physical_w_h
.body
.resp
.height
;
529 bcm2835_mbox_fb_get_bpp(struct bcm2835_fb_config
*fb
)
532 struct msg_fb_get_bpp msg
;
534 memset(&msg
, 0, sizeof(msg
));
535 msg
.hdr
.buf_size
= sizeof(msg
);
536 msg
.hdr
.code
= BCM2835_MBOX_CODE_REQ
;
537 BCM2835_MBOX_INIT_TAG(&msg
.bpp
, GET_DEPTH
);
538 msg
.bpp
.tag_hdr
.val_len
= 0;
541 err
= bcm2835_mbox_property(&msg
, sizeof(msg
));
543 fb
->bpp
= msg
.bpp
.body
.resp
.bpp
;
549 bcm2835_mbox_fb_init(struct bcm2835_fb_config
*fb
)
552 struct msg_fb_setup msg
;
554 memset(&msg
, 0, sizeof(msg
));
555 msg
.hdr
.buf_size
= sizeof(msg
);
556 msg
.hdr
.code
= BCM2835_MBOX_CODE_REQ
;
557 BCM2835_MBOX_INIT_TAG(&msg
.physical_w_h
, SET_PHYSICAL_W_H
);
558 msg
.physical_w_h
.body
.req
.width
= fb
->xres
;
559 msg
.physical_w_h
.body
.req
.height
= fb
->yres
;
560 BCM2835_MBOX_INIT_TAG(&msg
.virtual_w_h
, SET_VIRTUAL_W_H
);
561 msg
.virtual_w_h
.body
.req
.width
= fb
->vxres
;
562 msg
.virtual_w_h
.body
.req
.height
= fb
->vyres
;
563 BCM2835_MBOX_INIT_TAG(&msg
.offset
, SET_VIRTUAL_OFFSET
);
564 msg
.offset
.body
.req
.x
= fb
->xoffset
;
565 msg
.offset
.body
.req
.y
= fb
->yoffset
;
566 BCM2835_MBOX_INIT_TAG(&msg
.depth
, SET_DEPTH
);
567 msg
.depth
.body
.req
.bpp
= fb
->bpp
;
568 BCM2835_MBOX_INIT_TAG(&msg
.alpha
, SET_ALPHA_MODE
);
569 msg
.alpha
.body
.req
.alpha
= BCM2835_MBOX_ALPHA_MODE_IGNORED
;
570 BCM2835_MBOX_INIT_TAG(&msg
.buffer
, ALLOCATE_BUFFER
);
571 msg
.buffer
.body
.req
.alignment
= PAGE_SIZE
;
572 BCM2835_MBOX_INIT_TAG(&msg
.pitch
, GET_PITCH
);
575 err
= bcm2835_mbox_property(&msg
, sizeof(msg
));
577 fb
->xres
= msg
.physical_w_h
.body
.resp
.width
;
578 fb
->yres
= msg
.physical_w_h
.body
.resp
.height
;
579 fb
->vxres
= msg
.virtual_w_h
.body
.resp
.width
;
580 fb
->vyres
= msg
.virtual_w_h
.body
.resp
.height
;
581 fb
->xoffset
= msg
.offset
.body
.resp
.x
;
582 fb
->yoffset
= msg
.offset
.body
.resp
.y
;
583 fb
->pitch
= msg
.pitch
.body
.resp
.pitch
;
584 fb
->base
= VCBUS_TO_ARMC(msg
.buffer
.body
.resp
.fb_address
);
585 fb
->size
= msg
.buffer
.body
.resp
.fb_size
;