Author: loos
Date: Thu Nov 15 17:05:02 2018
New Revision: 340458
URL: https://svnweb.freebsd.org/changeset/base/340458

Log:
  Set the SPI clock speed and polarity on each transfer to catch up with
  recent changes in spibus and allow the use of different SPI modes on
  the same bus.
  
  Reported by:  ian
  Sponsored by: Rubicon Communications, LLC (Netgate)

Modified:
  head/sys/arm/mv/mv_spi.c

Modified: head/sys/arm/mv/mv_spi.c
==============================================================================
--- head/sys/arm/mv/mv_spi.c    Thu Nov 15 16:42:59 2018        (r340457)
+++ head/sys/arm/mv/mv_spi.c    Thu Nov 15 17:05:02 2018        (r340458)
@@ -44,6 +44,8 @@ __FBSDID("$FreeBSD$");
 #include <dev/spibus/spi.h>
 #include <dev/spibus/spibusvar.h>
 
+#include <arm/mv/mvvar.h>
+
 #include "spibus_if.h"
 
 struct mv_spi_softc {
@@ -70,11 +72,23 @@ struct mv_spi_softc {
 #define        MV_SPI_UNLOCK(_sc)      mtx_unlock(&(_sc)->sc_mtx)
 
 #define        MV_SPI_CONTROL          0
+#define        MV_SPI_CTRL_CS_MASK             7
 #define        MV_SPI_CTRL_CS_SHIFT            2
 #define        MV_SPI_CTRL_SMEMREADY           (1 << 1)
 #define        MV_SPI_CTRL_CS_ACTIVE           (1 << 0)
 #define        MV_SPI_CONF             0x4
+#define        MV_SPI_CONF_MODE_SHIFT          12
+#define        MV_SPI_CONF_MODE_MASK           (3 << MV_SPI_CONF_MODE_SHIFT)
 #define        MV_SPI_CONF_BYTELEN             (1 << 5)
+#define        MV_SPI_CONF_CLOCK_SPR_MASK      0xf
+#define        MV_SPI_CONF_CLOCK_SPPR_MASK     1
+#define        MV_SPI_CONF_CLOCK_SPPR_SHIFT    4
+#define        MV_SPI_CONF_CLOCK_SPPRHI_MASK   3
+#define        MV_SPI_CONF_CLOCK_SPPRHI_SHIFT  6
+#define        MV_SPI_CONF_CLOCK_MASK                                          
\
+    ((MV_SPI_CONF_CLOCK_SPPRHI_MASK << MV_SPI_CONF_CLOCK_SPPRHI_SHIFT) | \
+    (MV_SPI_CONF_CLOCK_SPPR_MASK << MV_SPI_CONF_CLOCK_SPPR_SHIFT) |    \
+    MV_SPI_CONF_CLOCK_SPR_MASK)
 #define        MV_SPI_DATAOUT          0x8
 #define        MV_SPI_DATAIN           0xc
 #define        MV_SPI_INTR_STAT        0x10
@@ -244,10 +258,27 @@ mv_spi_intr(void *arg)
 }
 
 static int
+mv_spi_psc_calc(uint32_t clock, uint32_t *spr, uint32_t *sppr)
+{
+       uint32_t divider, tclk;
+
+       tclk = get_tclk_armada38x();
+       for (*spr = 2; *spr <= 15; (*spr)++) {
+               for (*sppr = 0; *sppr <= 7; (*sppr)++) {
+                       divider = *spr * (1 << *sppr);
+                       if (tclk / divider <= clock)
+                               return (0);
+               }
+       }
+
+       return (EINVAL);
+}
+
+static int
 mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 {
        struct mv_spi_softc *sc;
-       uint32_t cs, reg;
+       uint32_t clock, cs, mode, reg, spr, sppr;
        int resid, timeout;
 
        KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
@@ -255,9 +286,23 @@ mv_spi_transfer(device_t dev, device_t child, struct s
        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 proper chip select, mode and clock for this transfer. */
        spibus_get_cs(child, &cs);
        cs &= ~SPIBUS_CS_HIGH;
+       spibus_get_mode(child, &mode);
+       if (mode > 3) {
+               device_printf(dev,
+                   "Invalid mode %u requested by %s\n", mode,
+                   device_get_nameunit(child));
+               return (EINVAL);
+       }
+       spibus_get_clock(child, &clock);
+       if (clock == 0 || mv_spi_psc_calc(clock, &spr, &sppr) != 0) {
+               device_printf(dev,
+                   "Invalid clock %uHz requested by %s\n", clock,
+                   device_get_nameunit(child));
+               return (EINVAL);
+       }
 
        sc = device_get_softc(dev);
        MV_SPI_LOCK(sc);
@@ -275,7 +320,20 @@ mv_spi_transfer(device_t dev, device_t child, struct s
        sc->sc_written = 0;
        sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
 
-       MV_SPI_WRITE(sc, MV_SPI_CONTROL, cs << MV_SPI_CTRL_CS_SHIFT);
+       /* Set SPI Mode and Clock. */
+       reg = MV_SPI_READ(sc, MV_SPI_CONF);
+       reg &= ~(MV_SPI_CONF_MODE_MASK | MV_SPI_CONF_CLOCK_MASK);
+       reg |= mode << MV_SPI_CONF_MODE_SHIFT;
+       reg |= spr & MV_SPI_CONF_CLOCK_SPR_MASK;
+       reg |= (sppr & MV_SPI_CONF_CLOCK_SPPR_MASK) <<
+           MV_SPI_CONF_CLOCK_SPPR_SHIFT;
+       reg |= (sppr & MV_SPI_CONF_CLOCK_SPPRHI_MASK) <<
+           MV_SPI_CONF_CLOCK_SPPRHI_SHIFT;
+       MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
+
+       /* Set CS number and assert CS. */
+       reg = (cs & MV_SPI_CTRL_CS_MASK) << MV_SPI_CTRL_CS_SHIFT;
+       MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
        reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
        MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to