Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142173287
D15031.id41388.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D15031.id41388.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D15031: bcm2835_spi.c mod to support mode and clock in ivars from spibus
Attached
Detach File
Event Timeline
Log In to Comment