Index: head/sys/arm/broadcom/bcm2835/bcm2835_spi.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_spi.c +++ head/sys/arm/broadcom/bcm2835/bcm2835_spi.c @@ -132,17 +132,6 @@ 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); } @@ -163,12 +152,6 @@ 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); } @@ -200,6 +183,13 @@ return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); } +static int +bcm_spi_cspol2_proc(SYSCTL_HANDLER_ARGS) +{ + + return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL2)); +} + static void bcm_spi_sysctl_init(struct bcm_spi_softc *sc) { @@ -214,20 +204,23 @@ 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), + CTLFLAG_RD | 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), + CTLFLAG_RD | 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), + CTLFLAG_RD | 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), + CTLFLAG_RD | 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), + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); + SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol2", + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), + bcm_spi_cspol2_proc, "IU", "SPI BUS chip select 2 polarity"); } static int @@ -303,9 +296,6 @@ */ 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 @@ -412,7 +402,7 @@ bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct bcm_spi_softc *sc; - uint32_t cs; + uint32_t cs, mode, clock; int err; sc = device_get_softc(dev); @@ -422,21 +412,34 @@ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("TX/RX data sizes should be equal")); - /* Get the proper chip select for this child. */ + /* Get the bus speed, mode, and chip select for this child. */ + spibus_get_cs(child, &cs); + if ((cs & (~SPIBUS_CS_HIGH)) > 2) { + device_printf(dev, + "Invalid chip select %u requested by %s\n", cs, + device_get_nameunit(child)); + return (EINVAL); + } - cs &= ~SPIBUS_CS_HIGH; + spibus_get_clock(child, &clock); + if (clock == 0) { + device_printf(dev, + "Invalid clock %uHz requested by %s\n", clock, + device_get_nameunit(child)); + return (EINVAL); + } - if (cs > 2) { + spibus_get_mode(child, &mode); + if (mode > 3) { device_printf(dev, - "Invalid chip select %d requested by %s\n", cs, + "Invalid mode %u requested by %s\n", mode, device_get_nameunit(child)); return (EINVAL); } - BCM_SPI_LOCK(sc); - /* If the controller is in use wait until it is available. */ + BCM_SPI_LOCK(sc); while (sc->sc_flags & BCM_SPI_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); @@ -454,13 +457,63 @@ sc->sc_written = 0; sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; +#ifdef BCM2835_SPI_USE_CS_HIGH /* TODO: for when behavior is correct */ /* + * Assign CS polarity first, while the CS indicates 'inactive'. + * This will need to set the correct polarity bit based on the 'cs', and + * the polarity bit will remain in this state, even after the transaction + * is complete. + */ + if((cs & ~SPIBUS_CS_HIGH) == 0) { + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CSPOL0, + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL0 : 0)); + } + else if((cs & ~SPIBUS_CS_HIGH) == 1) { + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CSPOL1, + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL1 : 0)); + } + else if((cs & ~SPIBUS_CS_HIGH) == 2) { + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CSPOL2, + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL2 : 0)); + } +#endif + + /* + * Set the mode in 'SPI_CS' (clock phase and polarity bits). + * This must happen before CS output pin is active. + * Otherwise, you might glitch and drop the first bit. + */ + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CPOL | SPI_CS_CPHA, + ((mode & SPIBUS_MODE_CPHA) ? SPI_CS_CPHA : 0) | + ((mode & SPIBUS_MODE_CPOL) ? SPI_CS_CPOL : 0)); + + /* + * Set the clock divider in 'SPI_CLK - see 'bcm_spi_clock_proc()'. + */ + + /* calculate 'clock' as a divider value from freq */ + clock = SPI_CORE_CLK / clock; + if (clock <= 1) + clock = 2; + else if (clock % 2) + clock--; + if (clock > 0xffff) + clock = 0; + + BCM_SPI_WRITE(sc, SPI_CLK, clock); + + /* * 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); + (cs & (~SPIBUS_CS_HIGH)) | /* cs is the lower 2 bits of the reg */ + 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); @@ -478,7 +531,7 @@ * return errors. */ if (err == EWOULDBLOCK) { - device_printf(sc->sc_dev, "SPI error\n"); + device_printf(sc->sc_dev, "SPI error (timeout)\n"); err = EIO; }