1239922Sgonzo/*-
2239922Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3239922Sgonzo * All rights reserved.
4239922Sgonzo *
5239922Sgonzo * Redistribution and use in source and binary forms, with or without
6239922Sgonzo * modification, are permitted provided that the following conditions
7239922Sgonzo * are met:
8239922Sgonzo * 1. Redistributions of source code must retain the above copyright
9239922Sgonzo *    notice, this list of conditions and the following disclaimer.
10239922Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239922Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239922Sgonzo *    documentation and/or other materials provided with the distribution.
13239922Sgonzo *
14239922Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239922Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239922Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239922Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239922Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239922Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239922Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239922Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239922Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239922Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239922Sgonzo * SUCH DAMAGE.
25239922Sgonzo */
26239922Sgonzo
27239922Sgonzo#include <sys/cdefs.h>
28239922Sgonzo__FBSDID("$FreeBSD: stable/11/sys/arm/broadcom/bcm2835/bcm2835_mbox.c 307575 2016-10-18 19:15:43Z gonzo $");
29239922Sgonzo
30239922Sgonzo#include <sys/param.h>
31239922Sgonzo#include <sys/systm.h>
32239922Sgonzo#include <sys/bus.h>
33239922Sgonzo#include <sys/kernel.h>
34276303Sloos#include <sys/lock.h>
35239922Sgonzo#include <sys/module.h>
36276303Sloos#include <sys/mutex.h>
37290666Sgonzo#include <sys/sx.h>
38239922Sgonzo#include <sys/rman.h>
39239922Sgonzo#include <machine/bus.h>
40239922Sgonzo
41239922Sgonzo#include <dev/ofw/ofw_bus.h>
42239922Sgonzo#include <dev/ofw/ofw_bus_subr.h>
43239922Sgonzo
44239922Sgonzo#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
45280283Sandrew#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
46280283Sandrew#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
47239922Sgonzo
48253006Srpaulo#include "mbox_if.h"
49253006Srpaulo
50239922Sgonzo#define	REG_READ	0x00
51239922Sgonzo#define	REG_POL		0x10
52239922Sgonzo#define	REG_SENDER	0x14
53239922Sgonzo#define	REG_STATUS	0x18
54239922Sgonzo#define		STATUS_FULL	0x80000000
55239922Sgonzo#define		STATUS_EMPTY	0x40000000
56239922Sgonzo#define	REG_CONFIG	0x1C
57239922Sgonzo#define		CONFIG_DATA_IRQ	0x00000001
58239922Sgonzo#define	REG_WRITE	0x20 /* This is Mailbox 1 address */
59239922Sgonzo
60239922Sgonzo#define	MBOX_MSG(chan, data)	(((data) & ~0xf) | ((chan) & 0xf))
61239922Sgonzo#define	MBOX_CHAN(msg)		((msg) & 0xf)
62239922Sgonzo#define	MBOX_DATA(msg)		((msg) & ~0xf)
63239922Sgonzo
64253006Srpaulo#define	MBOX_LOCK(sc)	do {	\
65253006Srpaulo	mtx_lock(&(sc)->lock);	\
66239922Sgonzo} while(0)
67239922Sgonzo
68253006Srpaulo#define	MBOX_UNLOCK(sc)	do {		\
69253006Srpaulo	mtx_unlock(&(sc)->lock);	\
70239922Sgonzo} while(0)
71239922Sgonzo
72239922Sgonzo#ifdef  DEBUG
73239922Sgonzo#define dprintf(fmt, args...) printf(fmt, ##args)
74239922Sgonzo#else
75239922Sgonzo#define dprintf(fmt, args...)
76239922Sgonzo#endif
77239922Sgonzo
78239922Sgonzostruct bcm_mbox_softc {
79239922Sgonzo	struct mtx		lock;
80239922Sgonzo	struct resource *	mem_res;
81239922Sgonzo	struct resource *	irq_res;
82239922Sgonzo	void*			intr_hl;
83239922Sgonzo	bus_space_tag_t		bst;
84239922Sgonzo	bus_space_handle_t	bsh;
85239922Sgonzo	int			msg[BCM2835_MBOX_CHANS];
86290533Sgonzo	int			have_message[BCM2835_MBOX_CHANS];
87290666Sgonzo	struct sx		property_chan_lock;
88239922Sgonzo};
89239922Sgonzo
90253006Srpaulo#define	mbox_read_4(sc, reg)		\
91253006Srpaulo    bus_space_read_4((sc)->bst, (sc)->bsh, reg)
92253006Srpaulo#define	mbox_write_4(sc, reg, val)		\
93253006Srpaulo    bus_space_write_4((sc)->bst, (sc)->bsh, reg, val)
94239922Sgonzo
95307575Sgonzostatic struct ofw_compat_data compat_data[] = {
96307575Sgonzo	{"broadcom,bcm2835-mbox",	1},
97307575Sgonzo	{"brcm,bcm2835-mbox",		1},
98307575Sgonzo	{NULL,				0}
99307575Sgonzo};
100307575Sgonzo
101279153Sandrewstatic int
102279153Sandrewbcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan)
103279153Sandrew{
104279153Sandrew	uint32_t data;
105279153Sandrew	uint32_t msg;
106279153Sandrew	int chan;
107279153Sandrew
108279153Sandrew	msg = mbox_read_4(sc, REG_READ);
109279153Sandrew	dprintf("bcm_mbox_intr: raw data %08x\n", msg);
110279153Sandrew	chan = MBOX_CHAN(msg);
111279153Sandrew	data = MBOX_DATA(msg);
112279153Sandrew	if (sc->msg[chan]) {
113279153Sandrew		printf("bcm_mbox_intr: channel %d oveflow\n", chan);
114279153Sandrew		return (1);
115279153Sandrew	}
116279153Sandrew	dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data);
117279153Sandrew	sc->msg[chan] = msg;
118279153Sandrew
119279153Sandrew	if (ochan != NULL)
120279153Sandrew		*ochan = chan;
121279153Sandrew
122279153Sandrew	return (0);
123279153Sandrew}
124279153Sandrew
125239922Sgonzostatic void
126239922Sgonzobcm_mbox_intr(void *arg)
127239922Sgonzo{
128239922Sgonzo	struct bcm_mbox_softc *sc = arg;
129239922Sgonzo	int chan;
130239922Sgonzo
131290533Sgonzo	MBOX_LOCK(sc);
132279153Sandrew	while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY))
133290666Sgonzo		if (bcm_mbox_read_msg(sc, &chan) == 0) {
134290666Sgonzo			sc->have_message[chan] = 1;
135290533Sgonzo			wakeup(&sc->have_message[chan]);
136290666Sgonzo		}
137290533Sgonzo	MBOX_UNLOCK(sc);
138239922Sgonzo}
139239922Sgonzo
140239922Sgonzostatic int
141239922Sgonzobcm_mbox_probe(device_t dev)
142239922Sgonzo{
143239922Sgonzo
144261410Sian	if (!ofw_bus_status_okay(dev))
145261410Sian		return (ENXIO);
146261410Sian
147307575Sgonzo	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
148307575Sgonzo		return (ENXIO);
149239922Sgonzo
150307575Sgonzo	device_set_desc(dev, "BCM2835 VideoCore Mailbox");
151307575Sgonzo
152307575Sgonzo	return (BUS_PROBE_DEFAULT);
153239922Sgonzo}
154239922Sgonzo
155239922Sgonzostatic int
156239922Sgonzobcm_mbox_attach(device_t dev)
157239922Sgonzo{
158239922Sgonzo	struct bcm_mbox_softc *sc = device_get_softc(dev);
159239922Sgonzo	int i;
160239922Sgonzo	int rid = 0;
161239922Sgonzo
162239922Sgonzo	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
163239922Sgonzo	if (sc->mem_res == NULL) {
164239922Sgonzo		device_printf(dev, "could not allocate memory resource\n");
165239922Sgonzo		return (ENXIO);
166239922Sgonzo	}
167239922Sgonzo
168239922Sgonzo	sc->bst = rman_get_bustag(sc->mem_res);
169239922Sgonzo	sc->bsh = rman_get_bushandle(sc->mem_res);
170239922Sgonzo
171239922Sgonzo	rid = 0;
172239922Sgonzo	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
173239922Sgonzo	if (sc->irq_res == NULL) {
174239922Sgonzo		device_printf(dev, "could not allocate interrupt resource\n");
175239922Sgonzo		return (ENXIO);
176239922Sgonzo	}
177239922Sgonzo
178239922Sgonzo	/* Setup and enable the timer */
179252450Srpaulo	if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
180252450Srpaulo	    NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) {
181252450Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res);
182239922Sgonzo		device_printf(dev, "Unable to setup the clock irq handler.\n");
183239922Sgonzo		return (ENXIO);
184239922Sgonzo	}
185239922Sgonzo
186261884Sbrueffer	mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF);
187239922Sgonzo	for (i = 0; i < BCM2835_MBOX_CHANS; i++) {
188275963Srpaulo		sc->msg[i] = 0;
189290533Sgonzo		sc->have_message[i] = 0;
190239922Sgonzo	}
191239922Sgonzo
192290666Sgonzo	sx_init(&sc->property_chan_lock, "mboxprop");
193290666Sgonzo
194239922Sgonzo	/* Read all pending messages */
195276297Sloos	while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0)
196276297Sloos		(void)mbox_read_4(sc, REG_READ);
197239922Sgonzo
198253006Srpaulo	mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ);
199239922Sgonzo
200239922Sgonzo	return (0);
201239922Sgonzo}
202239922Sgonzo
203239922Sgonzo/*
204239922Sgonzo * Mailbox API
205239922Sgonzo */
206253006Srpaulostatic int
207253006Srpaulobcm_mbox_write(device_t dev, int chan, uint32_t data)
208239922Sgonzo{
209276303Sloos	int limit = 1000;
210253006Srpaulo	struct bcm_mbox_softc *sc = device_get_softc(dev);
211239922Sgonzo
212239922Sgonzo	dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data);
213253006Srpaulo	MBOX_LOCK(sc);
214290533Sgonzo	sc->have_message[chan] = 0;
215276303Sloos	while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit)
216276303Sloos		DELAY(5);
217239922Sgonzo	if (limit == 0) {
218239922Sgonzo		printf("bcm_mbox_write: STATUS_FULL stuck");
219253006Srpaulo		MBOX_UNLOCK(sc);
220239922Sgonzo		return (EAGAIN);
221239922Sgonzo	}
222253006Srpaulo	mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data));
223276303Sloos	MBOX_UNLOCK(sc);
224239922Sgonzo
225239922Sgonzo	return (0);
226239922Sgonzo}
227239922Sgonzo
228253006Srpaulostatic int
229253006Srpaulobcm_mbox_read(device_t dev, int chan, uint32_t *data)
230239922Sgonzo{
231253006Srpaulo	struct bcm_mbox_softc *sc = device_get_softc(dev);
232279153Sandrew	int err, read_chan;
233239922Sgonzo
234239922Sgonzo	dprintf("bcm_mbox_read: chan %d\n", chan);
235279153Sandrew
236279153Sandrew	err = 0;
237253006Srpaulo	MBOX_LOCK(sc);
238279153Sandrew	if (!cold) {
239290533Sgonzo		if (sc->have_message[chan] == 0) {
240290533Sgonzo			if (mtx_sleep(&sc->have_message[chan], &sc->lock, 0,
241290533Sgonzo			    "mbox", 10*hz) != 0) {
242290533Sgonzo				device_printf(dev, "timeout waiting for message on chan %d\n", chan);
243290533Sgonzo				err = ETIMEDOUT;
244290533Sgonzo			}
245279153Sandrew		}
246279153Sandrew	} else {
247279153Sandrew		do {
248279153Sandrew			/* Wait for a message */
249279153Sandrew			while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY))
250279153Sandrew				;
251279153Sandrew			/* Read the message */
252279153Sandrew			if (bcm_mbox_read_msg(sc, &read_chan) != 0) {
253279153Sandrew				err = EINVAL;
254279153Sandrew				goto out;
255279153Sandrew			}
256279153Sandrew		} while (read_chan != chan);
257275963Srpaulo	}
258275963Srpaulo	/*
259275963Srpaulo	 *  get data from intr handler, the same channel is never coming
260275963Srpaulo	 *  because of holding sc lock.
261275963Srpaulo	 */
262275963Srpaulo	*data = MBOX_DATA(sc->msg[chan]);
263275963Srpaulo	sc->msg[chan] = 0;
264290533Sgonzo	sc->have_message[chan] = 0;
265279153Sandrewout:
266253006Srpaulo	MBOX_UNLOCK(sc);
267239922Sgonzo	dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data);
268239922Sgonzo
269279153Sandrew	return (err);
270239922Sgonzo}
271253006Srpaulo
272253006Srpaulostatic device_method_t bcm_mbox_methods[] = {
273253006Srpaulo	DEVMETHOD(device_probe,		bcm_mbox_probe),
274253006Srpaulo	DEVMETHOD(device_attach,	bcm_mbox_attach),
275253006Srpaulo
276253006Srpaulo	DEVMETHOD(mbox_read,		bcm_mbox_read),
277253006Srpaulo	DEVMETHOD(mbox_write,		bcm_mbox_write),
278253006Srpaulo
279253006Srpaulo	DEVMETHOD_END
280253006Srpaulo};
281253006Srpaulo
282253006Srpaulostatic driver_t bcm_mbox_driver = {
283253006Srpaulo	"mbox",
284253006Srpaulo	bcm_mbox_methods,
285253006Srpaulo	sizeof(struct bcm_mbox_softc),
286253006Srpaulo};
287253006Srpaulo
288253006Srpaulostatic devclass_t bcm_mbox_devclass;
289253006Srpaulo
290253006SrpauloDRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0);
291280283Sandrew
292280283Sandrewstatic void
293280283Sandrewbcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
294280283Sandrew{
295280283Sandrew	bus_addr_t *addr;
296280283Sandrew
297280283Sandrew	if (err)
298280283Sandrew		return;
299280283Sandrew	addr = (bus_addr_t *)arg;
300280283Sandrew	*addr = PHYS_TO_VCBUS(segs[0].ds_addr);
301280283Sandrew}
302280283Sandrew
303280294Sandrewstatic void *
304280294Sandrewbcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag,
305280294Sandrew    bus_dmamap_t *map, bus_addr_t *phys)
306280283Sandrew{
307280294Sandrew	void *buf;
308280283Sandrew	int err;
309280283Sandrew
310280283Sandrew	err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0,
311280283Sandrew	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
312280294Sandrew	    len, 1, len, 0, NULL, NULL, tag);
313280283Sandrew	if (err != 0) {
314280283Sandrew		device_printf(dev, "can't create DMA tag\n");
315280294Sandrew		return (NULL);
316280283Sandrew	}
317280283Sandrew
318280294Sandrew	err = bus_dmamem_alloc(*tag, &buf, 0, map);
319280283Sandrew	if (err != 0) {
320280294Sandrew		bus_dma_tag_destroy(*tag);
321280283Sandrew		device_printf(dev, "can't allocate dmamem\n");
322280294Sandrew		return (NULL);
323280283Sandrew	}
324280283Sandrew
325282049Sloos	err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb,
326280294Sandrew	    phys, 0);
327280283Sandrew	if (err != 0) {
328280294Sandrew		bus_dmamem_free(*tag, buf, *map);
329280294Sandrew		bus_dma_tag_destroy(*tag);
330280283Sandrew		device_printf(dev, "can't load DMA map\n");
331280294Sandrew		return (NULL);
332280294Sandrew	}
333280294Sandrew
334280294Sandrew	return (buf);
335280294Sandrew}
336280294Sandrew
337282357Sloosstatic int
338282357Sloosbcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys,
339282357Sloos	struct bcm2835_mbox_hdr *msg, size_t len)
340282357Sloos{
341282357Sloos	int idx;
342282357Sloos	struct bcm2835_mbox_tag_hdr *tag;
343282357Sloos	uint8_t *last;
344282357Sloos
345282357Sloos	if ((uint32_t)msg_phys != resp_phys) {
346282357Sloos		device_printf(dev, "response channel mismatch\n");
347282357Sloos		return (EIO);
348282357Sloos	}
349282357Sloos	if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) {
350282357Sloos		device_printf(dev, "mbox response error\n");
351282357Sloos		return (EIO);
352282357Sloos	}
353282357Sloos
354282357Sloos	/* Loop until the end tag. */
355282357Sloos	tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1);
356282357Sloos	last = (uint8_t *)msg + len;
357282357Sloos	for (idx = 0; tag->tag != 0; idx++) {
358282357Sloos		if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) {
359282357Sloos			device_printf(dev, "tag %d response error\n", idx);
360282357Sloos			return (EIO);
361282357Sloos		}
362282357Sloos		/* Clear the response bit. */
363282357Sloos		tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
364282357Sloos
365282357Sloos		/* Next tag. */
366282357Sloos		tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag +
367282357Sloos		    sizeof(*tag) + tag->val_buf_size);
368282357Sloos
369282357Sloos		if ((uint8_t *)tag > last) {
370282357Sloos			device_printf(dev, "mbox buffer size error\n");
371282357Sloos			return (EIO);
372282357Sloos		}
373282357Sloos	}
374282357Sloos
375282357Sloos	return (0);
376282357Sloos}
377282357Sloos
378280294Sandrewint
379290381Sgonzobcm2835_mbox_property(void *msg, size_t msg_size)
380280294Sandrew{
381290666Sgonzo	struct bcm_mbox_softc *sc;
382290381Sgonzo	struct msg_set_power_state *buf;
383280294Sandrew	bus_dma_tag_t msg_tag;
384280294Sandrew	bus_dmamap_t msg_map;
385280294Sandrew	bus_addr_t msg_phys;
386280294Sandrew	uint32_t reg;
387280294Sandrew	device_t mbox;
388290381Sgonzo	int err;
389280294Sandrew
390280294Sandrew	/* get mbox device */
391280294Sandrew	mbox = devclass_get_device(devclass_find("mbox"), 0);
392290381Sgonzo	if (mbox == NULL)
393280283Sandrew		return (ENXIO);
394280283Sandrew
395290666Sgonzo	sc = device_get_softc(mbox);
396290666Sgonzo	sx_xlock(&sc->property_chan_lock);
397290666Sgonzo
398280294Sandrew	/* Allocate memory for the message */
399290381Sgonzo	buf = bcm2835_mbox_init_dma(mbox, msg_size, &msg_tag, &msg_map,
400280294Sandrew	    &msg_phys);
401290666Sgonzo	if (buf == NULL) {
402290666Sgonzo		err = ENOMEM;
403290666Sgonzo		goto out;
404290666Sgonzo	}
405280283Sandrew
406290381Sgonzo	memcpy(buf, msg, msg_size);
407280283Sandrew
408280283Sandrew	bus_dmamap_sync(msg_tag, msg_map,
409290381Sgonzo	    BUS_DMASYNC_PREWRITE);
410280283Sandrew
411280283Sandrew	MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys);
412280283Sandrew	MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &reg);
413280283Sandrew
414290381Sgonzo	bus_dmamap_sync(msg_tag, msg_map,
415290381Sgonzo	    BUS_DMASYNC_PREREAD);
416290381Sgonzo
417290381Sgonzo	memcpy(msg, buf, msg_size);
418290381Sgonzo
419290381Sgonzo	err = bcm2835_mbox_err(mbox, msg_phys, reg,
420290381Sgonzo	    (struct bcm2835_mbox_hdr *)msg, msg_size);
421290381Sgonzo
422280283Sandrew	bus_dmamap_unload(msg_tag, msg_map);
423290381Sgonzo	bus_dmamem_free(msg_tag, buf, msg_map);
424280283Sandrew	bus_dma_tag_destroy(msg_tag);
425290666Sgonzoout:
426290666Sgonzo	sx_xunlock(&sc->property_chan_lock);
427290381Sgonzo	return (err);
428280283Sandrew}
429280294Sandrew
430280294Sandrewint
431290381Sgonzobcm2835_mbox_set_power_state(uint32_t device_id, boolean_t on)
432280294Sandrew{
433290381Sgonzo	struct msg_set_power_state msg;
434290381Sgonzo	int err;
435280294Sandrew
436290381Sgonzo	memset(&msg, 0, sizeof(msg));
437290381Sgonzo	msg.hdr.buf_size = sizeof(msg);
438290381Sgonzo	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
439290381Sgonzo	msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE;
440290381Sgonzo	msg.tag_hdr.val_buf_size = sizeof(msg.body);
441290381Sgonzo	msg.tag_hdr.val_len = sizeof(msg.body.req);
442290381Sgonzo	msg.body.req.device_id = device_id;
443290381Sgonzo	msg.body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) |
444290381Sgonzo	    BCM2835_MBOX_POWER_WAIT;
445290381Sgonzo	msg.end_tag = 0;
446280294Sandrew
447290381Sgonzo	err = bcm2835_mbox_property(&msg, sizeof(msg));
448280294Sandrew
449290381Sgonzo	return (err);
450290381Sgonzo}
451280294Sandrew
452290381Sgonzoint
453290381Sgonzobcm2835_mbox_get_clock_rate(uint32_t clock_id, uint32_t *hz)
454290381Sgonzo{
455290381Sgonzo	struct msg_get_clock_rate msg;
456290381Sgonzo	int err;
457280294Sandrew
458290381Sgonzo	memset(&msg, 0, sizeof(msg));
459290381Sgonzo	msg.hdr.buf_size = sizeof(msg);
460290381Sgonzo	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
461290381Sgonzo	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE;
462290381Sgonzo	msg.tag_hdr.val_buf_size = sizeof(msg.body);
463290381Sgonzo	msg.tag_hdr.val_len = sizeof(msg.body.req);
464290381Sgonzo	msg.body.req.clock_id = clock_id;
465290381Sgonzo	msg.end_tag = 0;
466280294Sandrew
467290381Sgonzo	err = bcm2835_mbox_property(&msg, sizeof(msg));
468290381Sgonzo	*hz = msg.body.resp.rate_hz;
469280294Sandrew
470290381Sgonzo	return (err);
471280294Sandrew}
472282357Sloos
473282357Sloosint
474290381Sgonzobcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *fb)
475282357Sloos{
476282357Sloos	int err;
477290381Sgonzo	struct msg_fb_get_w_h msg;
478282357Sloos
479290381Sgonzo	memset(&msg, 0, sizeof(msg));
480290381Sgonzo	msg.hdr.buf_size = sizeof(msg);
481290381Sgonzo	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
482290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, GET_PHYSICAL_W_H);
483290381Sgonzo	msg.physical_w_h.tag_hdr.val_len = 0;
484290381Sgonzo	msg.end_tag = 0;
485282357Sloos
486290381Sgonzo	err = bcm2835_mbox_property(&msg, sizeof(msg));
487282357Sloos	if (err == 0) {
488290381Sgonzo		fb->xres = msg.physical_w_h.body.resp.width;
489290381Sgonzo		fb->yres = msg.physical_w_h.body.resp.height;
490282357Sloos	}
491282357Sloos
492282357Sloos	return (err);
493282357Sloos}
494282357Sloos
495282357Sloosint
496290381Sgonzobcm2835_mbox_fb_init(struct bcm2835_fb_config *fb)
497282357Sloos{
498282357Sloos	int err;
499290381Sgonzo	struct msg_fb_setup msg;
500282357Sloos
501290381Sgonzo	memset(&msg, 0, sizeof(msg));
502290381Sgonzo	msg.hdr.buf_size = sizeof(msg);
503290381Sgonzo	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
504290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, SET_PHYSICAL_W_H);
505290381Sgonzo	msg.physical_w_h.body.req.width = fb->xres;
506290381Sgonzo	msg.physical_w_h.body.req.height = fb->yres;
507290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, SET_VIRTUAL_W_H);
508290381Sgonzo	msg.virtual_w_h.body.req.width = fb->vxres;
509290381Sgonzo	msg.virtual_w_h.body.req.height = fb->vyres;
510298428Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.offset, SET_VIRTUAL_OFFSET);
511290381Sgonzo	msg.offset.body.req.x = fb->xoffset;
512290381Sgonzo	msg.offset.body.req.y = fb->yoffset;
513290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.depth, SET_DEPTH);
514290381Sgonzo	msg.depth.body.req.bpp = fb->bpp;
515290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.alpha, SET_ALPHA_MODE);
516290381Sgonzo	msg.alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED;
517290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.buffer, ALLOCATE_BUFFER);
518290381Sgonzo	msg.buffer.body.req.alignment = PAGE_SIZE;
519290381Sgonzo	BCM2835_MBOX_INIT_TAG(&msg.pitch, GET_PITCH);
520290381Sgonzo	msg.end_tag = 0;
521282357Sloos
522290381Sgonzo	err = bcm2835_mbox_property(&msg, sizeof(msg));
523282357Sloos	if (err == 0) {
524290381Sgonzo		fb->xres = msg.physical_w_h.body.resp.width;
525290381Sgonzo		fb->yres = msg.physical_w_h.body.resp.height;
526290381Sgonzo		fb->vxres = msg.virtual_w_h.body.resp.width;
527290381Sgonzo		fb->vyres = msg.virtual_w_h.body.resp.height;
528290381Sgonzo		fb->xoffset = msg.offset.body.resp.x;
529290381Sgonzo		fb->yoffset = msg.offset.body.resp.y;
530290381Sgonzo		fb->pitch = msg.pitch.body.resp.pitch;
531290381Sgonzo		fb->base = VCBUS_TO_PHYS(msg.buffer.body.resp.fb_address);
532290381Sgonzo		fb->size = msg.buffer.body.resp.fb_size;
533282357Sloos	}
534282357Sloos
535282357Sloos	return (err);
536282357Sloos}
537