Index: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.h =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.h (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.h (nonexistent) @@ -1,46 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2013 Oleksandr Tymoshenko - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _BCM2835_GPIO_H_ -#define _BCM2835_GPIO_H_ - -enum bcm_gpio_fsel { - BCM_GPIO_INPUT, - BCM_GPIO_OUTPUT, - BCM_GPIO_ALT5, - BCM_GPIO_ALT4, - BCM_GPIO_ALT0, - BCM_GPIO_ALT1, - BCM_GPIO_ALT2, - BCM_GPIO_ALT3, -}; - -void bcm_gpio_set_alternate(device_t, uint32_t, uint32_t); - -#endif /* _BCM2835_GPIO_H_ */ Property changes on: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/broadcom/bcm2835/bcm2835_bsc.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 332262) @@ -1,753 +1,725 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Tsubai Masanari. * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * Copyright (c) 2017 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* * Driver for bcm2835 i2c-compatible two-wire bus, named 'BSC' on this SoC. * * This controller can only perform complete transfers, it does not provide * low-level control over sending start/repeat-start/stop sequences on the bus. * In addition, bugs in the silicon make it somewhat difficult to perform a * repeat-start, and limit the repeat-start to a read following a write on * the same slave device. (The i2c protocol allows a repeat start to change * direction or not, and change slave address or not at any time.) * * The repeat-start bug and workaround are described in a problem report at * https://github.com/raspberrypi/linux/issues/254 with the crucial part being * in a comment block from a fragment of a GPU i2c driver, containing this: * * ----------------------------------------------------------------------------- * - See i2c.v: The I2C peripheral samples the values for rw_bit and xfer_count * - in the IDLE state if start is set. * - * - We want to generate a ReSTART not a STOP at the end of the TX phase. In * - order to do that we must ensure the state machine goes RACK1 -> RACK2 -> * - SRSTRT1 (not RACK1 -> RACK2 -> SSTOP1). * - * - So, in the RACK2 state when (TX) xfer_count==0 we must therefore have * - already set, ready to be sampled: * - READ ; rw_bit <= I2CC bit 0 -- must be "read" * - ST; start <= I2CC bit 7 -- must be "Go" in order to not issue STOP * - DLEN; xfer_count <= I2CDLEN -- must be equal to our read amount * - * - The plan to do this is: * - 1. Start the sub-address write, but don't let it finish * - (keep xfer_count > 0) * - 2. Populate READ, DLEN and ST in preparation for ReSTART read sequence * - 3. Let TX finish (write the rest of the data) * - 4. Read back data as it arrives * ----------------------------------------------------------------------------- * * The transfer function below scans the list of messages passed to it, looking * for a read following a write to the same slave. When it finds that, it * starts the write without prefilling the tx fifo, which holds xfer_count>0, * then presets the direction, length, and start command for the following read, * as described above. Then the tx fifo is filled and the rest of the transfer * proceeds as normal, with the controller automatically supplying a * repeat-start on the bus when the write operation finishes. * * XXX I suspect the controller may be able to do a repeat-start on any * write->read or write->write transition, even when the slave addresses differ. * It's unclear whether the slave address can be prestaged along with the * direction and length while the write xfer_count is being held at zero. In * fact, if it can't do this, then it couldn't be used to read EDID data. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include "iicbus_if.h" static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-bsc", 1}, {"brcm,bcm2708-i2c", 1}, {"brcm,bcm2835-i2c", 1}, {NULL, 0} }; #define DEVICE_DEBUGF(sc, lvl, fmt, args...) \ if ((lvl) <= (sc)->sc_debug) \ device_printf((sc)->sc_dev, fmt, ##args) #define DEBUGF(sc, lvl, fmt, args...) \ if ((lvl) <= (sc)->sc_debug) \ printf(fmt, ##args) static void bcm_bsc_intr(void *); static int bcm_bsc_detach(device_t); static void bcm_bsc_modifyreg(struct bcm_bsc_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_BSC_READ(sc, off); reg &= ~mask; reg |= value; BCM_BSC_WRITE(sc, off, reg); } static int bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); BCM_BSC_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 32768; clk = BCM_BSC_CORE_CLK / clk; return (sysctl_handle_int(oidp, &clk, 0, req)); } static int bcm_bsc_clkt_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clkt; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clkt = BCM_BSC_READ(sc, BCM_BSC_CLKT); BCM_BSC_UNLOCK(sc); clkt &= 0xffff; error = sysctl_handle_int(oidp, &clkt, sizeof(clkt), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); BCM_BSC_WRITE(sc, BCM_BSC_CLKT, clkt & 0xffff); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_fall_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg >>= 16; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff0000, reg << 16); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_rise_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg &= 0xffff; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff, reg); BCM_BSC_UNLOCK(sc); return (0); } static void bcm_bsc_sysctl_init(struct bcm_bsc_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "frequency", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clock_proc, "IU", "I2C BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock_stretch", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clkt_proc, "IU", "I2C BUS clock stretch timeout"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "fall_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_fall_proc, "IU", "I2C BUS falling edge delay"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "rise_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_rise_proc, "IU", "I2C BUS rising edge delay"); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "debug", CTLFLAG_RWTUN, &sc->sc_debug, 0, "Enable debug; 1=reads/writes, 2=add starts/stops"); } static void bcm_bsc_reset(struct bcm_bsc_softc *sc) { /* Enable the BSC Controller, disable interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN); /* Clear pending interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_STATUS, BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR | BCM_BSC_STATUS_DONE); /* Clear the FIFO. */ bcm_bsc_modifyreg(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_CLEAR0, BCM_BSC_CTRL_CLEAR0); } static int bcm_bsc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 BSC controller"); return (BUS_PROBE_DEFAULT); } static int bcm_bsc_attach(device_t dev) { struct bcm_bsc_softc *sc; - unsigned long start; - device_t gpio; - int i, rid; + int rid; sc = device_get_softc(dev); sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); - - /* Check the unit we are attaching by its base address. */ - start = rman_get_start(sc->sc_mem_res); - for (i = 0; i < nitems(bcm_bsc_pins); i++) { - if (bcm_bsc_pins[i].start == (start & BCM_BSC_BASE_MASK)) - break; - } - if (i == nitems(bcm_bsc_pins)) { - device_printf(dev, "only bsc0 and bsc1 are supported\n"); - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); - return (ENXIO); - } - - /* - * Configure the GPIO pins to ALT0 function to enable BSC control - * over the pins. - */ - gpio = devclass_get_device(devclass_find("gpio"), 0); - if (!gpio) { - device_printf(dev, "cannot find gpio0\n"); - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); - return (ENXIO); - } - bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].sda, BCM_GPIO_ALT0); - bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].scl, BCM_GPIO_ALT0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_bsc_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_bsc", NULL, MTX_DEF); bcm_bsc_sysctl_init(sc); /* Enable the BSC controller. Flush the FIFO. */ BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); BCM_BSC_UNLOCK(sc); sc->sc_iicbus = device_add_child(dev, "iicbus", -1); if (sc->sc_iicbus == NULL) { bcm_bsc_detach(dev); return (ENXIO); } /* Probe and attach the iicbus when interrupts are available. */ config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev); return (0); } static int bcm_bsc_detach(device_t dev) { struct bcm_bsc_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); if (sc->sc_iicbus != NULL) device_delete_child(dev, sc->sc_iicbus); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_bsc_empty_rx_fifo(struct bcm_bsc_softc *sc) { uint32_t status; /* Assumes sc_totlen > 0 and BCM_BSC_STATUS_RXD is asserted on entry. */ do { if (sc->sc_resid == 0) { sc->sc_data = sc->sc_curmsg->buf; sc->sc_dlen = sc->sc_curmsg->len; sc->sc_resid = sc->sc_dlen; ++sc->sc_curmsg; } do { *sc->sc_data = BCM_BSC_READ(sc, BCM_BSC_DATA); DEBUGF(sc, 1, "0x%02x ", *sc->sc_data); ++sc->sc_data; --sc->sc_resid; --sc->sc_totlen; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_RXD)); } while (sc->sc_totlen > 0 && (status & BCM_BSC_STATUS_RXD)); } static void bcm_bsc_fill_tx_fifo(struct bcm_bsc_softc *sc) { uint32_t status; /* Assumes sc_totlen > 0 and BCM_BSC_STATUS_TXD is asserted on entry. */ do { if (sc->sc_resid == 0) { sc->sc_data = sc->sc_curmsg->buf; sc->sc_dlen = sc->sc_curmsg->len; sc->sc_resid = sc->sc_dlen; ++sc->sc_curmsg; } do { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data); DEBUGF(sc, 1, "0x%02x ", *sc->sc_data); ++sc->sc_data; --sc->sc_resid; --sc->sc_totlen; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)); /* * If a repeat-start was pending and we just hit the end of a tx * buffer, see if it's also the end of the writes that preceeded * the repeat-start. If so, log the repeat-start and the start * of the following read, and return because we're not writing * anymore (and TXD will be true because there's room to write * in the fifo). */ if (sc->sc_replen > 0 && sc->sc_resid == 0) { sc->sc_replen -= sc->sc_dlen; if (sc->sc_replen == 0) { DEBUGF(sc, 1, " err=0\n"); DEVICE_DEBUGF(sc, 2, "rstart 0x%02x\n", sc->sc_curmsg->slave | 0x01); DEVICE_DEBUGF(sc, 1, "read 0x%02x len %d: ", sc->sc_curmsg->slave | 0x01, sc->sc_totlen); sc->sc_flags |= BCM_I2C_READ; return; } } } while (sc->sc_totlen > 0 && (status & BCM_BSC_STATUS_TXD)); } static void bcm_bsc_intr(void *arg) { struct bcm_bsc_softc *sc; uint32_t status; sc = (struct bcm_bsc_softc *)arg; BCM_BSC_LOCK(sc); /* The I2C interrupt is shared among all the BSC controllers. */ if ((sc->sc_flags & BCM_I2C_BUSY) == 0) { BCM_BSC_UNLOCK(sc); return; } status = BCM_BSC_READ(sc, BCM_BSC_STATUS); DEBUGF(sc, 4, " ", status); /* RXD and DONE can assert together, empty fifo before checking done. */ if ((sc->sc_flags & BCM_I2C_READ) && (status & BCM_BSC_STATUS_RXD)) bcm_bsc_empty_rx_fifo(sc); /* Check for completion. */ if (status & (BCM_BSC_STATUS_ERRBITS | BCM_BSC_STATUS_DONE)) { sc->sc_flags |= BCM_I2C_DONE; if (status & BCM_BSC_STATUS_ERRBITS) sc->sc_flags |= BCM_I2C_ERROR; /* Disable interrupts. */ bcm_bsc_reset(sc); wakeup(sc); } else if (!(sc->sc_flags & BCM_I2C_READ)) { /* * Don't check for TXD until after determining whether the * transfer is complete; TXD will be asserted along with ERR or * DONE if there is room in the fifo. */ if ((status & BCM_BSC_STATUS_TXD) && sc->sc_totlen > 0) bcm_bsc_fill_tx_fifo(sc); } BCM_BSC_UNLOCK(sc); } static int bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct bcm_bsc_softc *sc; struct iic_msg *endmsgs, *nxtmsg; uint32_t readctl, status; int err; uint16_t curlen; uint8_t curisread, curslave, nxtisread, nxtslave; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); /* If the controller is busy wait until it is available. */ while (sc->sc_flags & BCM_I2C_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusw", 0); /* Now we have control over the BSC controller. */ sc->sc_flags = BCM_I2C_BUSY; DEVICE_DEBUGF(sc, 3, "Transfer %d msgs\n", nmsgs); /* Clear the FIFO and the pending interrupts. */ bcm_bsc_reset(sc); /* * Perform all the transfers requested in the array of msgs. Note that * it is bcm_bsc_empty_rx_fifo() and bcm_bsc_fill_tx_fifo() that advance * sc->sc_curmsg through the array of messages, as the data from each * message is fully consumed, but it is this loop that notices when we * have no more messages to process. */ err = 0; sc->sc_resid = 0; sc->sc_curmsg = msgs; endmsgs = &msgs[nmsgs]; while (sc->sc_curmsg < endmsgs) { readctl = 0; curslave = sc->sc_curmsg->slave >> 1; curisread = sc->sc_curmsg->flags & IIC_M_RD; sc->sc_replen = 0; sc->sc_totlen = sc->sc_curmsg->len; /* * Scan for scatter/gather IO (same slave and direction) or * repeat-start (read following write for the same slave). */ for (nxtmsg = sc->sc_curmsg + 1; nxtmsg < endmsgs; ++nxtmsg) { nxtslave = nxtmsg->slave >> 1; if (curslave == nxtslave) { nxtisread = nxtmsg->flags & IIC_M_RD; if (curisread == nxtisread) { /* * Same slave and direction, this * message will be part of the same * transfer as the previous one. */ sc->sc_totlen += nxtmsg->len; continue; } else if (curisread == IIC_M_WR) { /* * Read after write to same slave means * repeat-start, remember how many bytes * come before the repeat-start, switch * the direction to IIC_M_RD, and gather * up following reads to the same slave. */ curisread = IIC_M_RD; sc->sc_replen = sc->sc_totlen; sc->sc_totlen += nxtmsg->len; continue; } } break; } /* * curslave and curisread temporaries from above may refer to * the after-repstart msg, reset them to reflect sc_curmsg. */ curisread = (sc->sc_curmsg->flags & IIC_M_RD) ? 1 : 0; curslave = sc->sc_curmsg->slave | curisread; /* Write the slave address. */ BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, curslave >> 1); DEVICE_DEBUGF(sc, 2, "start 0x%02x\n", curslave); /* * Either set up read length and direction variables for a * simple transfer or get the hardware started on the first * piece of a transfer that involves a repeat-start and set up * the read length and direction vars for the second piece. */ if (sc->sc_replen == 0) { DEVICE_DEBUGF(sc, 1, "%-6s 0x%02x len %d: ", (curisread) ? "read" : "write", curslave, sc->sc_totlen); curlen = sc->sc_totlen; if (curisread) { readctl = BCM_BSC_CTRL_READ; sc->sc_flags |= BCM_I2C_READ; } else { readctl = 0; sc->sc_flags &= ~BCM_I2C_READ; } } else { DEVICE_DEBUGF(sc, 1, "%-6s 0x%02x len %d: ", (curisread) ? "read" : "write", curslave, sc->sc_replen); /* * Start the write transfer with an empty fifo and wait * for the 'transfer active' status bit to light up; * that indicates that the hardware has latched the * direction and length for the write, and we can safely * reload those registers and issue the start for the * following read; interrupts are not enabled here. */ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, sc->sc_replen); BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST); do { status = BCM_BSC_READ(sc, BCM_BSC_STATUS); if (status & BCM_BSC_STATUS_ERR) { /* no ACK on slave addr */ err = EIO; goto xfer_done; } } while ((status & BCM_BSC_STATUS_TA) == 0); /* * Set curlen and readctl for the repeat-start read that * we need to set up below, but set sc_flags to write, * because that is the operation in progress right now. */ curlen = sc->sc_totlen - sc->sc_replen; readctl = BCM_BSC_CTRL_READ; sc->sc_flags &= ~BCM_I2C_READ; } /* * Start the transfer with interrupts enabled, then if doing a * write, fill the tx fifo. Not prefilling the fifo until after * this start command is the key workaround for making * repeat-start work, and it's harmless to do it in this order * for a regular write too. */ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, curlen); BCM_BSC_WRITE(sc, BCM_BSC_CTRL, readctl | BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST | BCM_BSC_CTRL_INT_ALL); if (!(sc->sc_curmsg->flags & IIC_M_RD)) { bcm_bsc_fill_tx_fifo(sc); } /* Wait for the transaction to complete. */ while (err == 0 && !(sc->sc_flags & BCM_I2C_DONE)) { err = mtx_sleep(sc, &sc->sc_mtx, 0, "bsciow", hz); } /* Check for errors. */ if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) err = EIO; xfer_done: DEBUGF(sc, 1, " err=%d\n", err); DEVICE_DEBUGF(sc, 2, "stop\n"); if (err != 0) break; } /* Disable interrupts, clean fifo, etc. */ bcm_bsc_reset(sc); /* Clean the controller flags. */ sc->sc_flags = 0; /* Wake up the threads waiting for bus. */ wakeup(dev); BCM_BSC_UNLOCK(sc); return (err); } static int bcm_bsc_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct bcm_bsc_softc *sc; uint32_t busfreq; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); if (sc->sc_iicbus == NULL) busfreq = 100000; else busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / busfreq); BCM_BSC_UNLOCK(sc); return (IIC_ENOADDR); } static phandle_t bcm_bsc_get_node(device_t bus, device_t dev) { /* We only have one child, the I2C bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_bsc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_bsc_probe), DEVMETHOD(device_attach, bcm_bsc_attach), DEVMETHOD(device_detach, bcm_bsc_detach), /* iicbus interface */ DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, bcm_bsc_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_bsc_get_node), DEVMETHOD_END }; static devclass_t bcm_bsc_devclass; static driver_t bcm_bsc_driver = { "iichb", bcm_bsc_methods, sizeof(struct bcm_bsc_softc), }; DRIVER_MODULE(iicbus, bcm2835_bsc, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(bcm2835_bsc, simplebus, bcm_bsc_driver, bcm_bsc_devclass, 0, 0); Index: head/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h (revision 332262) @@ -1,81 +1,71 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _BCM2835_BSCVAR_H #define _BCM2835_BSCVAR_H -struct { - uint32_t sda; - uint32_t scl; - unsigned long start; -} bcm_bsc_pins[] = { - { 0, 1, 0x205000 }, /* BSC0 GPIO pins and base address. */ - { 2, 3, 0x804000 } /* BSC1 GPIO pins and base address. */ -}; -#define BCM_BSC_BASE_MASK 0x00ffffff - struct iic_msg; struct bcm_bsc_softc { device_t sc_dev; device_t sc_iicbus; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; void * sc_intrhand; struct iic_msg * sc_curmsg; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; int sc_debug; uint16_t sc_replen; uint16_t sc_totlen; uint16_t sc_resid; uint16_t sc_dlen; uint8_t * sc_data; uint8_t sc_flags; }; #define BCM_I2C_BUSY 0x01 #define BCM_I2C_READ 0x02 #define BCM_I2C_ERROR 0x04 #define BCM_I2C_DONE 0x08 #define BCM_BSC_WRITE(_sc, _off, _val) \ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) #define BCM_BSC_READ(_sc, _off) \ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) #define BCM_BSC_LOCK(_sc) \ mtx_lock(&(_sc)->sc_mtx) #define BCM_BSC_UNLOCK(_sc) \ mtx_unlock(&(_sc)->sc_mtx) #endif /* _BCM2835_BSCVAR_H_ */ Index: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 332262) @@ -1,1231 +1,1324 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012-2015 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include -#include - #include "gpio_if.h" #include "pic_if.h" #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define dprintf(fmt, args...) #endif #define BCM_GPIO_IRQS 4 #define BCM_GPIO_PINS 54 #define BCM_GPIO_PINS_PER_BANK 32 #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) +#define BCM2835_FSEL_GPIO_IN 0 +#define BCM2835_FSEL_GPIO_OUT 1 +#define BCM2835_FSEL_ALT5 2 +#define BCM2835_FSEL_ALT4 3 +#define BCM2835_FSEL_ALT0 4 +#define BCM2835_FSEL_ALT1 5 +#define BCM2835_FSEL_ALT2 6 +#define BCM2835_FSEL_ALT3 7 + +#define BCM2835_PUD_OFF 0 +#define BCM2835_PUD_DOWN 1 +#define BCM2835_PUD_UP 2 + static struct resource_spec bcm_gpio_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ { -1, 0, 0 } }; struct bcm_gpio_sysctl { struct bcm_gpio_softc *sc; uint32_t pin; }; struct bcm_gpio_irqsrc { struct intr_irqsrc bgi_isrc; uint32_t bgi_irq; uint32_t bgi_mode; uint32_t bgi_mask; }; struct bcm_gpio_softc { device_t sc_dev; device_t sc_busdev; struct mtx sc_mtx; struct resource * sc_res[BCM_GPIO_IRQS + 1]; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand[BCM_GPIO_IRQS]; int sc_gpio_npins; int sc_ro_npins; int sc_ro_pins[BCM_GPIO_PINS]; struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; }; enum bcm_gpio_pud { BCM_GPIO_NONE, BCM_GPIO_PULLDOWN, BCM_GPIO_PULLUP, }; #define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define BCM_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) #define BCM_GPIO_READ(_sc, _off) \ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) #define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits)) #define BCM_GPIO_SET_BITS(_sc, _off, _bits) \ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits) #define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK) #define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK)) #define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */ #define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */ #define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */ #define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */ #define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */ #define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */ #define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */ #define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */ #define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */ #define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */ #define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */ #define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */ #define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */ static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-gpio", 1}, {"brcm,bcm2835-gpio", 1}, {NULL, 0} }; static struct bcm_gpio_softc *bcm_gpio_sc = NULL; static int bcm_gpio_intr_bank0(void *arg); static int bcm_gpio_intr_bank1(void *arg); static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); static int bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) { int i; for (i = 0; i < sc->sc_ro_npins; i++) if (pin == sc->sc_ro_pins[i]) return (1); return (0); } static uint32_t bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) { uint32_t bank, func, offset; /* Five banks, 10 pins per bank, 3 bits per pin. */ bank = pin / 10; offset = (pin - bank * 10) * 3; BCM_GPIO_LOCK(sc); func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; BCM_GPIO_UNLOCK(sc); return (func); } static void bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) { switch (nfunc) { - case BCM_GPIO_INPUT: + case BCM2835_FSEL_GPIO_IN: strncpy(buf, "input", bufsize); break; - case BCM_GPIO_OUTPUT: + case BCM2835_FSEL_GPIO_OUT: strncpy(buf, "output", bufsize); break; - case BCM_GPIO_ALT0: + case BCM2835_FSEL_ALT0: strncpy(buf, "alt0", bufsize); break; - case BCM_GPIO_ALT1: + case BCM2835_FSEL_ALT1: strncpy(buf, "alt1", bufsize); break; - case BCM_GPIO_ALT2: + case BCM2835_FSEL_ALT2: strncpy(buf, "alt2", bufsize); break; - case BCM_GPIO_ALT3: + case BCM2835_FSEL_ALT3: strncpy(buf, "alt3", bufsize); break; - case BCM_GPIO_ALT4: + case BCM2835_FSEL_ALT4: strncpy(buf, "alt4", bufsize); break; - case BCM_GPIO_ALT5: + case BCM2835_FSEL_ALT5: strncpy(buf, "alt5", bufsize); break; default: strncpy(buf, "invalid", bufsize); } } static int bcm_gpio_str_func(char *func, uint32_t *nfunc) { if (strcasecmp(func, "input") == 0) - *nfunc = BCM_GPIO_INPUT; + *nfunc = BCM2835_FSEL_GPIO_IN; else if (strcasecmp(func, "output") == 0) - *nfunc = BCM_GPIO_OUTPUT; + *nfunc = BCM2835_FSEL_GPIO_OUT; else if (strcasecmp(func, "alt0") == 0) - *nfunc = BCM_GPIO_ALT0; + *nfunc = BCM2835_FSEL_ALT0; else if (strcasecmp(func, "alt1") == 0) - *nfunc = BCM_GPIO_ALT1; + *nfunc = BCM2835_FSEL_ALT1; else if (strcasecmp(func, "alt2") == 0) - *nfunc = BCM_GPIO_ALT2; + *nfunc = BCM2835_FSEL_ALT2; else if (strcasecmp(func, "alt3") == 0) - *nfunc = BCM_GPIO_ALT3; + *nfunc = BCM2835_FSEL_ALT3; else if (strcasecmp(func, "alt4") == 0) - *nfunc = BCM_GPIO_ALT4; + *nfunc = BCM2835_FSEL_ALT4; else if (strcasecmp(func, "alt5") == 0) - *nfunc = BCM_GPIO_ALT5; + *nfunc = BCM2835_FSEL_ALT5; else return (-1); return (0); } static uint32_t bcm_gpio_func_flag(uint32_t nfunc) { switch (nfunc) { - case BCM_GPIO_INPUT: + case BCM2835_FSEL_GPIO_IN: return (GPIO_PIN_INPUT); - case BCM_GPIO_OUTPUT: + case BCM2835_FSEL_GPIO_OUT: return (GPIO_PIN_OUTPUT); } return (0); } static void bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Must be called with lock held. */ BCM_GPIO_LOCK_ASSERT(sc); /* Five banks, 10 pins per bank, 3 bits per pin. */ bank = pin / 10; offset = (pin - bank * 10) * 3; data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); data &= ~(7 << offset); data |= (f << offset); BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); } static void bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank; /* Must be called with lock held. */ BCM_GPIO_LOCK_ASSERT(sc); bank = BCM_GPIO_BANK(pin); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); } -void +static void bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) { struct bcm_gpio_softc *sc; int i; sc = device_get_softc(dev); BCM_GPIO_LOCK(sc); - /* Disable pull-up or pull-down on pin. */ - bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); - - /* And now set the pin function. */ + /* Set the pin function. */ bcm_gpio_set_function(sc, pin, nfunc); /* Update the pin flags. */ for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i < sc->sc_gpio_npins) sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); BCM_GPIO_UNLOCK(sc); } static void bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { BCM_GPIO_LOCK(sc); /* * Manage input/output. */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; bcm_gpio_set_function(sc, pin->gp_pin, - BCM_GPIO_OUTPUT); + BCM2835_FSEL_GPIO_OUT); } else { pin->gp_flags |= GPIO_PIN_INPUT; bcm_gpio_set_function(sc, pin->gp_pin, - BCM_GPIO_INPUT); + BCM2835_FSEL_GPIO_IN); } } /* Manage Pull-up/pull-down. */ pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) { pin->gp_flags |= GPIO_PIN_PULLUP; bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); } else { pin->gp_flags |= GPIO_PIN_PULLDOWN; bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); } } else bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); BCM_GPIO_UNLOCK(sc); } static device_t bcm_gpio_get_bus(device_t dev) { struct bcm_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int bcm_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = BCM_GPIO_PINS - 1; return (0); } static int bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never touch on read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, reg; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never write to read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); BCM_GPIO_LOCK(sc); bank = BCM_GPIO_BANK(pin); if (value) reg = BCM_GPIO_GPSET(bank); else reg = BCM_GPIO_GPCLR(bank); BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, reg_data; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); bank = BCM_GPIO_BANK(pin); BCM_GPIO_LOCK(sc); reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); BCM_GPIO_UNLOCK(sc); *val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0; return (0); } static int bcm_gpio_pin_toggle(device_t dev, uint32_t pin) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, data, reg; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never write to read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); BCM_GPIO_LOCK(sc); bank = BCM_GPIO_BANK(pin); data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); if (data & BCM_GPIO_MASK(pin)) reg = BCM_GPIO_GPCLR(bank); else reg = BCM_GPIO_GPSET(bank); BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) { char buf[16]; struct bcm_gpio_softc *sc; struct bcm_gpio_sysctl *sc_sysctl; uint32_t nfunc; int error; sc_sysctl = arg1; sc = sc_sysctl->sc; /* Get the current pin function. */ nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); bcm_gpio_func_str(nfunc, buf, sizeof(buf)); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error != 0 || req->newptr == NULL) return (error); /* Ignore changes on read-only pins. */ if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) return (0); /* Parse the user supplied string and check for a valid pin function. */ if (bcm_gpio_str_func(buf, &nfunc) != 0) return (EINVAL); /* Update the pin alternate function. */ bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); return (0); } static void bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) { char pinbuf[3]; struct bcm_gpio_sysctl *sc_sysctl; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node, *pin_node, *pinN_node; struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; int i; /* * Add per-pin sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", CTLFLAG_RD, NULL, "GPIO Pins"); pin_tree = SYSCTL_CHILDREN(pin_node); for (i = 0; i < sc->sc_gpio_npins; i++) { snprintf(pinbuf, sizeof(pinbuf), "%d", i); pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, CTLFLAG_RD, NULL, "GPIO Pin"); pinN_tree = SYSCTL_CHILDREN(pinN_node); sc->sc_sysctl[i].sc = sc; sc_sysctl = &sc->sc_sysctl[i]; sc_sysctl->sc = sc; sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, "A", "Pin Function"); } } static int bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, const char *propname, const char *label) { int i, need_comma, npins, range_start, range_stop; pcell_t *pins; /* Get the property data. */ npins = OF_getencprop_alloc(node, propname, sizeof(*pins), (void **)&pins); if (npins < 0) return (-1); if (npins == 0) { OF_prop_free(pins); return (0); } for (i = 0; i < npins; i++) sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; sc->sc_ro_npins += npins; need_comma = 0; device_printf(sc->sc_dev, "%s pins: ", label); range_start = range_stop = pins[0]; for (i = 1; i < npins; i++) { if (pins[i] != range_stop + 1) { if (need_comma) printf(","); if (range_start != range_stop) printf("%d-%d", range_start, range_stop); else printf("%d", range_start); range_start = range_stop = pins[i]; need_comma = 1; } else range_stop++; } if (need_comma) printf(","); if (range_start != range_stop) printf("%d-%d.\n", range_start, range_stop); else printf("%d.\n", range_start); OF_prop_free(pins); return (0); } static int bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) { char *name; phandle_t gpio, node, reserved; ssize_t len; /* Get read-only pins if they're provided */ gpio = ofw_bus_get_node(sc->sc_dev); if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", "read-only") != 0) return (0); /* Traverse the GPIO subnodes to find the reserved pins node. */ reserved = 0; node = OF_child(gpio); while ((node != 0) && (reserved == 0)) { len = OF_getprop_alloc(node, "name", 1, (void **)&name); if (len == -1) return (-1); if (strcmp(name, "reserved") == 0) reserved = node; OF_prop_free(name); node = OF_peer(node); } if (reserved == 0) return (-1); /* Get the reserved pins. */ if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", "reserved") != 0) return (-1); return (0); } static int bcm_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 GPIO controller"); return (BUS_PROBE_DEFAULT); } static int bcm_gpio_intr_attach(device_t dev) { struct bcm_gpio_softc *sc; /* * Only first two interrupt lines are used. Third line is * mirrored second line and forth line is common for all banks. */ sc = device_get_softc(dev); if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) return (-1); if (bcm_gpio_pic_attach(sc) != 0) { device_printf(dev, "unable to attach PIC\n"); return (-1); } if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) return (-1); if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) return (-1); return (0); } static void bcm_gpio_intr_detach(device_t dev) { struct bcm_gpio_softc *sc; sc = device_get_softc(dev); if (sc->sc_intrhand[0] != NULL) bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); if (sc->sc_intrhand[1] != NULL) bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); bcm_gpio_pic_detach(sc); } static int bcm_gpio_attach(device_t dev) { int i, j; phandle_t gpio; struct bcm_gpio_softc *sc; uint32_t func; if (bcm_gpio_sc != NULL) return (ENXIO); bcm_gpio_sc = sc = device_get_softc(dev); sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN); if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) { device_printf(dev, "cannot allocate resources\n"); goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_res[0]); sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); /* Setup the GPIO interrupt handler. */ if (bcm_gpio_intr_attach(dev)) { device_printf(dev, "unable to setup the gpio irq handler\n"); goto fail; } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; /* * Find the read-only pins. These are pins we never touch or bad * things could happen. */ if (bcm_gpio_get_reserved_pins(sc) == -1) goto fail; /* Initialize the software controlled pins. */ for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", j); func = bcm_gpio_get_function(sc, j); sc->sc_gpio_pins[i].gp_pin = j; sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); i++; } sc->sc_gpio_npins = i; bcm_gpio_sysctl_init(sc); sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; + fdt_pinctrl_register(dev, "brcm,pins"); + fdt_pinctrl_configure_tree(dev); + return (0); fail: bcm_gpio_intr_detach(dev); bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int bcm_gpio_detach(device_t dev) { return (EBUSY); } static inline void bcm_gpio_modify(struct bcm_gpio_softc *sc, uint32_t reg, uint32_t mask, bool set_bits) { if (set_bits) BCM_GPIO_SET_BITS(sc, reg, mask); else BCM_GPIO_CLEAR_BITS(sc, reg, mask); } static inline void bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) { uint32_t bank; /* Write 1 to clear. */ bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); } static inline bool bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) { return (bgi->bgi_mode == GPIO_INTR_LEVEL_LOW || bgi->bgi_mode == GPIO_INTR_LEVEL_HIGH); } static inline void bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) { uint32_t bank; bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_LOCK(sc); switch (bgi->bgi_mode) { case GPIO_INTR_LEVEL_LOW: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); break; case GPIO_INTR_LEVEL_HIGH: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_RISING: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_FALLING: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_BOTH: BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; } BCM_GPIO_UNLOCK(sc); } static inline void bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) { uint32_t bank; bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_LOCK(sc); switch (bgi->bgi_mode) { case GPIO_INTR_LEVEL_LOW: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); break; case GPIO_INTR_LEVEL_HIGH: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_RISING: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_FALLING: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; case GPIO_INTR_EDGE_BOTH: BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); break; } BCM_GPIO_UNLOCK(sc); } static int bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) { u_int irq; struct bcm_gpio_irqsrc *bgi; uint32_t reg; /* Do not care of spurious interrupt on GPIO. */ reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); while (reg != 0) { irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; bgi = sc->sc_isrcs + irq; if (!bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); if (intr_isrc_dispatch(&bgi->bgi_isrc, curthread->td_intr_frame) != 0) { bcm_gpio_isrc_mask(sc, bgi); if (bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } reg &= ~bgi->bgi_mask; } return (FILTER_HANDLED); } static int bcm_gpio_intr_bank0(void *arg) { return (bcm_gpio_intr_internal(arg, 0)); } static int bcm_gpio_intr_bank1(void *arg) { return (bcm_gpio_intr_internal(arg, 1)); } static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) { int error; uint32_t irq; const char *name; name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < BCM_GPIO_PINS; irq++) { sc->sc_isrcs[irq].bgi_irq = irq; sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); sc->sc_isrcs[irq].bgi_mode = GPIO_INTR_CONFORM; error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); /* XXX deregister ISRCs */ } if (intr_pic_register(sc->sc_dev, OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) return (ENXIO); return (0); } static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) { /* * There has not been established any procedure yet * how to detach PIC from living system correctly. */ device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); return (EBUSY); } static void bcm_gpio_pic_config_intr(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi, uint32_t mode) { uint32_t bank; bank = BCM_GPIO_BANK(bgi->bgi_irq); BCM_GPIO_LOCK(sc); bcm_gpio_modify(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask, mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); bcm_gpio_modify(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask, mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); bcm_gpio_modify(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask, mode == GPIO_INTR_LEVEL_HIGH); bcm_gpio_modify(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask, mode == GPIO_INTR_LEVEL_LOW); bgi->bgi_mode = mode; BCM_GPIO_UNLOCK(sc); } static void bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; bcm_gpio_isrc_mask(sc, bgi); } static void bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; arm_irq_memory_barrier(bgi->bgi_irq); bcm_gpio_isrc_unmask(sc, bgi); } static int bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, struct intr_map_data_fdt *daf, u_int *irqp, uint32_t *modep) { u_int irq; uint32_t mode; /* * The first cell is the interrupt number. * The second cell is used to specify flags: * bits[3:0] trigger type and level flags: * 1 = low-to-high edge triggered. * 2 = high-to-low edge triggered. * 4 = active high level-sensitive. * 8 = active low level-sensitive. */ if (daf->ncells != 2) return (EINVAL); irq = daf->cells[0]; if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) return (EINVAL); /* Only reasonable modes are supported. */ if (daf->cells[1] == 1) mode = GPIO_INTR_EDGE_RISING; else if (daf->cells[1] == 2) mode = GPIO_INTR_EDGE_FALLING; else if (daf->cells[1] == 3) mode = GPIO_INTR_EDGE_BOTH; else if (daf->cells[1] == 4) mode = GPIO_INTR_LEVEL_HIGH; else if (daf->cells[1] == 8) mode = GPIO_INTR_LEVEL_LOW; else return (EINVAL); *irqp = irq; if (modep != NULL) *modep = mode; return (0); } static int bcm_gpio_pic_map_gpio(struct bcm_gpio_softc *sc, struct intr_map_data_gpio *dag, u_int *irqp, uint32_t *modep) { u_int irq; uint32_t mode; irq = dag->gpio_pin_num; if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) return (EINVAL); mode = dag->gpio_intr_mode; if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && mode != GPIO_INTR_EDGE_BOTH) return (EINVAL); *irqp = irq; if (modep != NULL) *modep = mode; return (0); } static int bcm_gpio_pic_map(struct bcm_gpio_softc *sc, struct intr_map_data *data, u_int *irqp, uint32_t *modep) { switch (data->type) { case INTR_MAP_DATA_FDT: return (bcm_gpio_pic_map_fdt(sc, (struct intr_map_data_fdt *)data, irqp, modep)); case INTR_MAP_DATA_GPIO: return (bcm_gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data, irqp, modep)); default: return (ENOTSUP); } } static int bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { int error; u_int irq; struct bcm_gpio_softc *sc = device_get_softc(dev); error = bcm_gpio_pic_map(sc, data, &irq, NULL); if (error == 0) *isrcp = &sc->sc_isrcs[irq].bgi_isrc; return (error); } static void bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; if (bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); } static void bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { bcm_gpio_pic_enable_intr(dev, isrc); } static void bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; bcm_gpio_isrc_mask(sc, bgi); if (bcm_gpio_isrc_is_level(bgi)) bcm_gpio_isrc_eoi(sc, bgi); } static int bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { u_int irq; uint32_t mode; struct bcm_gpio_softc *sc; struct bcm_gpio_irqsrc *bgi; if (data == NULL) return (ENOTSUP); sc = device_get_softc(dev); bgi = (struct bcm_gpio_irqsrc *)isrc; /* Get and check config for an interrupt. */ if (bcm_gpio_pic_map(sc, data, &irq, &mode) != 0 || bgi->bgi_irq != irq) return (EINVAL); /* * If this is a setup for another handler, * only check that its configuration match. */ if (isrc->isrc_handlers != 0) return (bgi->bgi_mode == mode ? 0 : EINVAL); bcm_gpio_pic_config_intr(sc, bgi, mode); return (0); } static int bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct bcm_gpio_softc *sc = device_get_softc(dev); struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; if (isrc->isrc_handlers == 0) bcm_gpio_pic_config_intr(sc, bgi, GPIO_INTR_CONFORM); return (0); } static phandle_t bcm_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } +static int +bcm_gpio_configure_pins(device_t dev, phandle_t cfgxref) +{ + phandle_t cfgnode; + int i, pintuples, pulltuples; + uint32_t pin; + uint32_t *pins; + uint32_t *pulls; + uint32_t function; + static struct bcm_gpio_softc *sc; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + pins = NULL; + pintuples = OF_getencprop_alloc(cfgnode, "brcm,pins", sizeof(*pins), + (void **)&pins); + + char name[32]; + OF_getprop(cfgnode, "name", &name, sizeof(name)); + + if (pintuples < 0) + return (ENOENT); + + if (pintuples == 0) + return (0); /* Empty property is not an error. */ + + if (OF_getencprop(cfgnode, "brcm,function", &function, + sizeof(function)) <= 0) { + OF_prop_free(pins); + return (EINVAL); + } + + pulls = NULL; + pulltuples = OF_getencprop_alloc(cfgnode, "brcm,pull", sizeof(*pulls), + (void **)&pulls); + + if ((pulls != NULL) && (pulltuples != pintuples)) { + OF_prop_free(pins); + OF_prop_free(pulls); + return (EINVAL); + } + + for (i = 0; i < pintuples; i++) { + pin = pins[i]; + bcm_gpio_set_alternate(dev, pin, function); + if (bootverbose) + device_printf(dev, "set pin %d to func %d", pin, function); + if (pulls) { + if (bootverbose) + printf(", pull %d", pulls[i]); + switch (pulls[i]) { + /* Convert to gpio(4) flags */ + case BCM2835_PUD_OFF: + bcm_gpio_pin_setflags(dev, pin, 0); + break; + case BCM2835_PUD_UP: + bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLUP); + break; + case BCM2835_PUD_DOWN: + bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLDOWN); + break; + default: + printf("%s: invalid pull value for pin %d: %d\n", + name, pin, pulls[i]); + } + } + if (bootverbose) + printf("\n"); + } + + OF_prop_free(pins); + if (pulls) + OF_prop_free(pulls); + + return (0); +} + static device_method_t bcm_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_gpio_probe), DEVMETHOD(device_attach, bcm_gpio_attach), DEVMETHOD(device_detach, bcm_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, bcm_gpio_configure_pins), + DEVMETHOD_END }; static devclass_t bcm_gpio_devclass; static driver_t bcm_gpio_driver = { "gpio", bcm_gpio_methods, sizeof(struct bcm_gpio_softc), }; -DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); +EARLY_DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); Index: head/sys/arm/broadcom/bcm2835/bcm2835_intr.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_intr.c (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_intr.c (revision 332262) @@ -1,455 +1,455 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Damjan Marion * All rights reserved. * * Based on OMAP3 INTC code by Ben Gray * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define INTC_PENDING_BASIC 0x00 #define INTC_PENDING_BANK1 0x04 #define INTC_PENDING_BANK2 0x08 #define INTC_FIQ_CONTROL 0x0C #define INTC_ENABLE_BANK1 0x10 #define INTC_ENABLE_BANK2 0x14 #define INTC_ENABLE_BASIC 0x18 #define INTC_DISABLE_BANK1 0x1C #define INTC_DISABLE_BANK2 0x20 #define INTC_DISABLE_BASIC 0x24 #define INTC_PENDING_BASIC_ARM 0x0000FF #define INTC_PENDING_BASIC_GPU1_PEND 0x000100 #define INTC_PENDING_BASIC_GPU2_PEND 0x000200 #define INTC_PENDING_BASIC_GPU1_7 0x000400 #define INTC_PENDING_BASIC_GPU1_9 0x000800 #define INTC_PENDING_BASIC_GPU1_10 0x001000 #define INTC_PENDING_BASIC_GPU1_18 0x002000 #define INTC_PENDING_BASIC_GPU1_19 0x004000 #define INTC_PENDING_BASIC_GPU2_21 0x008000 #define INTC_PENDING_BASIC_GPU2_22 0x010000 #define INTC_PENDING_BASIC_GPU2_23 0x020000 #define INTC_PENDING_BASIC_GPU2_24 0x040000 #define INTC_PENDING_BASIC_GPU2_25 0x080000 #define INTC_PENDING_BASIC_GPU2_30 0x100000 #define INTC_PENDING_BASIC_MASK 0x1FFFFF #define INTC_PENDING_BASIC_GPU1_MASK (INTC_PENDING_BASIC_GPU1_7 | \ INTC_PENDING_BASIC_GPU1_9 | \ INTC_PENDING_BASIC_GPU1_10 | \ INTC_PENDING_BASIC_GPU1_18 | \ INTC_PENDING_BASIC_GPU1_19) #define INTC_PENDING_BASIC_GPU2_MASK (INTC_PENDING_BASIC_GPU2_21 | \ INTC_PENDING_BASIC_GPU2_22 | \ INTC_PENDING_BASIC_GPU2_23 | \ INTC_PENDING_BASIC_GPU2_24 | \ INTC_PENDING_BASIC_GPU2_25 | \ INTC_PENDING_BASIC_GPU2_30) #define INTC_PENDING_BANK1_MASK (~((1 << 7) | (1 << 9) | (1 << 10) | \ (1 << 18) | (1 << 19))) #define INTC_PENDING_BANK2_MASK (~((1 << 21) | (1 << 22) | (1 << 23) | \ (1 << 24) | (1 << 25) | (1 << 30))) #define BANK1_START 8 #define BANK1_END (BANK1_START + 32 - 1) #define BANK2_START (BANK1_START + 32) #define BANK2_END (BANK2_START + 32 - 1) #define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START)) #define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) #define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) #define IRQ_BANK1(n) ((n) - BANK1_START) #define IRQ_BANK2(n) ((n) - BANK2_START) #ifdef DEBUG #define dprintf(fmt, args...) printf(fmt, ##args) #else #define dprintf(fmt, args...) #endif #define BCM_INTC_NIRQS 72 /* 8 + 32 + 32 */ struct bcm_intc_irqsrc { struct intr_irqsrc bii_isrc; u_int bii_irq; uint16_t bii_disable_reg; uint16_t bii_enable_reg; uint32_t bii_mask; }; struct bcm_intc_softc { device_t sc_dev; struct resource * intc_res; bus_space_tag_t intc_bst; bus_space_handle_t intc_bsh; struct resource * intc_irq_res; void * intc_irq_hdl; struct bcm_intc_irqsrc intc_isrcs[BCM_INTC_NIRQS]; }; static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-armctrl-ic", 1}, {"brcm,bcm2835-armctrl-ic", 1}, {"brcm,bcm2836-armctrl-ic", 1}, {NULL, 0} }; static struct bcm_intc_softc *bcm_intc_sc = NULL; #define intc_read_4(_sc, reg) \ bus_space_read_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg)) #define intc_write_4(_sc, reg, val) \ bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val)) static inline void bcm_intc_isrc_mask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii) { intc_write_4(sc, bii->bii_disable_reg, bii->bii_mask); } static inline void bcm_intc_isrc_unmask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii) { intc_write_4(sc, bii->bii_enable_reg, bii->bii_mask); } static inline int bcm2835_intc_active_intr(struct bcm_intc_softc *sc) { uint32_t pending, pending_gpu; pending = intc_read_4(sc, INTC_PENDING_BASIC) & INTC_PENDING_BASIC_MASK; if (pending == 0) return (-1); if (pending & INTC_PENDING_BASIC_ARM) return (ffs(pending) - 1); if (pending & INTC_PENDING_BASIC_GPU1_MASK) { if (pending & INTC_PENDING_BASIC_GPU1_7) return (BANK1_START + 7); if (pending & INTC_PENDING_BASIC_GPU1_9) return (BANK1_START + 9); if (pending & INTC_PENDING_BASIC_GPU1_10) return (BANK1_START + 10); if (pending & INTC_PENDING_BASIC_GPU1_18) return (BANK1_START + 18); if (pending & INTC_PENDING_BASIC_GPU1_19) return (BANK1_START + 19); } if (pending & INTC_PENDING_BASIC_GPU2_MASK) { if (pending & INTC_PENDING_BASIC_GPU2_21) return (BANK2_START + 21); if (pending & INTC_PENDING_BASIC_GPU2_22) return (BANK2_START + 22); if (pending & INTC_PENDING_BASIC_GPU2_23) return (BANK2_START + 23); if (pending & INTC_PENDING_BASIC_GPU2_24) return (BANK2_START + 24); if (pending & INTC_PENDING_BASIC_GPU2_25) return (BANK2_START + 25); if (pending & INTC_PENDING_BASIC_GPU2_30) return (BANK2_START + 30); } if (pending & INTC_PENDING_BASIC_GPU1_PEND) { pending_gpu = intc_read_4(sc, INTC_PENDING_BANK1); pending_gpu &= INTC_PENDING_BANK1_MASK; if (pending_gpu != 0) return (BANK1_START + ffs(pending_gpu) - 1); } if (pending & INTC_PENDING_BASIC_GPU2_PEND) { pending_gpu = intc_read_4(sc, INTC_PENDING_BANK2); pending_gpu &= INTC_PENDING_BANK2_MASK; if (pending_gpu != 0) return (BANK2_START + ffs(pending_gpu) - 1); } return (-1); /* It shouldn't end here, but it's hardware. */ } static int bcm2835_intc_intr(void *arg) { int irq, num; struct bcm_intc_softc *sc = arg; for (num = 0; ; num++) { irq = bcm2835_intc_active_intr(sc); if (irq == -1) break; if (intr_isrc_dispatch(&sc->intc_isrcs[irq].bii_isrc, curthread->td_intr_frame) != 0) { bcm_intc_isrc_mask(sc, &sc->intc_isrcs[irq]); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } arm_irq_memory_barrier(0); /* XXX */ } if (num == 0) device_printf(sc->sc_dev, "Spurious interrupt detected\n"); return (FILTER_HANDLED); } static void bcm_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_intc_irqsrc *bii = (struct bcm_intc_irqsrc *)isrc; arm_irq_memory_barrier(bii->bii_irq); bcm_intc_isrc_unmask(device_get_softc(dev), bii); } static void bcm_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc) { bcm_intc_isrc_mask(device_get_softc(dev), (struct bcm_intc_irqsrc *)isrc); } static int bcm_intc_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { u_int irq; struct intr_map_data_fdt *daf; struct bcm_intc_softc *sc; bool valid; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); daf = (struct intr_map_data_fdt *)data; if (daf->ncells == 1) irq = daf->cells[0]; else if (daf->ncells == 2) { valid = true; switch (daf->cells[0]) { case 0: irq = daf->cells[1]; if (irq >= BANK1_START) valid = false; break; case 1: irq = daf->cells[1] + BANK1_START; if (irq > BANK1_END) valid = false; break; case 2: irq = daf->cells[1] + BANK2_START; if (irq > BANK2_END) valid = false; break; default: valid = false; break; } if (!valid) { device_printf(dev, "invalid IRQ config: bank=%d, irq=%d\n", daf->cells[0], daf->cells[1]); return (EINVAL); } } else return (EINVAL); if (irq >= BCM_INTC_NIRQS) return (EINVAL); sc = device_get_softc(dev); *isrcp = &sc->intc_isrcs[irq].bii_isrc; return (0); } static void bcm_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { bcm_intc_disable_intr(dev, isrc); } static void bcm_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc) { bcm_intc_enable_intr(dev, isrc); } static void bcm_intc_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static int bcm_intc_pic_register(struct bcm_intc_softc *sc, intptr_t xref) { struct bcm_intc_irqsrc *bii; int error; uint32_t irq; const char *name; name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < BCM_INTC_NIRQS; irq++) { bii = &sc->intc_isrcs[irq]; bii->bii_irq = irq; if (IS_IRQ_BASIC(irq)) { bii->bii_disable_reg = INTC_DISABLE_BASIC; bii->bii_enable_reg = INTC_ENABLE_BASIC; bii->bii_mask = 1 << irq; } else if (IS_IRQ_BANK1(irq)) { bii->bii_disable_reg = INTC_DISABLE_BANK1; bii->bii_enable_reg = INTC_ENABLE_BANK1; bii->bii_mask = 1 << IRQ_BANK1(irq); } else if (IS_IRQ_BANK2(irq)) { bii->bii_disable_reg = INTC_DISABLE_BANK2; bii->bii_enable_reg = INTC_ENABLE_BANK2; bii->bii_mask = 1 << IRQ_BANK2(irq); } else return (ENXIO); error = intr_isrc_register(&bii->bii_isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); } if (intr_pic_register(sc->sc_dev, xref) == NULL) return (ENXIO); return (0); } static int bcm_intc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2835 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_intc_attach(device_t dev) { struct bcm_intc_softc *sc = device_get_softc(dev); int rid = 0; intptr_t xref; sc->sc_dev = dev; if (bcm_intc_sc) return (ENXIO); sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->intc_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } xref = OF_xref_from_node(ofw_bus_get_node(dev)); if (bcm_intc_pic_register(sc, xref) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->intc_res); device_printf(dev, "could not register PIC\n"); return (ENXIO); } rid = 0; sc->intc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->intc_irq_res == NULL) { if (intr_pic_claim_root(dev, xref, bcm2835_intc_intr, sc, 0) != 0) { /* XXX clean up */ device_printf(dev, "could not set PIC as a root\n"); return (ENXIO); } } else { if (bus_setup_intr(dev, sc->intc_irq_res, INTR_TYPE_CLK, bcm2835_intc_intr, NULL, sc, &sc->intc_irq_hdl)) { /* XXX clean up */ device_printf(dev, "could not setup irq handler\n"); return (ENXIO); } } sc->intc_bst = rman_get_bustag(sc->intc_res); sc->intc_bsh = rman_get_bushandle(sc->intc_res); bcm_intc_sc = sc; return (0); } static device_method_t bcm_intc_methods[] = { DEVMETHOD(device_probe, bcm_intc_probe), DEVMETHOD(device_attach, bcm_intc_attach), DEVMETHOD(pic_disable_intr, bcm_intc_disable_intr), DEVMETHOD(pic_enable_intr, bcm_intc_enable_intr), DEVMETHOD(pic_map_intr, bcm_intc_map_intr), DEVMETHOD(pic_post_filter, bcm_intc_post_filter), DEVMETHOD(pic_post_ithread, bcm_intc_post_ithread), DEVMETHOD(pic_pre_ithread, bcm_intc_pre_ithread), { 0, 0 } }; static driver_t bcm_intc_driver = { "intc", bcm_intc_methods, sizeof(struct bcm_intc_softc), }; static devclass_t bcm_intc_devclass; EARLY_DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, - 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/broadcom/bcm2835/bcm2835_pwm.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_pwm.c (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_pwm.c (revision 332262) @@ -1,389 +1,379 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Poul-Henning Kamp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include -#include #include static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-pwm", 1}, {"brcm,bcm2835-pwm", 1}, {NULL, 0} }; struct bcm_pwm_softc { device_t sc_dev; struct resource * sc_mem_res; bus_space_tag_t sc_m_bst; bus_space_handle_t sc_m_bsh; device_t clkman; uint32_t freq; uint32_t period; uint32_t ratio; uint32_t mode; }; #define BCM_PWM_MEM_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val) #define BCM_PWM_MEM_READ(_sc, _off) \ bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off) #define BCM_PWM_CLK_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off, _val) #define BCM_PWM_CLK_READ(_sc, _off) \ bus_space_read_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off) #define W_CTL(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x00, _val) #define R_CTL(_sc) BCM_PWM_MEM_READ(_sc, 0x00) #define W_STA(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x04, _val) #define R_STA(_sc) BCM_PWM_MEM_READ(_sc, 0x04) #define W_RNG(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x10, _val) #define R_RNG(_sc) BCM_PWM_MEM_READ(_sc, 0x10) #define W_DAT(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x14, _val) #define R_DAT(_sc) BCM_PWM_MEM_READ(_sc, 0x14) static int bcm_pwm_reconf(struct bcm_pwm_softc *sc) { uint32_t u; - device_t gpio; /* Disable PWM */ W_CTL(sc, 0); /* Stop PWM clock */ (void)bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, 0); if (sc->mode == 0) return (0); - - /* Ask GPIO0 to set ALT0 for pin 12 */ - gpio = devclass_get_device(devclass_find("gpio"), 0); - if (!gpio) { - device_printf(sc->sc_dev, "cannot find gpio0\n"); - return (ENXIO); - } - bcm_gpio_set_alternate(gpio, 12, BCM_GPIO_ALT0); u = bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, sc->freq); if (u == 0) return (EINVAL); sc->freq = u; /* Config PWM */ W_RNG(sc, sc->period); if (sc->ratio > sc->period) sc->ratio = sc->period; W_DAT(sc, sc->ratio); /* Start PWM */ if (sc->mode == 1) W_CTL(sc, 0x81); else W_CTL(sc, 0x1); return (0); } static int bcm_pwm_pwm_freq_proc(SYSCTL_HANDLER_ARGS) { struct bcm_pwm_softc *sc; uint32_t r; int error; sc = (struct bcm_pwm_softc *)arg1; if (sc->mode == 1) r = sc->freq / sc->period; else r = 0; error = sysctl_handle_int(oidp, &r, sizeof(r), req); return (error); } static int bcm_pwm_mode_proc(SYSCTL_HANDLER_ARGS) { struct bcm_pwm_softc *sc; uint32_t r; int error; sc = (struct bcm_pwm_softc *)arg1; r = sc->mode; error = sysctl_handle_int(oidp, &r, sizeof(r), req); if (error != 0 || req->newptr == NULL) return (error); if (r > 2) return (EINVAL); sc->mode = r; return (bcm_pwm_reconf(sc)); } static int bcm_pwm_freq_proc(SYSCTL_HANDLER_ARGS) { struct bcm_pwm_softc *sc; uint32_t r; int error; sc = (struct bcm_pwm_softc *)arg1; r = sc->freq; error = sysctl_handle_int(oidp, &r, sizeof(r), req); if (error != 0 || req->newptr == NULL) return (error); if (r > 125000000) return (EINVAL); sc->freq = r; return (bcm_pwm_reconf(sc)); } static int bcm_pwm_period_proc(SYSCTL_HANDLER_ARGS) { struct bcm_pwm_softc *sc; int error; sc = (struct bcm_pwm_softc *)arg1; error = sysctl_handle_int(oidp, &sc->period, sizeof(sc->period), req); if (error != 0 || req->newptr == NULL) return (error); return (bcm_pwm_reconf(sc)); } static int bcm_pwm_ratio_proc(SYSCTL_HANDLER_ARGS) { struct bcm_pwm_softc *sc; uint32_t r; int error; sc = (struct bcm_pwm_softc *)arg1; r = sc->ratio; error = sysctl_handle_int(oidp, &r, sizeof(r), req); if (error != 0 || req->newptr == NULL) return (error); if (r > sc->period) // XXX >= ? return (EINVAL); sc->ratio = r; BCM_PWM_MEM_WRITE(sc, 0x14, sc->ratio); return (0); } static int bcm_pwm_reg_proc(SYSCTL_HANDLER_ARGS) { struct bcm_pwm_softc *sc; uint32_t reg; int error; sc = (struct bcm_pwm_softc *)arg1; reg = BCM_PWM_MEM_READ(sc, arg2 & 0xff); error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_PWM_MEM_WRITE(sc, arg2, reg); return (0); } static void bcm_pwm_sysctl_init(struct bcm_pwm_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); if (bootverbose) { #define RR(x,y) \ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, y, \ CTLFLAG_RW | CTLTYPE_UINT, sc, 0x##x, \ bcm_pwm_reg_proc, "IU", "Register 0x" #x " " y); RR(24, "DAT2") RR(20, "RNG2") RR(18, "FIF1") RR(14, "DAT1") RR(10, "RNG1") RR(08, "DMAC") RR(04, "STA") RR(00, "CTL") #undef RR } SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "pwm_freq", CTLFLAG_RD | CTLTYPE_UINT, sc, 0, bcm_pwm_pwm_freq_proc, "IU", "PWM frequency (Hz)"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "period", CTLFLAG_RW | CTLTYPE_UINT, sc, 0, bcm_pwm_period_proc, "IU", "PWM period (#clocks)"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "ratio", CTLFLAG_RW | CTLTYPE_UINT, sc, 0, bcm_pwm_ratio_proc, "IU", "PWM ratio (0...period)"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "freq", CTLFLAG_RW | CTLTYPE_UINT, sc, 0, bcm_pwm_freq_proc, "IU", "PWM clock (Hz)"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "mode", CTLFLAG_RW | CTLTYPE_UINT, sc, 0, bcm_pwm_mode_proc, "IU", "PWM mode (0=off, 1=pwm, 2=dither)"); } static int bcm_pwm_probe(device_t dev) { #if 0 // XXX: default state is disabled in RPI3 DTB, assume for now // XXX: that people want the PWM to work if the KLD this module. if (!ofw_bus_status_okay(dev)) return (ENXIO); #endif if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 PWM controller"); return (BUS_PROBE_DEFAULT); } static int bcm_pwm_attach(device_t dev) { struct bcm_pwm_softc *sc; int rid; if (device_get_unit(dev) != 0) { device_printf(dev, "only one PWM controller supported\n"); return (ENXIO); } sc = device_get_softc(dev); sc->sc_dev = dev; sc->clkman = devclass_get_device(devclass_find("bcm2835_clkman"), 0); if (sc->clkman == NULL) { device_printf(dev, "cannot find Clock Manager\n"); return (ENXIO); } rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_m_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_m_bsh = rman_get_bushandle(sc->sc_mem_res); /* Add sysctl nodes. */ bcm_pwm_sysctl_init(sc); sc->freq = 125000000; sc->period = 10000; sc->ratio = 2500; return (bus_generic_attach(dev)); } static int bcm_pwm_detach(device_t dev) { struct bcm_pwm_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); sc->mode = 0; (void)bcm_pwm_reconf(sc); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static phandle_t bcm_pwm_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } static device_method_t bcm_pwm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_pwm_probe), DEVMETHOD(device_attach, bcm_pwm_attach), DEVMETHOD(device_detach, bcm_pwm_detach), DEVMETHOD(ofw_bus_get_node, bcm_pwm_get_node), DEVMETHOD_END }; static devclass_t bcm_pwm_devclass; static driver_t bcm_pwm_driver = { "pwm", bcm_pwm_methods, sizeof(struct bcm_pwm_softc), }; DRIVER_MODULE(bcm2835_pwm, simplebus, bcm_pwm_driver, bcm_pwm_devclass, 0, 0); MODULE_DEPEND(bcm2835_pwm, bcm2835_clkman, 1, 1, 1); Index: head/sys/arm/broadcom/bcm2835/bcm2835_spi.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 332262) @@ -1,530 +1,519 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include "spibus_if.h" static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-spi", 1}, {"brcm,bcm2835-spi", 1}, {NULL, 0} }; static void bcm_spi_intr(void *); #ifdef BCM_SPI_DEBUG static void bcm_spi_printr(device_t dev) { struct bcm_spi_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = BCM_SPI_READ(sc, SPI_CS); device_printf(dev, "CS=%b\n", reg, "\20\1CS0\2CS1\3CPHA\4CPOL\7CSPOL" "\10TA\11DMAEN\12INTD\13INTR\14ADCS\15REN\16LEN" "\21DONE\22RXD\23TXD\24RXR\25RXF\26CSPOL0\27CSPOL1" "\30CSPOL2\31DMA_LEN\32LEN_LONG"); reg = BCM_SPI_READ(sc, SPI_CLK) & SPI_CLK_MASK; if (reg % 2) reg--; if (reg == 0) reg = 65536; device_printf(dev, "CLK=%uMhz/%d=%luhz\n", SPI_CORE_CLK / 1000000, reg, SPI_CORE_CLK / reg); reg = BCM_SPI_READ(sc, SPI_DLEN) & SPI_DLEN_MASK; device_printf(dev, "DLEN=%d\n", reg); reg = BCM_SPI_READ(sc, SPI_LTOH) & SPI_LTOH_MASK; device_printf(dev, "LTOH=%d\n", reg); reg = BCM_SPI_READ(sc, SPI_DC); device_printf(dev, "DC=RPANIC=%#x RDREQ=%#x TPANIC=%#x TDREQ=%#x\n", (reg & SPI_DC_RPANIC_MASK) >> SPI_DC_RPANIC_SHIFT, (reg & SPI_DC_RDREQ_MASK) >> SPI_DC_RDREQ_SHIFT, (reg & SPI_DC_TPANIC_MASK) >> SPI_DC_TPANIC_SHIFT, (reg & SPI_DC_TDREQ_MASK) >> SPI_DC_TDREQ_SHIFT); } #endif static void bcm_spi_modifyreg(struct bcm_spi_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_SPI_READ(sc, off); reg &= ~mask; reg |= value; BCM_SPI_WRITE(sc, off, reg); } static int bcm_spi_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_spi_softc *sc; uint32_t clk; int error; sc = (struct bcm_spi_softc *)arg1; BCM_SPI_LOCK(sc); clk = BCM_SPI_READ(sc, SPI_CLK); BCM_SPI_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 65536; clk = SPI_CORE_CLK / clk; error = sysctl_handle_int(oidp, &clk, sizeof(clk), req); if (error != 0 || req->newptr == NULL) return (error); clk = SPI_CORE_CLK / clk; if (clk <= 1) clk = 2; else if (clk % 2) clk--; if (clk > 0xffff) clk = 0; BCM_SPI_LOCK(sc); BCM_SPI_WRITE(sc, SPI_CLK, clk); BCM_SPI_UNLOCK(sc); return (0); } static int bcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit) { struct bcm_spi_softc *sc; uint32_t reg; int error; sc = (struct bcm_spi_softc *)arg1; BCM_SPI_LOCK(sc); reg = BCM_SPI_READ(sc, SPI_CS); BCM_SPI_UNLOCK(sc); reg = (reg & bit) ? 1 : 0; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); if (reg) reg = bit; BCM_SPI_LOCK(sc); bcm_spi_modifyreg(sc, SPI_CS, bit, reg); BCM_SPI_UNLOCK(sc); return (0); } static int bcm_spi_cpol_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPOL)); } static int bcm_spi_cpha_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPHA)); } static int bcm_spi_cspol0_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL0)); } static int bcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); } static void bcm_spi_sysctl_init(struct bcm_spi_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_clock_proc, "IU", "SPI BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpha_proc, "IU", "SPI BUS clock phase"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); } static int bcm_spi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 SPI controller"); return (BUS_PROBE_DEFAULT); } static int bcm_spi_attach(device_t dev) { struct bcm_spi_softc *sc; - device_t gpio; - int i, rid; + int rid; if (device_get_unit(dev) != 0) { device_printf(dev, "only one SPI controller supported\n"); return (ENXIO); } sc = device_get_softc(dev); sc->sc_dev = dev; - - /* Configure the GPIO pins to ALT0 function to enable SPI the pins. */ - gpio = devclass_get_device(devclass_find("gpio"), 0); - if (!gpio) { - device_printf(dev, "cannot find gpio0\n"); - return (ENXIO); - } - for (i = 0; i < nitems(bcm_spi_pins); i++) - bcm_gpio_set_alternate(gpio, bcm_spi_pins[i], BCM_GPIO_ALT0); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_spi_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_spi", NULL, MTX_DEF); /* Add sysctl nodes. */ bcm_spi_sysctl_init(sc); #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif /* * Enable the SPI controller. Clear the rx and tx FIFO. * Defaults to SPI mode 0. */ BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); /* Set the SPI clock to 500Khz. */ BCM_SPI_WRITE(sc, SPI_CLK, SPI_CORE_CLK / 500000); #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } static int bcm_spi_detach(device_t dev) { struct bcm_spi_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_spi_fill_fifo(struct bcm_spi_softc *sc) { struct spi_command *cmd; uint32_t cs, written; uint8_t *data; cmd = sc->sc_cmd; cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); while (sc->sc_written < sc->sc_len && cs == (SPI_CS_TA | SPI_CS_TXD)) { data = (uint8_t *)cmd->tx_cmd; written = sc->sc_written++; if (written >= cmd->tx_cmd_sz) { data = (uint8_t *)cmd->tx_data; written -= cmd->tx_cmd_sz; } BCM_SPI_WRITE(sc, SPI_FIFO, data[written]); cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); } } static void bcm_spi_drain_fifo(struct bcm_spi_softc *sc) { struct spi_command *cmd; uint32_t cs, read; uint8_t *data; cmd = sc->sc_cmd; cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; while (sc->sc_read < sc->sc_len && cs == SPI_CS_RXD) { data = (uint8_t *)cmd->rx_cmd; read = sc->sc_read++; if (read >= cmd->rx_cmd_sz) { data = (uint8_t *)cmd->rx_data; read -= cmd->rx_cmd_sz; } data[read] = BCM_SPI_READ(sc, SPI_FIFO) & 0xff; cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; } } static void bcm_spi_intr(void *arg) { struct bcm_spi_softc *sc; sc = (struct bcm_spi_softc *)arg; BCM_SPI_LOCK(sc); /* Filter stray interrupts. */ if ((sc->sc_flags & BCM_SPI_BUSY) == 0) { BCM_SPI_UNLOCK(sc); return; } /* TX - Fill up the FIFO. */ bcm_spi_fill_fifo(sc); /* RX - Drain the FIFO. */ bcm_spi_drain_fifo(sc); /* Check for end of transfer. */ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { /* Disable interrupts and the SPI engine. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); wakeup(sc->sc_dev); } BCM_SPI_UNLOCK(sc); } static int bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct bcm_spi_softc *sc; uint32_t cs; int err; sc = device_get_softc(dev); KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, ("TX/RX command sizes should be equal")); KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("TX/RX data sizes should be equal")); /* Get the proper chip select for this child. */ spibus_get_cs(child, &cs); cs &= ~SPIBUS_CS_HIGH; if (cs > 2) { device_printf(dev, "Invalid chip select %d requested by %s\n", cs, device_get_nameunit(child)); return (EINVAL); } BCM_SPI_LOCK(sc); /* If the controller is in use wait until it is available. */ while (sc->sc_flags & BCM_SPI_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); /* Now we have control over SPI controller. */ sc->sc_flags = BCM_SPI_BUSY; /* Clear the FIFO. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); /* Save a pointer to the SPI command. */ sc->sc_cmd = cmd; sc->sc_read = 0; sc->sc_written = 0; sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; /* * Set the CS for this transaction, enable interrupts and announce * we're ready to tx. This will kick off the first interrupt. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, cs | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); /* Make sure the SPI engine and interrupts are disabled. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); /* Release the controller and wakeup the next thread waiting for it. */ sc->sc_flags = 0; wakeup_one(dev); BCM_SPI_UNLOCK(sc); /* * Check for transfer timeout. The SPI controller doesn't * return errors. */ if (err == EWOULDBLOCK) { device_printf(sc->sc_dev, "SPI error\n"); err = EIO; } return (err); } static phandle_t bcm_spi_get_node(device_t bus, device_t dev) { /* We only have one child, the SPI bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_spi_probe), DEVMETHOD(device_attach, bcm_spi_attach), DEVMETHOD(device_detach, bcm_spi_detach), /* SPI interface */ DEVMETHOD(spibus_transfer, bcm_spi_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_spi_get_node), DEVMETHOD_END }; static devclass_t bcm_spi_devclass; static driver_t bcm_spi_driver = { "spi", bcm_spi_methods, sizeof(struct bcm_spi_softc), }; DRIVER_MODULE(bcm2835_spi, simplebus, bcm_spi_driver, bcm_spi_devclass, 0, 0); Index: head/sys/arm/broadcom/bcm2835/bcm2835_spivar.h =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_spivar.h (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2835_spivar.h (revision 332262) @@ -1,74 +1,62 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _BCM2835_SPIVAR_H_ #define _BCM2835_SPIVAR_H_ -/* - * Only the available pins are listed here. - * i.e. CS2 isn't available. - */ -uint32_t bcm_spi_pins[] = { - 7, /* CS1 */ - 8, /* CS0 */ - 9, /* MISO */ - 10, /* MOSI */ - 11 /* SCLK */ -}; - struct bcm_spi_softc { device_t sc_dev; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; struct spi_command *sc_cmd; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; uint32_t sc_len; uint32_t sc_read; uint32_t sc_flags; uint32_t sc_written; void * sc_intrhand; }; #define BCM_SPI_BUSY 0x1 #define BCM_SPI_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define BCM_SPI_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) #define BCM_SPI_LOCK(_sc) \ mtx_lock(&(_sc)->sc_mtx) #define BCM_SPI_UNLOCK(_sc) \ mtx_unlock(&(_sc)->sc_mtx) #endif /* _BCM2835_SPIVAR_H_ */ Index: head/sys/arm/broadcom/bcm2835/bcm2836.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2836.c (revision 332261) +++ head/sys/arm/broadcom/bcm2835/bcm2836.c (revision 332262) @@ -1,739 +1,739 @@ /* * Copyright 2015 Andrew Turner. * Copyright 2016 Svatopluk Kraus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #ifdef SMP #include #endif #include #include #include #ifdef SMP #include #endif #include #include #include "pic_if.h" #define BCM_LINTC_CONTROL_REG 0x00 #define BCM_LINTC_PRESCALER_REG 0x08 #define BCM_LINTC_GPU_ROUTING_REG 0x0c #define BCM_LINTC_PMU_ROUTING_SET_REG 0x10 #define BCM_LINTC_PMU_ROUTING_CLR_REG 0x14 #define BCM_LINTC_TIMER_CFG_REG(n) (0x40 + (n) * 4) #define BCM_LINTC_MBOX_CFG_REG(n) (0x50 + (n) * 4) #define BCM_LINTC_PENDING_REG(n) (0x60 + (n) * 4) #define BCM_LINTC_MBOX0_SET_REG(n) (0x80 + (n) * 16) #define BCM_LINTC_MBOX1_SET_REG(n) (0x84 + (n) * 16) #define BCM_LINTC_MBOX2_SET_REG(n) (0x88 + (n) * 16) #define BCM_LINTC_MBOX3_SET_REG(n) (0x8C + (n) * 16) #define BCM_LINTC_MBOX0_CLR_REG(n) (0xC0 + (n) * 16) #define BCM_LINTC_MBOX1_CLR_REG(n) (0xC4 + (n) * 16) #define BCM_LINTC_MBOX2_CLR_REG(n) (0xC8 + (n) * 16) #define BCM_LINTC_MBOX3_CLR_REG(n) (0xCC + (n) * 16) /* Prescaler Register */ #define BCM_LINTC_PSR_19_2 0x80000000 /* 19.2 MHz */ /* GPU Interrupt Routing Register */ #define BCM_LINTC_GIRR_IRQ_CORE(n) (n) #define BCM_LINTC_GIRR_FIQ_CORE(n) ((n) << 2) /* PMU Interrupt Routing Register */ #define BCM_LINTC_PIRR_IRQ_EN_CORE(n) (1 << (n)) #define BCM_LINTC_PIRR_FIQ_EN_CORE(n) (1 << ((n) + 4)) /* Timer Config Register */ #define BCM_LINTC_TCR_IRQ_EN_TIMER(n) (1 << (n)) #define BCM_LINTC_TCR_FIQ_EN_TIMER(n) (1 << ((n) + 4)) /* MBOX Config Register */ #define BCM_LINTC_MCR_IRQ_EN_MBOX(n) (1 << (n)) #define BCM_LINTC_MCR_FIQ_EN_MBOX(n) (1 << ((n) + 4)) #define BCM_LINTC_CNTPSIRQ_IRQ 0 #define BCM_LINTC_CNTPNSIRQ_IRQ 1 #define BCM_LINTC_CNTHPIRQ_IRQ 2 #define BCM_LINTC_CNTVIRQ_IRQ 3 #define BCM_LINTC_MBOX0_IRQ 4 #define BCM_LINTC_MBOX1_IRQ 5 #define BCM_LINTC_MBOX2_IRQ 6 #define BCM_LINTC_MBOX3_IRQ 7 #define BCM_LINTC_GPU_IRQ 8 #define BCM_LINTC_PMU_IRQ 9 #define BCM_LINTC_AXI_IRQ 10 #define BCM_LINTC_LTIMER_IRQ 11 #define BCM_LINTC_NIRQS 12 #define BCM_LINTC_TIMER0_IRQ BCM_LINTC_CNTPSIRQ_IRQ #define BCM_LINTC_TIMER1_IRQ BCM_LINTC_CNTPNSIRQ_IRQ #define BCM_LINTC_TIMER2_IRQ BCM_LINTC_CNTHPIRQ_IRQ #define BCM_LINTC_TIMER3_IRQ BCM_LINTC_CNTVIRQ_IRQ #define BCM_LINTC_TIMER0_IRQ_MASK (1 << BCM_LINTC_TIMER0_IRQ) #define BCM_LINTC_TIMER1_IRQ_MASK (1 << BCM_LINTC_TIMER1_IRQ) #define BCM_LINTC_TIMER2_IRQ_MASK (1 << BCM_LINTC_TIMER2_IRQ) #define BCM_LINTC_TIMER3_IRQ_MASK (1 << BCM_LINTC_TIMER3_IRQ) #define BCM_LINTC_MBOX0_IRQ_MASK (1 << BCM_LINTC_MBOX0_IRQ) #define BCM_LINTC_GPU_IRQ_MASK (1 << BCM_LINTC_GPU_IRQ) #define BCM_LINTC_PMU_IRQ_MASK (1 << BCM_LINTC_PMU_IRQ) #define BCM_LINTC_UP_PENDING_MASK \ (BCM_LINTC_TIMER0_IRQ_MASK | \ BCM_LINTC_TIMER1_IRQ_MASK | \ BCM_LINTC_TIMER2_IRQ_MASK | \ BCM_LINTC_TIMER3_IRQ_MASK | \ BCM_LINTC_GPU_IRQ_MASK | \ BCM_LINTC_PMU_IRQ_MASK) #define BCM_LINTC_SMP_PENDING_MASK \ (BCM_LINTC_UP_PENDING_MASK | \ BCM_LINTC_MBOX0_IRQ_MASK) #ifdef SMP #define BCM_LINTC_PENDING_MASK BCM_LINTC_SMP_PENDING_MASK #else #define BCM_LINTC_PENDING_MASK BCM_LINTC_UP_PENDING_MASK #endif struct bcm_lintc_irqsrc { struct intr_irqsrc bli_isrc; u_int bli_irq; union { u_int bli_mask; /* for timers */ u_int bli_value; /* for GPU */ }; }; struct bcm_lintc_softc { device_t bls_dev; struct mtx bls_mtx; struct resource * bls_mem; bus_space_tag_t bls_bst; bus_space_handle_t bls_bsh; struct bcm_lintc_irqsrc bls_isrcs[BCM_LINTC_NIRQS]; }; static struct bcm_lintc_softc *bcm_lintc_sc; #ifdef SMP #define BCM_LINTC_NIPIS 32 /* only mailbox 0 is used for IPI */ CTASSERT(INTR_IPI_COUNT <= BCM_LINTC_NIPIS); #endif #define BCM_LINTC_LOCK(sc) mtx_lock_spin(&(sc)->bls_mtx) #define BCM_LINTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->bls_mtx) #define BCM_LINTC_LOCK_INIT(sc) mtx_init(&(sc)->bls_mtx, \ device_get_nameunit((sc)->bls_dev), "bmc_local_intc", MTX_SPIN) #define BCM_LINTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->bls_mtx) #define bcm_lintc_read_4(sc, reg) \ bus_space_read_4((sc)->bls_bst, (sc)->bls_bsh, (reg)) #define bcm_lintc_write_4(sc, reg, val) \ bus_space_write_4((sc)->bls_bst, (sc)->bls_bsh, (reg), (val)) static inline void bcm_lintc_rwreg_clr(struct bcm_lintc_softc *sc, uint32_t reg, uint32_t mask) { bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) & ~mask); } static inline void bcm_lintc_rwreg_set(struct bcm_lintc_softc *sc, uint32_t reg, uint32_t mask) { bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) | mask); } static void bcm_lintc_timer_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) bcm_lintc_rwreg_clr(sc, BCM_LINTC_TIMER_CFG_REG(cpu), bli->bli_mask); BCM_LINTC_UNLOCK(sc); } static void bcm_lintc_timer_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) bcm_lintc_rwreg_set(sc, BCM_LINTC_TIMER_CFG_REG(cpu), bli->bli_mask); BCM_LINTC_UNLOCK(sc); } static inline void bcm_lintc_gpu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { /* It's accessed just and only by one core. */ bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, 0); } static inline void bcm_lintc_gpu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { /* It's accessed just and only by one core. */ bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, bli->bli_value); } static inline void bcm_lintc_pmu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu, mask; mask = 0; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu); /* Write-clear register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_CLR_REG, mask); BCM_LINTC_UNLOCK(sc); } static inline void bcm_lintc_pmu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { cpuset_t *cpus; uint32_t cpu, mask; mask = 0; cpus = &bli->bli_isrc.isrc_cpu; BCM_LINTC_LOCK(sc); for (cpu = 0; cpu < 4; cpu++) if (CPU_ISSET(cpu, cpus)) mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu); /* Write-set register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, mask); BCM_LINTC_UNLOCK(sc); } static void bcm_lintc_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { switch (bli->bli_irq) { case BCM_LINTC_TIMER0_IRQ: case BCM_LINTC_TIMER1_IRQ: case BCM_LINTC_TIMER2_IRQ: case BCM_LINTC_TIMER3_IRQ: bcm_lintc_timer_mask(sc, bli); return; case BCM_LINTC_MBOX0_IRQ: case BCM_LINTC_MBOX1_IRQ: case BCM_LINTC_MBOX2_IRQ: case BCM_LINTC_MBOX3_IRQ: return; case BCM_LINTC_GPU_IRQ: bcm_lintc_gpu_mask(sc, bli); return; case BCM_LINTC_PMU_IRQ: bcm_lintc_pmu_mask(sc, bli); return; default: panic("%s: not implemented for irq %u", __func__, bli->bli_irq); } } static void bcm_lintc_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) { switch (bli->bli_irq) { case BCM_LINTC_TIMER0_IRQ: case BCM_LINTC_TIMER1_IRQ: case BCM_LINTC_TIMER2_IRQ: case BCM_LINTC_TIMER3_IRQ: bcm_lintc_timer_unmask(sc, bli); return; case BCM_LINTC_MBOX0_IRQ: case BCM_LINTC_MBOX1_IRQ: case BCM_LINTC_MBOX2_IRQ: case BCM_LINTC_MBOX3_IRQ: return; case BCM_LINTC_GPU_IRQ: bcm_lintc_gpu_unmask(sc, bli); return; case BCM_LINTC_PMU_IRQ: bcm_lintc_pmu_unmask(sc, bli); return; default: panic("%s: not implemented for irq %u", __func__, bli->bli_irq); } } #ifdef SMP static inline void bcm_lintc_ipi_write(struct bcm_lintc_softc *sc, cpuset_t cpus, u_int ipi) { u_int cpu; uint32_t mask; mask = 1 << ipi; for (cpu = 0; cpu < mp_ncpus; cpu++) if (CPU_ISSET(cpu, &cpus)) bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_SET_REG(cpu), mask); } static inline void bcm_lintc_ipi_dispatch(struct bcm_lintc_softc *sc, u_int cpu, struct trapframe *tf) { u_int ipi; uint32_t mask; mask = bcm_lintc_read_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu)); if (mask == 0) { device_printf(sc->bls_dev, "Spurious ipi detected\n"); return; } for (ipi = 0; mask != 0; mask >>= 1, ipi++) { if ((mask & 0x01) == 0) continue; /* * Clear an IPI before dispatching to not miss anyone * and make sure that it's observed by everybody. */ bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu), 1 << ipi); #if defined(__aarch64__) dsb(sy); #else dsb(); #endif intr_ipi_dispatch(ipi, tf); } } #endif static inline void bcm_lintc_irq_dispatch(struct bcm_lintc_softc *sc, u_int irq, struct trapframe *tf) { struct bcm_lintc_irqsrc *bli; bli = &sc->bls_isrcs[irq]; if (intr_isrc_dispatch(&bli->bli_isrc, tf) != 0) device_printf(sc->bls_dev, "Stray irq %u detected\n", irq); } static int bcm_lintc_intr(void *arg) { struct bcm_lintc_softc *sc; u_int cpu; uint32_t num, reg; struct trapframe *tf; sc = arg; cpu = PCPU_GET(cpuid); tf = curthread->td_intr_frame; for (num = 0; ; num++) { reg = bcm_lintc_read_4(sc, BCM_LINTC_PENDING_REG(cpu)); if ((reg & BCM_LINTC_PENDING_MASK) == 0) break; #ifdef SMP if (reg & BCM_LINTC_MBOX0_IRQ_MASK) bcm_lintc_ipi_dispatch(sc, cpu, tf); #endif if (reg & BCM_LINTC_TIMER0_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER0_IRQ, tf); if (reg & BCM_LINTC_TIMER1_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER1_IRQ, tf); if (reg & BCM_LINTC_TIMER2_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER2_IRQ, tf); if (reg & BCM_LINTC_TIMER3_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER3_IRQ, tf); if (reg & BCM_LINTC_GPU_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_GPU_IRQ, tf); if (reg & BCM_LINTC_PMU_IRQ_MASK) bcm_lintc_irq_dispatch(sc, BCM_LINTC_PMU_IRQ, tf); arm_irq_memory_barrier(0); /* XXX */ } reg &= ~BCM_LINTC_PENDING_MASK; if (reg != 0) device_printf(sc->bls_dev, "Unknown interrupt(s) %x\n", reg); else if (num == 0) device_printf(sc->bls_dev, "Spurious interrupt detected\n"); return (FILTER_HANDLED); } static void bcm_lintc_disable_intr(device_t dev, struct intr_irqsrc *isrc) { bcm_lintc_mask(device_get_softc(dev), (struct bcm_lintc_irqsrc *)isrc); } static void bcm_lintc_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; arm_irq_memory_barrier(bli->bli_irq); bcm_lintc_unmask(device_get_softc(dev), bli); } static int bcm_lintc_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct intr_map_data_fdt *daf; struct bcm_lintc_softc *sc; if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); daf = (struct intr_map_data_fdt *)data; if (daf->ncells != 1 || daf->cells[0] >= BCM_LINTC_NIRQS) return (EINVAL); sc = device_get_softc(dev); *isrcp = &sc->bls_isrcs[daf->cells[0]].bli_isrc; return (0); } static void bcm_lintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; if (bli->bli_irq == BCM_LINTC_GPU_IRQ) bcm_lintc_gpu_mask(device_get_softc(dev), bli); else { /* * Handler for PPI interrupt does not make sense much unless * there is one bound ithread for each core for it. Thus the * interrupt can be masked on current core only while ithread * bounded to this core ensures unmasking on the same core. */ panic ("%s: handlers are not supported", __func__); } } static void bcm_lintc_post_ithread(device_t dev, struct intr_irqsrc *isrc) { struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; if (bli->bli_irq == BCM_LINTC_GPU_IRQ) bcm_lintc_gpu_unmask(device_get_softc(dev), bli); else { /* See comment in bcm_lintc_pre_ithread(). */ panic ("%s: handlers are not supported", __func__); } } static void bcm_lintc_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static int bcm_lintc_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct bcm_lintc_softc *sc; if (isrc->isrc_handlers == 0 && isrc->isrc_flags & INTR_ISRCF_PPI) { sc = device_get_softc(dev); BCM_LINTC_LOCK(sc); CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); BCM_LINTC_UNLOCK(sc); } return (0); } #ifdef SMP static void bcm_lintc_init_rwreg_on_ap(struct bcm_lintc_softc *sc, u_int cpu, u_int irq, uint32_t reg, uint32_t mask) { if (intr_isrc_init_on_cpu(&sc->bls_isrcs[irq].bli_isrc, cpu)) bcm_lintc_rwreg_set(sc, reg, mask); } static void bcm_lintc_init_pmu_on_ap(struct bcm_lintc_softc *sc, u_int cpu) { struct intr_irqsrc *isrc = &sc->bls_isrcs[BCM_LINTC_PMU_IRQ].bli_isrc; if (intr_isrc_init_on_cpu(isrc, cpu)) { /* Write-set register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, BCM_LINTC_PIRR_IRQ_EN_CORE(cpu)); } } static void bcm_lintc_init_secondary(device_t dev) { u_int cpu; struct bcm_lintc_softc *sc; cpu = PCPU_GET(cpuid); sc = device_get_softc(dev); BCM_LINTC_LOCK(sc); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER0_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(0)); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER1_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(1)); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER2_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(2)); bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER3_IRQ, BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(3)); bcm_lintc_init_pmu_on_ap(sc, cpu); BCM_LINTC_UNLOCK(sc); } static void bcm_lintc_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, u_int ipi) { struct bcm_lintc_softc *sc = device_get_softc(dev); KASSERT(isrc == &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc, ("%s: bad ISRC %p argument", __func__, isrc)); bcm_lintc_ipi_write(sc, cpus, ipi); } static int bcm_lintc_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) { struct bcm_lintc_softc *sc = device_get_softc(dev); KASSERT(ipi < BCM_LINTC_NIPIS, ("%s: too high ipi %u", __func__, ipi)); *isrcp = &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc; return (0); } #endif static int bcm_lintc_pic_attach(struct bcm_lintc_softc *sc) { struct bcm_lintc_irqsrc *bisrcs; struct intr_pic *pic; int error; u_int flags; uint32_t irq; const char *name; intptr_t xref; bisrcs = sc->bls_isrcs; name = device_get_nameunit(sc->bls_dev); for (irq = 0; irq < BCM_LINTC_NIRQS; irq++) { bisrcs[irq].bli_irq = irq; switch (irq) { case BCM_LINTC_TIMER0_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(0); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_TIMER1_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(1); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_TIMER2_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(2); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_TIMER3_IRQ: bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(3); flags = INTR_ISRCF_PPI; break; case BCM_LINTC_MBOX0_IRQ: case BCM_LINTC_MBOX1_IRQ: case BCM_LINTC_MBOX2_IRQ: case BCM_LINTC_MBOX3_IRQ: bisrcs[irq].bli_value = 0; /* not used */ flags = INTR_ISRCF_IPI; break; case BCM_LINTC_GPU_IRQ: bisrcs[irq].bli_value = BCM_LINTC_GIRR_IRQ_CORE(0); flags = 0; break; case BCM_LINTC_PMU_IRQ: bisrcs[irq].bli_value = 0; /* not used */ flags = INTR_ISRCF_PPI; break; default: bisrcs[irq].bli_value = 0; /* not used */ flags = 0; break; } error = intr_isrc_register(&bisrcs[irq].bli_isrc, sc->bls_dev, flags, "%s,%u", name, irq); if (error != 0) return (error); } xref = OF_xref_from_node(ofw_bus_get_node(sc->bls_dev)); pic = intr_pic_register(sc->bls_dev, xref); if (pic == NULL) return (ENXIO); return (intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc, 0)); } static int bcm_lintc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "brcm,bcm2836-l1-intc")) return (ENXIO); device_set_desc(dev, "BCM2836 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_lintc_attach(device_t dev) { struct bcm_lintc_softc *sc; int cpu, rid; sc = device_get_softc(dev); sc->bls_dev = dev; if (bcm_lintc_sc != NULL) return (ENXIO); rid = 0; sc->bls_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bls_mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->bls_bst = rman_get_bustag(sc->bls_mem); sc->bls_bsh = rman_get_bushandle(sc->bls_mem); bcm_lintc_write_4(sc, BCM_LINTC_CONTROL_REG, 0); bcm_lintc_write_4(sc, BCM_LINTC_PRESCALER_REG, BCM_LINTC_PSR_19_2); /* Disable all timers on all cores. */ for (cpu = 0; cpu < 4; cpu++) bcm_lintc_write_4(sc, BCM_LINTC_TIMER_CFG_REG(cpu), 0); #ifdef SMP /* Enable mailbox 0 on all cores used for IPI. */ for (cpu = 0; cpu < 4; cpu++) bcm_lintc_write_4(sc, BCM_LINTC_MBOX_CFG_REG(cpu), BCM_LINTC_MCR_IRQ_EN_MBOX(0)); #endif if (bcm_lintc_pic_attach(sc) != 0) { device_printf(dev, "could not attach PIC\n"); return (ENXIO); } BCM_LINTC_LOCK_INIT(sc); bcm_lintc_sc = sc; return (0); } static device_method_t bcm_lintc_methods[] = { DEVMETHOD(device_probe, bcm_lintc_probe), DEVMETHOD(device_attach, bcm_lintc_attach), DEVMETHOD(pic_disable_intr, bcm_lintc_disable_intr), DEVMETHOD(pic_enable_intr, bcm_lintc_enable_intr), DEVMETHOD(pic_map_intr, bcm_lintc_map_intr), DEVMETHOD(pic_post_filter, bcm_lintc_post_filter), DEVMETHOD(pic_post_ithread, bcm_lintc_post_ithread), DEVMETHOD(pic_pre_ithread, bcm_lintc_pre_ithread), DEVMETHOD(pic_setup_intr, bcm_lintc_setup_intr), #ifdef SMP DEVMETHOD(pic_init_secondary, bcm_lintc_init_secondary), DEVMETHOD(pic_ipi_send, bcm_lintc_ipi_send), DEVMETHOD(pic_ipi_setup, bcm_lintc_ipi_setup), #endif DEVMETHOD_END }; static driver_t bcm_lintc_driver = { "local_intc", bcm_lintc_methods, sizeof(struct bcm_lintc_softc), }; static devclass_t bcm_lintc_devclass; EARLY_DRIVER_MODULE(local_intc, simplebus, bcm_lintc_driver, bcm_lintc_devclass, - 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); + 0, 0, BUS_PASS_INTERRUPT); Index: head/sys/arm/conf/RPI-B =================================================================== --- head/sys/arm/conf/RPI-B (revision 332261) +++ head/sys/arm/conf/RPI-B (revision 332262) @@ -1,99 +1,101 @@ # # RPI-B -- Custom configuration for the Raspberry Pi # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident RPI-B include "std.armv6" include "../broadcom/bcm2835/std.rpi" include "../broadcom/bcm2835/std.bcm2835" options INTRNG options SCHED_4BSD # 4BSD scheduler options PLATFORM # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=ue0 #options ROOTDEVNAME=\"ufs:mmcsd0s2\" device bpf device loop device ether device uart device pty device snp device pl011 # Device mode support device usb_template # Control of the gadget # Comment following lines for boot console on serial port device vt device kbdmux device ukbd device sdhci device mmc device mmcsd device gpio device gpioled # I2C device iic device iicbus device bcm2835_bsc device md device random # Entropy device # USB support device usb device dwcotg # DWC OTG controller # USB storage support device scbus device da device umass # USB ethernet support device smcphy device mii device smsc # SPI device spibus device bcm2835_spi device vchiq device sound +device fdt_pinctrl + # Flattened Device Tree options FDT # Configure using FDT/DTB data # Note: DTB is normally loaded and modified by RPi boot loader, then # handed to kernel via U-Boot and ubldr. #options FDT_DTB_STATIC #makeoptions FDT_DTS_FILE=rpi.dts makeoptions MODULES_EXTRA="dtb/rpi rpi_ft5406" Index: head/sys/arm/conf/RPI2 =================================================================== --- head/sys/arm/conf/RPI2 (revision 332261) +++ head/sys/arm/conf/RPI2 (revision 332262) @@ -1,102 +1,104 @@ # # RPI2 -- Custom configuration for the Raspberry Pi 2 # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ # #NO_UNIVERSE ident RPI2 include "std.armv7" include "../broadcom/bcm2835/std.rpi" include "../broadcom/bcm2835/std.bcm2836" options INTRNG options SCHED_ULE # ULE scheduler options SMP # Enable multiple cores options PLATFORM # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=ue0 options ROOTDEVNAME=\"ufs:mmcsd0s2\" # ARM Generic Timer device generic_timer device bpf device loop device ether device uart device pty device snp device pl011 # Comment following lines for boot console on serial port device vt device kbdmux device ukbd device sdhci device mmc device mmcsd device gpio device gpioled # I2C device iic device iicbus device bcm2835_bsc device md device random # Entropy device # USB support device usb device dwcotg # DWC OTG controller # USB storage support device scbus device da device umass # USB ethernet support device smcphy device mii device smsc # SPI device spibus device bcm2835_spi device vchiq device sound +device fdt_pinctrl + # Flattened Device Tree options FDT # Configure using FDT/DTB data # Note: DTB is normally loaded and modified by RPi boot loader, then # handed to kernel via U-Boot and ubldr. #options FDT_DTB_STATIC #makeoptions FDT_DTS_FILE=rpi2.dts makeoptions MODULES_EXTRA="dtb/rpi rpi_ft5406" Index: head/sys/dts/arm/rpi.dts =================================================================== --- head/sys/dts/arm/rpi.dts (revision 332261) +++ head/sys/dts/arm/rpi.dts (revision 332262) @@ -1,92 +1,105 @@ /* * Copyright (c) 2012 Oleksandr Tymoshenko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "bcm2835-rpi-b.dts" / { /* This is only necessary for FDT_DTB_STATIC */ memory { device_type = "memory"; reg = <0 0x8000000>; /* 128MB, Set by VideoCore */ }; rpi_ft5406 { compatible = "rpi,rpi-ft5406"; status = "okay"; }; /* Temporary patches */ soc { sdhost: mmc@7e202000 { status = "disabled"; } sdhci@7e300000 { status = "okay"; }; spi@7e204000 { status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; }; gpio@7e200000 { /* Pins that can short 3.3V to GND in output mode: 46-47 * Pins used by VideoCore: 48-53 */ broadcom,read-only = <46>, <47>, <48>, <49>, <50>, <51>, <52>, <53>; /* Reserved */ pins_reserved: reserved { broadcom,pins = <48>, <49>, <50>, <51>, <52>, <53>; }; + + spi0_pins: spi0_pins { + brcm,pins = <9 10 11>; + brcm,function = <4>; /* alt0 */ + }; + + spi0_cs_pins: spi0_cs_pins { + brcm,pins = <8 7>; + brcm,function = <1>; /* output */ + }; } vchiq { compatible = "brcm,bcm2835-vchiq"; reg = <0x7e00b840 0xf>; interrupts = <0 2>; cache-line-size = <32>; }; display { compatible = "broadcom,bcm2835-fb", "broadcom,bcm2708-fb"; broadcom,vc-mailbox = <&mailbox>; broadcom,vc-channel = <1>; broadcom,width = <0>; /* Set by VideoCore */ broadcom,height = <0>; /* Set by VideoCore */ broadcom,depth = <0>; /* Set by VideoCore */ }; }; }; - Index: head/sys/dts/arm/rpi2.dts =================================================================== --- head/sys/dts/arm/rpi2.dts (revision 332261) +++ head/sys/dts/arm/rpi2.dts (revision 332262) @@ -1,91 +1,103 @@ /* * Copyright (c) 2012 Oleksandr Tymoshenko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "bcm2836-rpi-2-b.dts" / { /* This is only necessary for FDT_DTB_STATIC */ memory { device_type = "memory"; reg = <0 0x8000000>; /* 128MB, Set by VideoCore */ }; rpi_ft5406 { compatible = "rpi,rpi-ft5406"; status = "okay"; }; /* Temporary patches */ soc { sdhost: mmc@7e202000 { status = "disabled"; } sdhci@7e300000 { status = "okay"; }; spi@7e204000 { status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; }; gpio@7e200000 { /* Pins that can short 3.3V to GND in output mode: 46-47 * Pins used by VideoCore: 48-53 */ broadcom,read-only = <46>, <47>, <48>, <49>, <50>, <51>, <52>, <53>; /* Reserved */ pins_reserved: reserved { broadcom,pins = <48>, <49>, <50>, <51>, <52>, <53>; + }; + + spi0_pins: spi0_pins { + brcm,pins = <9 10 11>; + brcm,function = <4>; /* alt0 */ + }; + + spi0_cs_pins: spi0_cs_pins { + brcm,pins = <8 7>; + brcm,function = <1>; /* output */ }; } vchiq { compatible = "brcm,bcm2835-vchiq"; reg = <0x7e00b840 0xf>; interrupts = <0 2>; cache-line-size = <32>; }; display { compatible = "broadcom,bcm2835-fb", "broadcom,bcm2708-fb"; broadcom,vc-mailbox = <&mailbox>; broadcom,vc-channel = <1>; broadcom,width = <0>; /* Set by VideoCore */ broadcom,height = <0>; /* Set by VideoCore */ broadcom,depth = <0>; /* Set by VideoCore */ }; }; };