Index: stable/11/sys/dev/gpio/gpiospi.c =================================================================== --- stable/11/sys/dev/gpio/gpiospi.c (revision 315328) +++ stable/11/sys/dev/gpio/gpiospi.c (revision 315329) @@ -1,402 +1,404 @@ /*- * Copyright (c) 2011, Aleksandr Rybalko * 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 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 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_gpio.h" #include #include #include #include #include #include #include #include #include "gpiobus_if.h" #include #include #include #include "spibus_if.h" #ifdef GPIO_SPI_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif /* GPIO_SPI_DEBUG */ struct gpio_spi_softc { device_t sc_dev; device_t sc_busdev; int sc_freq; uint8_t sc_sclk; uint8_t sc_miso; uint8_t sc_mosi; uint8_t sc_cs0; uint8_t sc_cs1; uint8_t sc_cs2; uint8_t sc_cs3; }; static void gpio_spi_chip_activate(struct gpio_spi_softc *, int); static void gpio_spi_chip_deactivate(struct gpio_spi_softc *, int); static int gpio_spi_probe(device_t dev) { device_set_desc(dev, "GPIO SPI bit-banging driver"); return (0); } static void gpio_delay(struct gpio_spi_softc *sc) { int d; d = sc->sc_freq / 1000000; if (d == 0) d = 1; DELAY(d); } static int gpio_spi_attach(device_t dev) { uint32_t value; struct gpio_spi_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_busdev = device_get_parent(dev); /* Required variables */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "sclk", &value)) return (ENXIO); sc->sc_sclk = value & 0xff; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "mosi", &value)) return (ENXIO); sc->sc_mosi = value & 0xff; /* Handle no miso; we just never read back from the device */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "miso", &value)) value = 0xff; sc->sc_miso = value & 0xff; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "cs0", &value)) return (ENXIO); sc->sc_cs0 = value & 0xff; /* Optional variables */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "cs1", &value)) value = 0xff; sc->sc_cs1 = value & 0xff; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "cs2", &value)) value = 0xff; sc->sc_cs2 = value & 0xff; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "cs3", &value)) value = 0xff; sc->sc_cs3 = value & 0xff; /* Default to 100KHz */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "freq", &value)) { value = 100000; } sc->sc_freq = value; if (bootverbose) { device_printf(dev, "frequency: %d Hz\n", sc->sc_freq); device_printf(dev, "Use GPIO pins: sclk=%d, mosi=%d, miso=%d, " "cs0=%d, cs1=%d, cs2=%d, cs3=%d\n", sc->sc_sclk, sc->sc_mosi, sc->sc_miso, sc->sc_cs0, sc->sc_cs1, sc->sc_cs2, sc->sc_cs3); } /* Set directions */ GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, GPIO_PIN_OUTPUT|GPIO_PIN_PULLDOWN); GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_mosi, GPIO_PIN_OUTPUT|GPIO_PIN_PULLDOWN); if (sc->sc_miso != 0xff) { GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_miso, GPIO_PIN_INPUT|GPIO_PIN_PULLDOWN); } GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs0, GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP); if (sc->sc_cs1 != 0xff) GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs1, GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP); if (sc->sc_cs2 != 0xff) GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs2, GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP); if (sc->sc_cs3 != 0xff) GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs3, GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP); gpio_spi_chip_deactivate(sc, -1); device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } static int gpio_spi_detach(device_t dev) { return (0); } static void gpio_spi_chip_activate(struct gpio_spi_softc *sc, int cs) { /* called with locked gpiobus */ switch (cs) { case 0: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs0, 0); break; case 1: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs1, 0); break; case 2: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs2, 0); break; case 3: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs3, 0); break; default: device_printf(sc->sc_dev, "don't have CS%d\n", cs); } gpio_delay(sc); } static void gpio_spi_chip_deactivate(struct gpio_spi_softc *sc, int cs) { /* called wth locked gpiobus */ /* * Put CSx to high */ switch (cs) { case -1: /* All CS */ GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs0, 1); if (sc->sc_cs1 == 0xff) break; GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs1, 1); if (sc->sc_cs2 == 0xff) break; GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs2, 1); if (sc->sc_cs3 == 0xff) break; GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs3, 1); break; case 0: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs0, 1); break; case 1: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs1, 1); break; case 2: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs2, 1); break; case 3: GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_cs3, 1); break; default: device_printf(sc->sc_dev, "don't have CS%d\n", cs); } } static uint8_t gpio_spi_txrx(struct gpio_spi_softc *sc, int cs, int mode, uint8_t data) { uint32_t mask, out = 0; unsigned int bit; /* called with locked gpiobus */ for (mask = 0x80; mask > 0; mask >>= 1) { if ((mode == SPIBUS_MODE_CPOL) || (mode == SPIBUS_MODE_CPHA)) { /* If mode 1 or 2 */ /* first step */ GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_mosi, (data & mask)?1:0); GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 0); gpio_delay(sc); /* second step */ if (sc->sc_miso != 0xff) { GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sc_miso, &bit); out |= bit?mask:0; } /* Data captured */ gpio_delay(sc); GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 1); + gpio_delay(sc); } else { /* If mode 0 or 3 */ /* first step */ GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_mosi, (data & mask)?1:0); GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 1); gpio_delay(sc); /* second step */ if (sc->sc_miso != 0xff) { GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sc_miso, &bit); out |= bit?mask:0; } /* Data captured */ gpio_delay(sc); GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 0); + gpio_delay(sc); } } return (out & 0xff); } static int gpio_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct gpio_spi_softc *sc; uint8_t *buf_in, *buf_out; struct spibus_ivar *devi = SPIBUS_IVAR(child); int i; 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")); gpio_spi_chip_activate(sc, devi->cs); /* Preset pins */ if ((devi->mode == SPIBUS_MODE_CPOL) || (devi->mode == SPIBUS_MODE_CPHA)) { GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 1); } else { GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 0); } /* * Transfer command */ buf_out = (uint8_t *)cmd->tx_cmd; buf_in = (uint8_t *)cmd->rx_cmd; for (i = 0; i < cmd->tx_cmd_sz; i++) buf_in[i] = gpio_spi_txrx(sc, devi->cs, devi->mode, buf_out[i]); /* * Receive/transmit data (depends on command) */ buf_out = (uint8_t *)cmd->tx_data; buf_in = (uint8_t *)cmd->rx_data; for (i = 0; i < cmd->tx_data_sz; i++) buf_in[i] = gpio_spi_txrx(sc, devi->cs, devi->mode, buf_out[i]); /* Return pins to mode default */ if ((devi->mode == SPIBUS_MODE_CPOL) || (devi->mode == SPIBUS_MODE_CPHA)) { GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 1); } else { GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sc_sclk, 0); } gpio_spi_chip_deactivate(sc, devi->cs); return (0); } static device_method_t gpio_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gpio_spi_probe), DEVMETHOD(device_attach, gpio_spi_attach), DEVMETHOD(device_detach, gpio_spi_detach), DEVMETHOD(spibus_transfer, gpio_spi_transfer), {0, 0} }; static driver_t gpio_spi_driver = { "gpiospi", gpio_spi_methods, sizeof(struct gpio_spi_softc), }; static devclass_t gpio_spi_devclass; DRIVER_MODULE(gpiospi, gpiobus, gpio_spi_driver, gpio_spi_devclass, 0, 0); DRIVER_MODULE(spibus, gpiospi, spibus_driver, spibus_devclass, 0, 0); MODULE_DEPEND(spi, gpiospi, 1, 1, 1); MODULE_DEPEND(gpiobus, gpiospi, 1, 1, 1); Index: stable/11/sys/dev/spibus/spibus.c =================================================================== --- stable/11/sys/dev/spibus/spibus.c (revision 315328) +++ stable/11/sys/dev/spibus/spibus.c (revision 315329) @@ -1,234 +1,233 @@ /*- * Copyright (c) 2006 M. Warner Losh * 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 "spibus_if.h" static int spibus_probe(device_t dev) { device_set_desc(dev, "spibus bus"); return (BUS_PROBE_GENERIC); } static int spibus_attach(device_t dev) { struct spibus_softc *sc = SPIBUS_SOFTC(dev); sc->dev = dev; bus_enumerate_hinted_children(dev); return (bus_generic_attach(dev)); } /* * Since this is not a self-enumerating bus, and since we always add * children in attach, we have to always delete children here. */ static int spibus_detach(device_t dev) { int err, ndevs, i; device_t *devlist; if ((err = bus_generic_detach(dev)) != 0) return (err); if ((err = device_get_children(dev, &devlist, &ndevs)) != 0) return (err); for (i = 0; i < ndevs; i++) device_delete_child(dev, devlist[i]); free(devlist, M_TEMP); return (0); } static int spibus_suspend(device_t dev) { return (bus_generic_suspend(dev)); } static int spibus_resume(device_t dev) { return (bus_generic_resume(dev)); } static int spibus_print_child(device_t dev, device_t child) { struct spibus_ivar *devi = SPIBUS_IVAR(child); int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(" at cs %d", devi->cs); retval += printf(" mode %d", devi->mode); retval += bus_print_child_footer(dev, child); return (retval); } static void spibus_probe_nomatch(device_t bus, device_t child) { struct spibus_ivar *devi = SPIBUS_IVAR(child); - device_printf(bus, ""); - printf(" at cs %d\n", devi->cs); - printf(" mode %d", devi->mode); + device_printf(bus, " at cs %d mode %d\n", devi->cs, + devi->mode); return; } static int spibus_child_location_str(device_t bus, device_t child, char *buf, size_t buflen) { struct spibus_ivar *devi = SPIBUS_IVAR(child); snprintf(buf, buflen, "cs=%d", devi->cs); return (0); } static int spibus_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static int spibus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct spibus_ivar *devi = SPIBUS_IVAR(child); switch (which) { default: return (EINVAL); case SPIBUS_IVAR_CS: *(uint32_t *)result = devi->cs; break; case SPIBUS_IVAR_MODE: *(uint32_t *)result = devi->mode; break; case SPIBUS_IVAR_CLOCK: *(uint32_t *)result = devi->clock; break; } return (0); } static device_t spibus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct spibus_ivar *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(sizeof(struct spibus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } device_set_ivars(child, devi); return (child); } static void spibus_hinted_child(device_t bus, const char *dname, int dunit) { device_t child; struct spibus_ivar *devi; child = BUS_ADD_CHILD(bus, 0, dname, dunit); devi = SPIBUS_IVAR(child); devi->mode = SPIBUS_MODE_NONE; resource_int_value(dname, dunit, "cs", &devi->cs); resource_int_value(dname, dunit, "mode", &devi->mode); } static int spibus_transfer_impl(device_t dev, device_t child, struct spi_command *cmd) { return (SPIBUS_TRANSFER(device_get_parent(dev), child, cmd)); } static device_method_t spibus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, spibus_probe), DEVMETHOD(device_attach, spibus_attach), DEVMETHOD(device_detach, spibus_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, spibus_suspend), DEVMETHOD(device_resume, spibus_resume), /* Bus interface */ DEVMETHOD(bus_add_child, spibus_add_child), DEVMETHOD(bus_print_child, spibus_print_child), DEVMETHOD(bus_probe_nomatch, spibus_probe_nomatch), DEVMETHOD(bus_read_ivar, spibus_read_ivar), DEVMETHOD(bus_child_pnpinfo_str, spibus_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, spibus_child_location_str), DEVMETHOD(bus_hinted_child, spibus_hinted_child), /* spibus interface */ DEVMETHOD(spibus_transfer, spibus_transfer_impl), DEVMETHOD_END }; driver_t spibus_driver = { "spibus", spibus_methods, sizeof(struct spibus_softc) }; devclass_t spibus_devclass; DRIVER_MODULE(spibus, spi, spibus_driver, spibus_devclass, 0, 0); MODULE_VERSION(spibus, 1);