Page MenuHomeFreeBSD

D15031.id41388.diff
No OneTemporary

D15031.id41388.diff

Index: sys/arm/broadcom/bcm2835/bcm2835_spi.c
===================================================================
--- sys/arm/broadcom/bcm2835/bcm2835_spi.c
+++ sys/arm/broadcom/bcm2835/bcm2835_spi.c
@@ -200,6 +200,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)
{
@@ -228,6 +235,9 @@
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");
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol2",
+ CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
+ bcm_spi_cspol2_proc, "IU", "SPI BUS chip select 2 polarity");
}
static int
@@ -422,7 +432,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 +444,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);
@@ -465,12 +483,74 @@
sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
/*
+ * Assign CS polarity first, while the CS indicates 'inactive'.
+ * This will need to set the corect 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));
+ }
+
+ /*
+ * 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()'.
+ * If the spibus device has 0 for clock, leave value alone.
+ * A zero maximum clock implies 'use default' - see 'ofw_spibus.c'.
+ * Since spibus_get_clock value is a maximum, if reg value is slower,
+ * keep it. Otherwise, assign the lower frequency. The previous
+ * register value for the frequency will be restored after the
+ * transaction is over, so that sysctl vars and overlays can still
+ * be used to set a maximum frequency when the device is created.
+ */
+ 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);
+ }
+
+ /*
* 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,6 +558,19 @@
/* 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' while
+ * the spi device is still locked. This way a single transaction can
+ * can specify a clock speed and mode without affecting the defaults
+ * for the SPI device itself. This is especially important for the
+ * maximum clock speed, since _not_ restoring it would mess up the
+ * algorithm that determines whether or not the transaction needs to
+ * apply a slower clock speed.
+ */
+ 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
Sat, Jan 17, 7:19 PM (5 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27697996
Default Alt Text
D15031.id41388.diff (5 KB)

Event Timeline