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)
This commit is contained in:
parent
77613ca0cb
commit
aaf1f854e1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=340458
@ -44,6 +44,8 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <dev/spibus/spi.h>
|
#include <dev/spibus/spi.h>
|
||||||
#include <dev/spibus/spibusvar.h>
|
#include <dev/spibus/spibusvar.h>
|
||||||
|
|
||||||
|
#include <arm/mv/mvvar.h>
|
||||||
|
|
||||||
#include "spibus_if.h"
|
#include "spibus_if.h"
|
||||||
|
|
||||||
struct mv_spi_softc {
|
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_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
|
||||||
|
|
||||||
#define MV_SPI_CONTROL 0
|
#define MV_SPI_CONTROL 0
|
||||||
|
#define MV_SPI_CTRL_CS_MASK 7
|
||||||
#define MV_SPI_CTRL_CS_SHIFT 2
|
#define MV_SPI_CTRL_CS_SHIFT 2
|
||||||
#define MV_SPI_CTRL_SMEMREADY (1 << 1)
|
#define MV_SPI_CTRL_SMEMREADY (1 << 1)
|
||||||
#define MV_SPI_CTRL_CS_ACTIVE (1 << 0)
|
#define MV_SPI_CTRL_CS_ACTIVE (1 << 0)
|
||||||
#define MV_SPI_CONF 0x4
|
#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_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_DATAOUT 0x8
|
||||||
#define MV_SPI_DATAIN 0xc
|
#define MV_SPI_DATAIN 0xc
|
||||||
#define MV_SPI_INTR_STAT 0x10
|
#define MV_SPI_INTR_STAT 0x10
|
||||||
@ -243,11 +257,28 @@ mv_spi_intr(void *arg)
|
|||||||
MV_SPI_UNLOCK(sc);
|
MV_SPI_UNLOCK(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static int
|
||||||
mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
|
mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
|
||||||
{
|
{
|
||||||
struct mv_spi_softc *sc;
|
struct mv_spi_softc *sc;
|
||||||
uint32_t cs, reg;
|
uint32_t clock, cs, mode, reg, spr, sppr;
|
||||||
int resid, timeout;
|
int resid, timeout;
|
||||||
|
|
||||||
KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
|
KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
|
||||||
@ -255,9 +286,23 @@ mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
|
|||||||
KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
|
KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
|
||||||
("TX/RX data sizes should be equal"));
|
("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);
|
spibus_get_cs(child, &cs);
|
||||||
cs &= ~SPIBUS_CS_HIGH;
|
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);
|
sc = device_get_softc(dev);
|
||||||
MV_SPI_LOCK(sc);
|
MV_SPI_LOCK(sc);
|
||||||
@ -275,7 +320,20 @@ mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
|
|||||||
sc->sc_written = 0;
|
sc->sc_written = 0;
|
||||||
sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
|
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);
|
reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
|
||||||
MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
|
MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user