Page MenuHomeFreeBSD

D15031.id41339.diff
No OneTemporary

D15031.id41339.diff

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);

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 26, 11:41 PM (1 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37335963
Default Alt Text
D15031.id41339.diff (3 KB)

Event Timeline