Date: Thu, 22 Aug 2013 10:57:51 -0300 From: Luiz Otavio O Souza <loos.br@gmail.com> To: freebsd-arm@freebsd.org Subject: SPI driver for RPi Message-ID: <126AAEEA-1F99-42E4-9620-9CB4F3610671@gmail.com>
next in thread | raw e-mail | index | archive | help
--Apple-Mail-247--559286389 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Hello, Here is a SPI driver for RPi. It has been tested with a hardware loopback (MOSI wired to MISO) and = with a s25fl032 spansion flash (removed from a wr941nd router). I've tested the reading from the flash up to 10Mhz of clock and = surprisingly my (simple) wiring support it without any fail. At boot the SPI clock is set to 500Khz, not fast and not too slow (as = the 3.814Khz default): # dd if=3D/dev/flash/spi0 of=3Dflash-wr941nd-2 bs=3D64k 64+0 records in 64+0 records out 4194304 bytes transferred in 75.632316 secs (55457 bytes/sec) # diff flash-wr941nd flash-wr941nd-2 # sysctl dev.spi.0.clock=3D1000000 dev.spi.0.clock: 500000 -> 1000000 # dd if=3D/dev/flash/spi0 of=3Dflash-wr941nd-2 bs=3D64k 64+0 records in 64+0 records out 4194304 bytes transferred in 37.896805 secs (110677 bytes/sec) # diff flash-wr941nd flash-wr941nd-2 =20 # sysctl dev.spi.0.clock=3D2000000 =20 dev.spi.0.clock: 1000000 -> 2016129 # dd if=3D/dev/flash/spi0 of=3Dflash-wr941nd-2 bs=3D64k 64+0 records in 64+0 records out 4194304 bytes transferred in 18.879980 secs (222156 bytes/sec) # diff flash-wr941nd flash-wr941nd-2 =20 # sysctl dev.spi.0.clock=3D4000000 =20 dev.spi.0.clock: 2016129 -> 4032258 # dd if=3D/dev/flash/spi0 of=3Dflash-wr941nd-2 bs=3D64k 64+0 records in 64+0 records out 4194304 bytes transferred in 9.642490 secs (434981 bytes/sec) # diff flash-wr941nd flash-wr941nd-2 =20 # sysctl dev.spi.0.clock=3D10000000 =20 dev.spi.0.clock: 4032258 -> 10416666 # dd if=3D/dev/flash/spi0 of=3Dflash-wr941nd-2 bs=3D64k 64+0 records in 64+0 records out 4194304 bytes transferred in 4.317743 secs (971411 bytes/sec) # diff flash-wr941nd flash-wr941nd-2 =20 It export a few handy sysctl knobs: # sysctl dev.spi dev.spi.0.%desc: BCM2708/2835 SPI controller dev.spi.0.%driver: spi dev.spi.0.%parent: simplebus0 dev.spi.0.clock: 500000 dev.spi.0.cpol: 0 dev.spi.0.cpha: 0 dev.spi.0.cspol0: 0 dev.spi.0.cspol1: 0 About the patches: - bcm2835_spi.diff implements the SPI driver, the dts and kernel = changes; - ofw_spibus.diff adds the OFW SPI bus glue to attach the SPI children = as described in the FDT. - mx25l-fdt-intr.diff adds the support for FDT and configure a intr hook = so the device identification runs only when the interrupts are active = (the SPI driver is interrupt based); - rpi-mx25l-dts.diff the change i did to add my mx25l on the rpi dts = (only as reference). Luiz --Apple-Mail-247--559286389 Content-Disposition: attachment; filename=bcm2835_spi.diff Content-Type: application/octet-stream; name="bcm2835_spi.diff" Content-Transfer-Encoding: 7bit --- /dev/null 2013-08-22 00:33:00.000000000 -0300 +++ sys/arm/broadcom/bcm2835/bcm2835_spi.c 2013-08-20 15:57:57.928470462 -0300 @@ -0,0 +1,592 @@ +/*- + * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> + * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/cpufunc.h> +#include <machine/resource.h> +#include <machine/fdt.h> +#include <machine/frame.h> +#include <machine/intr.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/spibus/spi.h> +#include <dev/spibus/spibusvar.h> + +#include <arm/broadcom/bcm2835/bcm2835_gpio.h> + +#include "spibus_if.h" + +/* Only the accessible pins are listed here. */ +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 SPI_CORE_CLK 250000000U +#define SPI_CS 0x0 +#define SPI_CS_LEN_LONG (1 << 25) +#define SPI_CS_DMA_LEN (1 << 24) +#define SPI_CS_CSPOL2 (1 << 23) +#define SPI_CS_CSPOL1 (1 << 22) +#define SPI_CS_CSPOL0 (1 << 21) +#define SPI_CS_RXF (1 << 20) +#define SPI_CS_RXR (1 << 19) +#define SPI_CS_TXD (1 << 18) +#define SPI_CS_RXD (1 << 17) +#define SPI_CS_DONE (1 << 16) +#define SPI_CS_LEN (1 << 13) +#define SPI_CS_REN (1 << 12) +#define SPI_CS_ADCS (1 << 11) +#define SPI_CS_INTR (1 << 10) +#define SPI_CS_INTD (1 << 9) +#define SPI_CS_DMAEN (1 << 8) +#define SPI_CS_TA (1 << 7) +#define SPI_CS_CSPOL (1 << 6) +#define SPI_CS_CLEAR_RXFIFO (1 << 5) +#define SPI_CS_CLEAR_TXFIFO (1 << 4) +#define SPI_CS_CPOL (1 << 3) +#define SPI_CS_CPHA (1 << 2) +#define SPI_CS_MASK 0x3 +#define SPI_FIFO 0x4 +#define SPI_CLK 0x8 +#define SPI_CLK_MASK 0xffff +#define SPI_DLEN 0xc +#define SPI_DLEN_MASK 0xffff +#define SPI_LTOH 0x10 +#define SPI_LTOH_MASK 0xf +#define SPI_DC 0x14 +#define SPI_DC_RPANIC_SHIFT 24 +#define SPI_DC_RPANIC_MASK (0xff << SPI_DC_RPANIC_SHIFT) +#define SPI_DC_RDREQ_SHIFT 16 +#define SPI_DC_RDREQ_MASK (0xff << SPI_DC_RDREQ_SHIFT) +#define SPI_DC_TPANIC_SHIFT 8 +#define SPI_DC_TPANIC_MASK (0xff << SPI_DC_TPANIC_SHIFT) +#define SPI_DC_TDREQ_SHIFT 0 +#define SPI_DC_TDREQ_MASK 0xff + +#define SPI_BUSY 0x01 + +#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) + +static void bcm_spi_intr(void *); + +#undef DEBUG + +#ifdef 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; + 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_is_compatible(dev, "broadcom,bcm2835-spi")) + 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; + + 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 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 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); + + /* 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; + int cs, 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")); + + BCM_SPI_LOCK(sc); + + if (sc->sc_flags & SPI_BUSY) + mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz); + + if (sc->sc_flags & SPI_BUSY) { + BCM_SPI_UNLOCK(sc); + return (ETIMEDOUT); + } + + sc->sc_flags = 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); + + /* Get the proper chip select for this child. */ + spibus_get_cs(child, &cs); + if (cs < 0 || cs > 2) { + device_printf(dev, + "Invalid chip select %d requested by %s\n", cs, + device_get_nameunit(child)); + return (EINVAL); + } + + /* 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); + + /* + * Check for transfer timeout. The SPI controller doesn't + * return errors. + */ + if (err == EWOULDBLOCK) { + device_printf(sc->sc_dev, "SPI error\n"); + err = EIO; + } + + /* Clean the controller flags. */ + sc->sc_flags = 0; + + BCM_SPI_UNLOCK(sc); + + 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: sys/arm/broadcom/bcm2835/files.bcm2835 =================================================================== --- sys/arm/broadcom/bcm2835/files.bcm2835 (revision 253747) +++ sys/arm/broadcom/bcm2835/files.bcm2835 (working copy) @@ -7,6 +7,7 @@ arm/broadcom/bcm2835/bcm2835_machdep.c standard arm/broadcom/bcm2835/bcm2835_mbox.c standard arm/broadcom/bcm2835/bcm2835_sdhci.c optional sdhci +arm/broadcom/bcm2835/bcm2835_spi.c optional bcm2835_spi arm/broadcom/bcm2835/bcm2835_systimer.c standard arm/broadcom/bcm2835/bcm2835_wdog.c standard arm/broadcom/bcm2835/bus_space.c optional fdt Index: sys/boot/fdt/dts/bcm2835.dtsi =================================================================== --- sys/boot/fdt/dts/bcm2835.dtsi (revision 253747) +++ sys/boot/fdt/dts/bcm2835.dtsi (working copy) @@ -396,6 +396,14 @@ }; }; + spi0 { + compatible = "broadcom,bcm2835-spi", + "broadcom,bcm2708-spi"; + reg = <0x204000 0x20>; + interrupts = <62>; + interrupt-parent = <&intc>; + }; + dma: dma { compatible = "broadcom,bcm2835-dma", "broadcom,bcm2708-dma"; Index: sys/arm/conf/RPI-B =================================================================== --- sys/arm/conf/RPI-B (revision 253747) +++ sys/arm/conf/RPI-B (working copy) @@ -102,6 +102,10 @@ device mii device smsc +# SPI +device spibus +device bcm2835_spi + # Flattened Device Tree options FDT # Note: DTB is normally loaded and modified by RPi boot loader, then --Apple-Mail-247--559286389 Content-Disposition: attachment; filename=ofw_spibus.diff Content-Type: application/octet-stream; name="ofw_spibus.diff" Content-Transfer-Encoding: 7bit --- /dev/null 2013-08-22 00:33:00.000000000 -0300 +++ sys/dev/ofw/ofw_spibus.c 2013-08-19 14:50:08.051446131 -0300 @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 2009, Nathan Whitehorn <nwhitehorn@FreeBSD.org> + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Oleksandr Rybalko + * under sponsorship from the FreeBSD Foundation. + * + * 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 unmodified, 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 ``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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/libkern.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/spibus/spi.h> +#include <dev/spibus/spibusvar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include "spibus_if.h" + +struct ofw_spibus_devinfo { + struct spibus_ivar opd_dinfo; + struct ofw_bus_devinfo opd_obdinfo; +}; + +/* Methods */ +static device_probe_t ofw_spibus_probe; +static device_attach_t ofw_spibus_attach; +static device_t ofw_spibus_add_child(device_t dev, u_int order, + const char *name, int unit); +static const struct ofw_bus_devinfo *ofw_spibus_get_devinfo(device_t bus, + device_t dev); + +static int +ofw_spibus_probe(device_t dev) +{ + + if (ofw_bus_get_node(dev) == -1) + return (ENXIO); + device_set_desc(dev, "OFW SPI bus"); + + return (0); +} + +static int +ofw_spibus_attach(device_t dev) +{ + struct spibus_softc *sc = device_get_softc(dev); + struct ofw_spibus_devinfo *dinfo; + phandle_t child; + pcell_t paddr; + device_t childdev; + uint32_t addr; + + sc->dev = dev; + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + + /* + * Attach those children represented in the device tree. + */ + for (child = OF_child(ofw_bus_get_node(dev)); child != 0; + child = OF_peer(child)) { + /* + * Try to get the CS number first from the spi-chipselect + * property, then try the reg property. + */ + if (OF_getprop(child, "spi-chipselect", &paddr, sizeof(paddr)) == -1) + if (OF_getprop(child, "reg", &paddr, sizeof(paddr)) == -1) + continue; + + addr = fdt32_to_cpu(paddr); + /* + * Now set up the SPI and OFW bus layer devinfo and add it + * to the bus. + */ + dinfo = malloc(sizeof(struct ofw_spibus_devinfo), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (dinfo == NULL) + continue; + dinfo->opd_dinfo.cs = addr; + if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != + 0) { + free(dinfo, M_DEVBUF); + continue; + } + childdev = device_add_child(dev, NULL, -1); + device_set_ivars(childdev, dinfo); + } + + return (bus_generic_attach(dev)); +} + +static device_t +ofw_spibus_add_child(device_t dev, u_int order, const char *name, int unit) +{ + device_t child; + struct ofw_spibus_devinfo *devi; + + child = device_add_child_ordered(dev, order, name, unit); + if (child == NULL) + return (child); + devi = malloc(sizeof(struct ofw_spibus_devinfo), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (devi == NULL) { + device_delete_child(dev, child); + return (0); + } + + /* + * NULL all the OFW-related parts of the ivars for non-OFW + * children. + */ + devi->opd_obdinfo.obd_node = -1; + devi->opd_obdinfo.obd_name = NULL; + devi->opd_obdinfo.obd_compat = NULL; + devi->opd_obdinfo.obd_type = NULL; + devi->opd_obdinfo.obd_model = NULL; + + device_set_ivars(child, devi); + + return (child); +} + +static const struct ofw_bus_devinfo * +ofw_spibus_get_devinfo(device_t bus, device_t dev) +{ + struct ofw_spibus_devinfo *dinfo; + + dinfo = device_get_ivars(dev); + return (&dinfo->opd_obdinfo); +} + +static device_method_t ofw_spibus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_spibus_probe), + DEVMETHOD(device_attach, ofw_spibus_attach), + + /* Bus interface */ + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), + DEVMETHOD(bus_add_child, ofw_spibus_add_child), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, ofw_spibus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static devclass_t ofwspibus_devclass; + +DEFINE_CLASS_1(spibus, ofw_spibus_driver, ofw_spibus_methods, + sizeof(struct spibus_softc), spibus_driver); +DRIVER_MODULE(ofw_spibus, spi, ofw_spibus_driver, ofwspibus_devclass, 0, 0); +MODULE_VERSION(ofw_spibus, 1); +MODULE_DEPEND(ofw_spibus, spibus, 1, 1, 1); Index: sys/dev/spibus/spibus.c =================================================================== --- sys/dev/spibus/spibus.c (revision 253747) +++ sys/dev/spibus/spibus.c (working copy) @@ -23,7 +23,7 @@ spibus_probe(device_t dev) { device_set_desc(dev, "spibus bus"); - return (0); + return (BUS_PROBE_GENERIC); } static int @@ -185,7 +185,7 @@ DEVMETHOD_END }; -static driver_t spibus_driver = { +driver_t spibus_driver = { "spibus", spibus_methods, sizeof(struct spibus_softc) Index: sys/dev/spibus/spibusvar.h =================================================================== --- sys/dev/spibus/spibusvar.h (revision 253747) +++ sys/dev/spibus/spibusvar.h (working copy) @@ -26,3 +26,6 @@ } SPIBUS_ACCESSOR(cs, CS, uint32_t) + +extern driver_t spibus_driver; +extern devclass_t spibus_devclass; Index: sys/conf/files =================================================================== --- sys/conf/files (revision 253747) +++ sys/conf/files (working copy) @@ -1903,6 +1903,7 @@ dev/ofw/ofw_bus_subr.c optional fdt dev/ofw/ofw_fdt.c optional fdt dev/ofw/ofw_if.m optional fdt +dev/ofw/ofw_spibus.c optional fdt spibus dev/ofw/openfirm.c optional fdt dev/ofw/openfirmio.c optional fdt dev/patm/if_patm.c optional patm pci --Apple-Mail-247--559286389 Content-Disposition: attachment; filename=mx25l-fdt-intr.diff Content-Type: application/octet-stream; name="mx25l-fdt-intr.diff" Content-Transfer-Encoding: 7bit Index: sys/dev/flash/mx25l.c =================================================================== --- sys/dev/flash/mx25l.c (revision 253747) +++ sys/dev/flash/mx25l.c (working copy) @@ -43,6 +43,14 @@ #include <dev/spibus/spi.h> #include "spibus_if.h" +#include "opt_platform.h" + +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> +#endif + #include <dev/flash/mx25lreg.h> #define FL_NONE 0x00 @@ -76,6 +84,7 @@ struct disk *sc_disk; struct proc *sc_p; struct bio_queue_head sc_bio_queue; + struct intr_config_hook sc_intr_hook; unsigned int sc_flags; }; @@ -358,23 +367,27 @@ static int mx25l_probe(device_t dev) { + +#ifdef FDT + if (!ofw_bus_is_compatible(dev, "flash,mx25l")) + return (ENXIO); +#endif device_set_desc(dev, "M25Pxx Flash Family"); return (0); } -static int -mx25l_attach(device_t dev) +static void +mx25l_start(void *arg) { struct mx25l_softc *sc; struct mx25l_flash_ident *ident; - sc = device_get_softc(dev); - sc->sc_dev = dev; - M25PXX_LOCK_INIT(sc); + sc = (struct mx25l_softc *)arg; + config_intrhook_disestablish(&sc->sc_intr_hook); ident = mx25l_get_device_ident(sc); if (ident == NULL) - return (ENXIO); + return; mx25l_wait_for_device_ready(sc->sc_dev); @@ -403,7 +416,27 @@ kproc_create(&mx25l_task, sc, &sc->sc_p, 0, 0, "task: mx25l flash"); device_printf(sc->sc_dev, "%s, sector %d bytes, %d sectors\n", ident->name, ident->sectorsize, ident->sectorcount); +} +static int +mx25l_attach(device_t dev) +{ + struct mx25l_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + M25PXX_LOCK_INIT(sc); + + sc->sc_intr_hook.ich_func = mx25l_start; + sc->sc_intr_hook.ich_arg = sc; + + /* + * We have to wait until interrupts are enabled. In some cases SPI + * read and write only works if the interrupts are available. + */ + if (config_intrhook_establish(&sc->sc_intr_hook) != 0) + return (ENOMEM); + return (0); } --Apple-Mail-247--559286389 Content-Disposition: attachment; filename=rpi-mx25l-dts.diff Content-Type: application/octet-stream; name="rpi-mx25l-dts.diff" Content-Transfer-Encoding: 7bit Index: sys/boot/fdt/dts/rpi.dts =================================================================== --- sys/boot/fdt/dts/rpi.dts (revision 253747) +++ sys/boot/fdt/dts/rpi.dts (working copy) @@ -281,6 +281,14 @@ broadcom,function = "ALT3"; }; }; + + spi0 { + flash0 { + compatible = "flash,mx25l"; + spi-chipselect = <0>; + }; + }; + usb { hub { compatible = "usb,hub", "usb,device"; --Apple-Mail-247--559286389--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?126AAEEA-1F99-42E4-9620-9CB4F3610671>