]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/broadcom/bcm2835/bcm2835_mbox.c
Import lua 5.3.4 to contrib
[FreeBSD/FreeBSD.git] / sys / arm / broadcom / bcm2835 / bcm2835_mbox.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/module.h>
38 #include <sys/mutex.h>
39 #include <sys/sx.h>
40 #include <sys/rman.h>
41 #include <machine/bus.h>
42
43 #include <dev/ofw/ofw_bus.h>
44 #include <dev/ofw/ofw_bus_subr.h>
45
46 #include <arm/broadcom/bcm2835/bcm2835_mbox.h>
47 #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
48 #include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
49
50 #include "mbox_if.h"
51
52 #define REG_READ        0x00
53 #define REG_POL         0x10
54 #define REG_SENDER      0x14
55 #define REG_STATUS      0x18
56 #define         STATUS_FULL     0x80000000
57 #define         STATUS_EMPTY    0x40000000
58 #define REG_CONFIG      0x1C
59 #define         CONFIG_DATA_IRQ 0x00000001
60 #define REG_WRITE       0x20 /* This is Mailbox 1 address */
61
62 #define MBOX_MSG(chan, data)    (((data) & ~0xf) | ((chan) & 0xf))
63 #define MBOX_CHAN(msg)          ((msg) & 0xf)
64 #define MBOX_DATA(msg)          ((msg) & ~0xf)
65
66 #define MBOX_LOCK(sc)   do {    \
67         mtx_lock(&(sc)->lock);  \
68 } while(0)
69
70 #define MBOX_UNLOCK(sc) do {            \
71         mtx_unlock(&(sc)->lock);        \
72 } while(0)
73
74 #ifdef  DEBUG
75 #define dprintf(fmt, args...) printf(fmt, ##args)
76 #else
77 #define dprintf(fmt, args...)
78 #endif
79
80 struct bcm_mbox_softc {
81         struct mtx              lock;
82         struct resource *       mem_res;
83         struct resource *       irq_res;
84         void*                   intr_hl;
85         bus_space_tag_t         bst;
86         bus_space_handle_t      bsh;
87         int                     msg[BCM2835_MBOX_CHANS];
88         int                     have_message[BCM2835_MBOX_CHANS];
89         struct sx               property_chan_lock;
90 };
91
92 #define mbox_read_4(sc, reg)            \
93     bus_space_read_4((sc)->bst, (sc)->bsh, reg)
94 #define mbox_write_4(sc, reg, val)              \
95     bus_space_write_4((sc)->bst, (sc)->bsh, reg, val)
96
97 static struct ofw_compat_data compat_data[] = {
98         {"broadcom,bcm2835-mbox",       1},
99         {"brcm,bcm2835-mbox",           1},
100         {NULL,                          0}
101 };
102
103 static int
104 bcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan)
105 {
106         uint32_t data;
107         uint32_t msg;
108         int chan;
109
110         msg = mbox_read_4(sc, REG_READ);
111         dprintf("bcm_mbox_intr: raw data %08x\n", msg);
112         chan = MBOX_CHAN(msg);
113         data = MBOX_DATA(msg);
114         if (sc->msg[chan]) {
115                 printf("bcm_mbox_intr: channel %d oveflow\n", chan);
116                 return (1);
117         }
118         dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data);
119         sc->msg[chan] = msg;
120
121         if (ochan != NULL)
122                 *ochan = chan;
123
124         return (0);
125 }
126
127 static void
128 bcm_mbox_intr(void *arg)
129 {
130         struct bcm_mbox_softc *sc = arg;
131         int chan;
132
133         MBOX_LOCK(sc);
134         while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY))
135                 if (bcm_mbox_read_msg(sc, &chan) == 0) {
136                         sc->have_message[chan] = 1;
137                         wakeup(&sc->have_message[chan]);
138                 }
139         MBOX_UNLOCK(sc);
140 }
141
142 static int
143 bcm_mbox_probe(device_t dev)
144 {
145
146         if (!ofw_bus_status_okay(dev))
147                 return (ENXIO);
148
149         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
150                 return (ENXIO);
151
152         device_set_desc(dev, "BCM2835 VideoCore Mailbox");
153
154         return (BUS_PROBE_DEFAULT);
155 }
156
157 static int
158 bcm_mbox_attach(device_t dev)
159 {
160         struct bcm_mbox_softc *sc = device_get_softc(dev);
161         int i;
162         int rid = 0;
163
164         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
165         if (sc->mem_res == NULL) {
166                 device_printf(dev, "could not allocate memory resource\n");
167                 return (ENXIO);
168         }
169
170         sc->bst = rman_get_bustag(sc->mem_res);
171         sc->bsh = rman_get_bushandle(sc->mem_res);
172
173         rid = 0;
174         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
175         if (sc->irq_res == NULL) {
176                 device_printf(dev, "could not allocate interrupt resource\n");
177                 return (ENXIO);
178         }
179
180         /* Setup and enable the timer */
181         if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 
182             NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) {
183                 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res);
184                 device_printf(dev, "Unable to setup the clock irq handler.\n");
185                 return (ENXIO);
186         }
187
188         mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF);
189         for (i = 0; i < BCM2835_MBOX_CHANS; i++) {
190                 sc->msg[i] = 0;
191                 sc->have_message[i] = 0;
192         }
193
194         sx_init(&sc->property_chan_lock, "mboxprop");
195
196         /* Read all pending messages */
197         while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0)
198                 (void)mbox_read_4(sc, REG_READ);
199
200         mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ);
201
202         return (0);
203 }
204
205 /* 
206  * Mailbox API
207  */
208 static int
209 bcm_mbox_write(device_t dev, int chan, uint32_t data)
210 {
211         int limit = 1000;
212         struct bcm_mbox_softc *sc = device_get_softc(dev);
213
214         dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data);
215         MBOX_LOCK(sc);
216         sc->have_message[chan] = 0;
217         while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit)
218                 DELAY(5);
219         if (limit == 0) {
220                 printf("bcm_mbox_write: STATUS_FULL stuck");
221                 MBOX_UNLOCK(sc);
222                 return (EAGAIN);
223         }
224         mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data));
225         MBOX_UNLOCK(sc);
226
227         return (0);
228 }
229
230 static int
231 bcm_mbox_read(device_t dev, int chan, uint32_t *data)
232 {
233         struct bcm_mbox_softc *sc = device_get_softc(dev);
234         int err, read_chan;
235
236         dprintf("bcm_mbox_read: chan %d\n", chan);
237
238         err = 0;
239         MBOX_LOCK(sc);
240         if (!cold) {
241                 if (sc->have_message[chan] == 0) {
242                         if (mtx_sleep(&sc->have_message[chan], &sc->lock, 0,
243                             "mbox", 10*hz) != 0) {
244                                 device_printf(dev, "timeout waiting for message on chan %d\n", chan);
245                                 err = ETIMEDOUT;
246                         }
247                 }
248         } else {
249                 do {
250                         /* Wait for a message */
251                         while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY))
252                                 ;
253                         /* Read the message */
254                         if (bcm_mbox_read_msg(sc, &read_chan) != 0) {
255                                 err = EINVAL;
256                                 goto out;
257                         }
258                 } while (read_chan != chan);
259         }
260         /*
261          *  get data from intr handler, the same channel is never coming
262          *  because of holding sc lock.
263          */
264         *data = MBOX_DATA(sc->msg[chan]);
265         sc->msg[chan] = 0;
266         sc->have_message[chan] = 0;
267 out:
268         MBOX_UNLOCK(sc);
269         dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data);
270
271         return (err);
272 }
273
274 static device_method_t bcm_mbox_methods[] = {
275         DEVMETHOD(device_probe,         bcm_mbox_probe),
276         DEVMETHOD(device_attach,        bcm_mbox_attach),
277
278         DEVMETHOD(mbox_read,            bcm_mbox_read),
279         DEVMETHOD(mbox_write,           bcm_mbox_write),
280
281         DEVMETHOD_END
282 };
283
284 static driver_t bcm_mbox_driver = {
285         "mbox",
286         bcm_mbox_methods,
287         sizeof(struct bcm_mbox_softc),
288 };
289
290 static devclass_t bcm_mbox_devclass;
291
292 DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0);
293
294 static void
295 bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
296 {
297         bus_addr_t *addr;
298
299         if (err)
300                 return;
301         addr = (bus_addr_t *)arg;
302         *addr = PHYS_TO_VCBUS(segs[0].ds_addr);
303 }
304
305 static void *
306 bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag,
307     bus_dmamap_t *map, bus_addr_t *phys)
308 {
309         void *buf;
310         int err;
311
312         err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0,
313             BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
314             len, 1, len, 0, NULL, NULL, tag);
315         if (err != 0) {
316                 device_printf(dev, "can't create DMA tag\n");
317                 return (NULL);
318         }
319
320         err = bus_dmamem_alloc(*tag, &buf, 0, map);
321         if (err != 0) {
322                 bus_dma_tag_destroy(*tag);
323                 device_printf(dev, "can't allocate dmamem\n");
324                 return (NULL);
325         }
326
327         err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb,
328             phys, 0);
329         if (err != 0) {
330                 bus_dmamem_free(*tag, buf, *map);
331                 bus_dma_tag_destroy(*tag);
332                 device_printf(dev, "can't load DMA map\n");
333                 return (NULL);
334         }
335
336         return (buf);
337 }
338
339 static int
340 bcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys,
341         struct bcm2835_mbox_hdr *msg, size_t len)
342 {
343         int idx;
344         struct bcm2835_mbox_tag_hdr *tag;
345         uint8_t *last;
346
347         if ((uint32_t)msg_phys != resp_phys) {
348                 device_printf(dev, "response channel mismatch\n");
349                 return (EIO);
350         }
351         if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) {
352                 device_printf(dev, "mbox response error\n");
353                 return (EIO);
354         }
355
356         /* Loop until the end tag. */
357         tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1);
358         last = (uint8_t *)msg + len;
359         for (idx = 0; tag->tag != 0; idx++) {
360                 if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) {
361                         device_printf(dev, "tag %d response error\n", idx);
362                         return (EIO);
363                 }
364                 /* Clear the response bit. */
365                 tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
366
367                 /* Next tag. */
368                 tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag +
369                     sizeof(*tag) + tag->val_buf_size);
370
371                 if ((uint8_t *)tag > last) {
372                         device_printf(dev, "mbox buffer size error\n");
373                         return (EIO);
374                 }
375         }
376
377         return (0);
378 }
379
380 int
381 bcm2835_mbox_property(void *msg, size_t msg_size)
382 {
383         struct bcm_mbox_softc *sc;
384         struct msg_set_power_state *buf;
385         bus_dma_tag_t msg_tag;
386         bus_dmamap_t msg_map;
387         bus_addr_t msg_phys;
388         uint32_t reg;
389         device_t mbox;
390         int err;
391
392         /* get mbox device */
393         mbox = devclass_get_device(devclass_find("mbox"), 0);
394         if (mbox == NULL)
395                 return (ENXIO);
396
397         sc = device_get_softc(mbox);
398         sx_xlock(&sc->property_chan_lock);
399
400         /* Allocate memory for the message */
401         buf = bcm2835_mbox_init_dma(mbox, msg_size, &msg_tag, &msg_map,
402             &msg_phys);
403         if (buf == NULL) {
404                 err = ENOMEM;
405                 goto out;
406         }
407
408         memcpy(buf, msg, msg_size);
409
410         bus_dmamap_sync(msg_tag, msg_map,
411             BUS_DMASYNC_PREWRITE);
412
413         MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys);
414         MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &reg);
415
416         bus_dmamap_sync(msg_tag, msg_map,
417             BUS_DMASYNC_PREREAD);
418
419         memcpy(msg, buf, msg_size);
420
421         err = bcm2835_mbox_err(mbox, msg_phys, reg,
422             (struct bcm2835_mbox_hdr *)msg, msg_size);
423
424         bus_dmamap_unload(msg_tag, msg_map);
425         bus_dmamem_free(msg_tag, buf, msg_map);
426         bus_dma_tag_destroy(msg_tag);
427 out:
428         sx_xunlock(&sc->property_chan_lock);
429         return (err);
430 }
431
432 int
433 bcm2835_mbox_set_power_state(uint32_t device_id, boolean_t on)
434 {
435         struct msg_set_power_state msg;
436         int err;
437
438         memset(&msg, 0, sizeof(msg));
439         msg.hdr.buf_size = sizeof(msg);
440         msg.hdr.code = BCM2835_MBOX_CODE_REQ;
441         msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE;
442         msg.tag_hdr.val_buf_size = sizeof(msg.body);
443         msg.tag_hdr.val_len = sizeof(msg.body.req);
444         msg.body.req.device_id = device_id;
445         msg.body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) |
446             BCM2835_MBOX_POWER_WAIT;
447         msg.end_tag = 0;
448
449         err = bcm2835_mbox_property(&msg, sizeof(msg));
450
451         return (err);
452 }
453
454 int
455 bcm2835_mbox_get_clock_rate(uint32_t clock_id, uint32_t *hz)
456 {
457         struct msg_get_clock_rate msg;
458         int err;
459
460         memset(&msg, 0, sizeof(msg));
461         msg.hdr.buf_size = sizeof(msg);
462         msg.hdr.code = BCM2835_MBOX_CODE_REQ;
463         msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE;
464         msg.tag_hdr.val_buf_size = sizeof(msg.body);
465         msg.tag_hdr.val_len = sizeof(msg.body.req);
466         msg.body.req.clock_id = clock_id;
467         msg.end_tag = 0;
468
469         err = bcm2835_mbox_property(&msg, sizeof(msg));
470         *hz = msg.body.resp.rate_hz;
471
472         return (err);
473 }
474
475 int
476 bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *fb)
477 {
478         int err;
479         struct msg_fb_get_w_h msg;
480
481         memset(&msg, 0, sizeof(msg));
482         msg.hdr.buf_size = sizeof(msg);
483         msg.hdr.code = BCM2835_MBOX_CODE_REQ;
484         BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, GET_PHYSICAL_W_H);
485         msg.physical_w_h.tag_hdr.val_len = 0;
486         msg.end_tag = 0;
487
488         err = bcm2835_mbox_property(&msg, sizeof(msg));
489         if (err == 0) {
490                 fb->xres = msg.physical_w_h.body.resp.width;
491                 fb->yres = msg.physical_w_h.body.resp.height;
492         }
493
494         return (err);
495 }
496
497 int
498 bcm2835_mbox_fb_init(struct bcm2835_fb_config *fb)
499 {
500         int err;
501         struct msg_fb_setup msg;
502
503         memset(&msg, 0, sizeof(msg));
504         msg.hdr.buf_size = sizeof(msg);
505         msg.hdr.code = BCM2835_MBOX_CODE_REQ;
506         BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, SET_PHYSICAL_W_H);
507         msg.physical_w_h.body.req.width = fb->xres;
508         msg.physical_w_h.body.req.height = fb->yres;
509         BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, SET_VIRTUAL_W_H);
510         msg.virtual_w_h.body.req.width = fb->vxres;
511         msg.virtual_w_h.body.req.height = fb->vyres;
512         BCM2835_MBOX_INIT_TAG(&msg.offset, SET_VIRTUAL_OFFSET);
513         msg.offset.body.req.x = fb->xoffset;
514         msg.offset.body.req.y = fb->yoffset;
515         BCM2835_MBOX_INIT_TAG(&msg.depth, SET_DEPTH);
516         msg.depth.body.req.bpp = fb->bpp;
517         BCM2835_MBOX_INIT_TAG(&msg.alpha, SET_ALPHA_MODE);
518         msg.alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED;
519         BCM2835_MBOX_INIT_TAG(&msg.buffer, ALLOCATE_BUFFER);
520         msg.buffer.body.req.alignment = PAGE_SIZE;
521         BCM2835_MBOX_INIT_TAG(&msg.pitch, GET_PITCH);
522         msg.end_tag = 0;
523
524         err = bcm2835_mbox_property(&msg, sizeof(msg));
525         if (err == 0) {
526                 fb->xres = msg.physical_w_h.body.resp.width;
527                 fb->yres = msg.physical_w_h.body.resp.height;
528                 fb->vxres = msg.virtual_w_h.body.resp.width;
529                 fb->vyres = msg.virtual_w_h.body.resp.height;
530                 fb->xoffset = msg.offset.body.resp.x;
531                 fb->yoffset = msg.offset.body.resp.y;
532                 fb->pitch = msg.pitch.body.resp.pitch;
533                 fb->base = VCBUS_TO_PHYS(msg.buffer.body.resp.fb_address);
534                 fb->size = msg.buffer.body.resp.fb_size;
535         }
536
537         return (err);
538 }