Index: sys/arm/broadcom/bcm2835/bcm2835_spi.c =================================================================== --- sys/arm/broadcom/bcm2835/bcm2835_spi.c +++ sys/arm/broadcom/bcm2835/bcm2835_spi.c @@ -422,7 +422,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, reg_cs, reg_clk, mode, clock; int err; sc = device_get_softc(dev); @@ -434,18 +434,26 @@ /* Get the proper chip select for this child. */ spibus_get_cs(child, &cs); - - cs &= ~SPIBUS_CS_HIGH; - - if (cs > 2) { + if ((cs & (~SPIBUS_CS_HIGH)) > 2) { device_printf(dev, "Invalid chip select %d requested by %s\n", cs, device_get_nameunit(child)); return (EINVAL); } + /* obtain clock, mode from spibus */ + spibus_get_clock(child, &clock); + spibus_get_mode(child, &mode); + BCM_SPI_LOCK(sc); + /* preserve SPI_CS and SPI_CLK so I can restore them */ + /* these were originally controlled by sysctl vars. */ + /* since spibus and spigen now control these via ivars */ + /* there may no longer be a need for preserving them */ + reg_cs = BCM_SPI_READ(sc, SPI_CS); + reg_clk = BCM_SPI_READ(sc, SPI_CLK); + /* 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); @@ -469,8 +477,38 @@ * 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); + SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD | + SPI_CS_CSPOL, + (cs & (~SPIBUS_CS_HIGH)) | + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL : 0) | + SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); + + /* set the mode in 'SPI_CS' */ + 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() */ + /* if the spibus device has 0 for clock, leave value alone */ + /* a zero maximum clock implies 'use default' - see ofw_spibus.c */ + /* since spibus clock is a maximum, if reg value is slower, keep it */ + if(clock != 0) { + /* 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; + + /* test if 'reg_clk' value is less than 'clock' */ + /* if it is, then the frequency is higher, and clock */ + /* should be assigned as the new divider. */ + if((reg_clk & 0xffff) < clock) + BCM_SPI_WRITE(sc, SPI_CLK, clock); + } /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); @@ -478,6 +516,11 @@ /* 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); + /* restore the original mode in 'SPI_CS' and clock in 'SPI_CLK' */ + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CPOL | SPI_CS_CPHA, reg_cs); + BCM_SPI_WRITE(sc, SPI_CLK, reg_clk); + /* Release the controller and wakeup the next thread waiting for it. */ sc->sc_flags = 0; wakeup_one(dev);