summaryrefslogtreecommitdiffstats
path: root/sys/arm/mv
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/mv')
-rw-r--r--sys/arm/mv/mv_spi.c104
1 files changed, 75 insertions, 29 deletions
diff --git a/sys/arm/mv/mv_spi.c b/sys/arm/mv/mv_spi.c
index 00b683b..556a4f4 100644
--- a/sys/arm/mv/mv_spi.c
+++ b/sys/arm/mv/mv_spi.c
@@ -59,6 +59,7 @@ struct mv_spi_softc {
uint32_t sc_flags;
uint32_t sc_written;
void *sc_intrhand;
+ bool sc_bytelen;
};
#define MV_SPI_BUSY 0x1
@@ -71,8 +72,10 @@ struct mv_spi_softc {
#define MV_SPI_CONTROL 0
#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_BYTELEN (1 << 5)
#define MV_SPI_DATAOUT 0x8
#define MV_SPI_DATAIN 0xc
#define MV_SPI_INTR_STAT 0x10
@@ -175,47 +178,60 @@ mv_spi_detach(device_t dev)
}
static __inline void
-mv_spi_rx_byte(struct mv_spi_softc *sc)
+mv_spi_rx_bytes(struct mv_spi_softc *sc)
{
struct spi_command *cmd;
- uint32_t read;
- uint8_t *data;
+ uint32_t data, read;
+ uint8_t bytes, *p;
+ data = 0;
cmd = sc->sc_cmd;
- if (sc->sc_read < sc->sc_len && sc->sc_written > 0) {
- data = (uint8_t *)cmd->rx_cmd;
+ bytes = (sc->sc_len >= 2) ? 2 : 1;
+ if (sc->sc_read < sc->sc_len)
+ data = MV_SPI_READ(sc, MV_SPI_DATAIN);
+ while (sc->sc_read < sc->sc_len && bytes > 0) {
+ p = (uint8_t *)cmd->rx_cmd;
read = sc->sc_read++;
if (read >= cmd->rx_cmd_sz) {
- data = (uint8_t *)cmd->rx_data;
+ p = (uint8_t *)cmd->rx_data;
read -= cmd->rx_cmd_sz;
}
- data[read] = MV_SPI_READ(sc, MV_SPI_DATAIN) & 0xff;
+ p[read] = (data >> ((2 - bytes) * 8)) & 0xff;
+ bytes--;
}
}
static __inline void
-mv_spi_tx_byte(struct mv_spi_softc *sc)
+mv_spi_tx_bytes(struct mv_spi_softc *sc)
{
struct spi_command *cmd;
- uint32_t written;
- uint8_t *data;
+ uint32_t data, written;
+ uint8_t bytes, count, *p;
+ data = 0;
+ count = 0;
cmd = sc->sc_cmd;
- if (sc->sc_written < sc->sc_len) {
- data = (uint8_t *)cmd->tx_cmd;
+ bytes = (sc->sc_len >= 2) ? 2 : 1;
+ while (sc->sc_written < sc->sc_len && bytes > 0) {
+ p = (uint8_t *)cmd->tx_cmd;
written = sc->sc_written++;
if (written >= cmd->tx_cmd_sz) {
- data = (uint8_t *)cmd->tx_data;
+ p = (uint8_t *)cmd->tx_data;
written -= cmd->tx_cmd_sz;
}
- MV_SPI_WRITE(sc, MV_SPI_DATAOUT, data[written]);
+ data |= p[written] << ((2 - bytes) * 8);
+ bytes--;
+ count++;
}
+ if (count > 0)
+ MV_SPI_WRITE(sc, MV_SPI_DATAOUT, data);
}
static void
mv_spi_intr(void *arg)
{
struct mv_spi_softc *sc;
+ uint32_t reg;
sc = (struct mv_spi_softc *)arg;
MV_SPI_LOCK(sc);
@@ -227,10 +243,17 @@ mv_spi_intr(void *arg)
}
/* RX */
- mv_spi_rx_byte(sc);
+ mv_spi_rx_bytes(sc);
+
+ if (sc->sc_bytelen && (sc->sc_len - sc->sc_written) < 2) {
+ reg = MV_SPI_READ(sc, MV_SPI_CONF);
+ reg &= ~MV_SPI_CONF_BYTELEN;
+ MV_SPI_WRITE(sc, MV_SPI_CONF, reg);
+ sc->sc_bytelen = false;
+ }
/* TX */
- mv_spi_tx_byte(sc);
+ mv_spi_tx_bytes(sc);
/* Check for end of transfer. */
if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len)
@@ -244,7 +267,7 @@ mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
{
struct mv_spi_softc *sc;
uint32_t cs, reg;
- int err;
+ int resid, timeout;
KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
("TX/RX command sizes should be equal"));
@@ -275,13 +298,41 @@ mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
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_INTR_MASK, MV_SPI_INTR_SMEMREADY);
-
- /* Write the first byte to start the transmission. */
- mv_spi_tx_byte(sc);
+ /* Is the buffer big enough to enable the two byte FIFO ? */
+ if (sc->sc_len >= 2)
+ sc->sc_bytelen = true;
+ reg = MV_SPI_READ(sc, MV_SPI_CONF);
+ if (sc->sc_bytelen)
+ reg |= MV_SPI_CONF_BYTELEN;
+ else
+ reg &= ~MV_SPI_CONF_BYTELEN;
+ MV_SPI_WRITE(sc, MV_SPI_CONF, reg);
+
+ while ((resid = sc->sc_len - sc->sc_written) > 0) {
+
+ if (sc->sc_bytelen && resid < 2) {
+ reg = MV_SPI_READ(sc, MV_SPI_CONF);
+ reg &= ~MV_SPI_CONF_BYTELEN;
+ MV_SPI_WRITE(sc, MV_SPI_CONF, reg);
+ sc->sc_bytelen = false;
+ }
- /* Wait for the transaction to complete. */
- err = mtx_sleep(dev, &sc->sc_mtx, 0, "mv_spi", hz * 2);
+ /*
+ * Write to start the transmission and read the byte
+ * back when ready.
+ */
+ mv_spi_tx_bytes(sc);
+ timeout = 1000;
+ while (--timeout > 0) {
+ reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
+ if (reg & MV_SPI_CTRL_SMEMREADY)
+ break;
+ DELAY(100);
+ }
+ if (timeout == 0)
+ break;
+ mv_spi_rx_bytes(sc);
+ }
/* Stop the controller. */
MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
@@ -298,12 +349,7 @@ mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
* Check for transfer timeout. The SPI controller doesn't
* return errors.
*/
- if (err == EWOULDBLOCK) {
- device_printf(sc->sc_dev, "SPI error\n");
- err = EIO;
- }
-
- return (err);
+ return ((timeout == 0) ? EIO : 0);
}
static phandle_t
OpenPOWER on IntegriCloud