Module Name: src
Committed By: jmcneill
Date: Mon Feb 19 19:00:42 UTC 2018
Modified Files:
src/sys/dev/ic: pl181.c pl181var.h
Log Message:
Rewrite data transfer path to take advantage of the PL181's 64-byte FIFO.
Before: 134217728 bytes transferred in 43.683 secs (3072539 bytes/sec)
After: 134217728 bytes transferred in 23.789 secs (5642007 bytes/sec)
To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/ic/pl181.c
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/ic/pl181var.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ic/pl181.c
diff -u src/sys/dev/ic/pl181.c:1.4 src/sys/dev/ic/pl181.c:1.5
--- src/sys/dev/ic/pl181.c:1.4 Sun Jun 4 15:08:30 2017
+++ src/sys/dev/ic/pl181.c Mon Feb 19 19:00:42 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: pl181.c,v 1.4 2017/06/04 15:08:30 jmcneill Exp $ */
+/* $NetBSD: pl181.c,v 1.5 2018/02/19 19:00:42 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pl181.c,v 1.4 2017/06/04 15:08:30 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pl181.c,v 1.5 2018/02/19 19:00:42 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -49,6 +49,22 @@ __KERNEL_RCSID(0, "$NetBSD: pl181.c,v 1.
*/
#define PLMMC_MAXXFER rounddown(65535, SDMMC_SECTOR_SIZE)
+/*
+ * PL181 FIFO is 16 words deep (64 bytes)
+ */
+#define PL181_FIFO_DEPTH 64
+
+/*
+ * Data transfer IRQ status bits
+ */
+#define PLMMC_INT_DATA_MASK \
+ (MMCI_INT_DATA_TIMEOUT|MMCI_INT_DATA_CRC_FAIL| \
+ MMCI_INT_TX_FIFO_EMPTY|MMCI_INT_TX_FIFO_HALF_EMPTY| \
+ MMCI_INT_RX_FIFO_FULL|MMCI_INT_RX_FIFO_HALF_FULL| \
+ MMCI_INT_DATA_END|MMCI_INT_DATA_BLOCK_END)
+#define PLMMC_INT_CMD_MASK \
+ (MMCI_INT_CMD_TIMEOUT|MMCI_INT_CMD_RESP_END)
+
static int plmmc_host_reset(sdmmc_chipset_handle_t);
static uint32_t plmmc_host_ocr(sdmmc_chipset_handle_t);
static int plmmc_host_maxblklen(sdmmc_chipset_handle_t);
@@ -63,9 +79,7 @@ static void plmmc_exec_command(sdmmc_chi
static void plmmc_card_enable_intr(sdmmc_chipset_handle_t, int);
static void plmmc_card_intr_ack(sdmmc_chipset_handle_t);
-static int plmmc_wait_status(struct plmmc_softc *, uint32_t, int);
-static int plmmc_pio_wait(struct plmmc_softc *,
- struct sdmmc_command *);
+static int plmmc_wait_cmd(struct plmmc_softc *);
static int plmmc_pio_transfer(struct plmmc_softc *,
struct sdmmc_command *, int);
@@ -86,15 +100,19 @@ static struct sdmmc_chip_functions plmmc
#define MMCI_WRITE(sc, reg, val) \
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+#define MMCI_WRITE_MULTI(sc, reg, datap, cnt) \
+ bus_space_write_multi_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (datap), (cnt))
#define MMCI_READ(sc, reg) \
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define MMCI_READ_MULTI(sc, reg, datap, cnt) \
+ bus_space_read_multi_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (datap), (cnt))
void
plmmc_init(struct plmmc_softc *sc)
{
struct sdmmcbus_attach_args saa;
- mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO);
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_BIO);
cv_init(&sc->sc_intr_cv, "plmmcirq");
#ifdef PLMMC_DEBUG
@@ -130,80 +148,124 @@ plmmc_init(struct plmmc_softc *sc)
sc->sc_sdmmc_dev = config_found(sc->sc_dev, &saa, NULL);
}
-int
-plmmc_intr(void *priv)
+static int
+plmmc_intr_xfer(struct plmmc_softc *sc, struct sdmmc_command *cmd)
{
- struct plmmc_softc *sc = priv;
- uint32_t status;
+ uint32_t len;
- mutex_enter(&sc->sc_intr_lock);
- status = MMCI_READ(sc, MMCI_STATUS_REG);
-#ifdef PLMMC_DEBUG
- printf("%s: MMCI_STATUS_REG = %#x\n", __func__, status);
-#endif
- if (!status) {
- mutex_exit(&sc->sc_intr_lock);
- return 0;
+ if (cmd == NULL) {
+ device_printf(sc->sc_dev, "TX/RX interrupt with no active transfer\n");
+ return EINVAL;
}
- sc->sc_intr_status |= status;
- cv_broadcast(&sc->sc_intr_cv);
+ if (cmd->c_buf == NULL) {
+ return EINVAL;
+ }
+
+ const uint32_t fifo_cnt =
+ __SHIFTOUT(MMCI_READ(sc, MMCI_FIFO_CNT_REG), MMCI_FIFO_CNT) * 4;
+ if (fifo_cnt > sc->sc_fifo_resid) {
+ device_printf(sc->sc_dev, "FIFO counter is out of sync with active transfer\n");
+ return EIO;
+ }
- mutex_exit(&sc->sc_intr_lock);
+ if (cmd->c_flags & SCF_CMD_READ)
+ len = sc->sc_fifo_resid - fifo_cnt;
+ else
+ len = min(sc->sc_fifo_resid, PL181_FIFO_DEPTH);
- return 1;
+ if (len == 0)
+ return 0;
+
+ if (cmd->c_flags & SCF_CMD_READ)
+ MMCI_READ_MULTI(sc, MMCI_FIFO_REG, (uint32_t *)cmd->c_buf, len / 4);
+ else
+ MMCI_WRITE_MULTI(sc, MMCI_FIFO_REG, (uint32_t *)cmd->c_buf, len / 4);
+
+ sc->sc_fifo_resid -= len;
+ cmd->c_resid -= len;
+ cmd->c_buf += len;
+
+ return 0;
}
-static int
-plmmc_wait_status(struct plmmc_softc *sc, uint32_t mask, int timeout)
+int
+plmmc_intr(void *priv)
{
- int retry, error;
+ struct plmmc_softc *sc = priv;
+ uint32_t status, mask;
+ int retry = 100000;
- KASSERT(mutex_owned(&sc->sc_intr_lock));
+ mutex_enter(&sc->sc_lock);
- if (sc->sc_intr_status & mask)
- return 0;
+ while (--retry > 0) {
+ status = MMCI_READ(sc, MMCI_STATUS_REG);
+#ifdef PLMMC_DEBUG
+ printf("%s: MMCI_STATUS_REG = %#x\n", __func__, status);
+#endif
+ if ((status & sc->sc_status_mask) == 0)
+ break;
+ MMCI_WRITE(sc, MMCI_CLEAR_REG, status);
+ sc->sc_intr_status |= status;
- retry = timeout / hz;
- if (sc->sc_ih == NULL)
- retry *= 1000;
-
- while (retry > 0) {
- if (sc->sc_ih == NULL) {
- sc->sc_intr_status |= MMCI_READ(sc, MMCI_STATUS_REG);
- if (sc->sc_intr_status & mask)
- return 0;
- delay(10000);
- } else {
- error = cv_timedwait(&sc->sc_intr_cv,
- &sc->sc_intr_lock, hz);
- if (error && error != EWOULDBLOCK) {
- device_printf(sc->sc_dev,
- "cv_timedwait returned %d\n", error);
- return error;
+ if (status & MMCI_INT_CMD_TIMEOUT)
+ break;
+
+ if (status & (MMCI_INT_DATA_TIMEOUT|MMCI_INT_DATA_CRC_FAIL)) {
+ device_printf(sc->sc_dev,
+ "data xfer error, status %08x\n", status);
+ break;
+ }
+
+ if (status & (MMCI_INT_TX_FIFO_EMPTY|MMCI_INT_TX_FIFO_HALF_EMPTY|
+ MMCI_INT_RX_FIFO_FULL|MMCI_INT_RX_FIFO_HALF_FULL|
+ MMCI_INT_DATA_END|MMCI_INT_DATA_BLOCK_END)) {
+
+ /* Data transfer in progress */
+ if (plmmc_intr_xfer(sc, sc->sc_cmd) == 0 &&
+ sc->sc_fifo_resid == 0) {
+ /* Disable data IRQs */
+ mask = MMCI_READ(sc, MMCI_MASK0_REG);
+ mask &= ~PLMMC_INT_DATA_MASK;
+ MMCI_WRITE(sc, MMCI_MASK0_REG, mask);
+ /* Ignore data status bits after transfer */
+ sc->sc_status_mask &= ~PLMMC_INT_DATA_MASK;
}
- if (sc->sc_intr_status & mask)
- return 0;
}
- --retry;
+
+ if (status & MMCI_INT_CMD_RESP_END)
+ cv_broadcast(&sc->sc_intr_cv);
+ }
+ if (retry == 0) {
+ device_printf(sc->sc_dev, "intr handler stuck, fifo resid %d, status %08x\n",
+ sc->sc_fifo_resid, MMCI_READ(sc, MMCI_STATUS_REG));
}
- device_printf(sc->sc_dev, "%s timeout, MMCI_STATUS_REG = %#x\n",
- __func__, MMCI_READ(sc, MMCI_STATUS_REG));
+ cv_broadcast(&sc->sc_intr_cv);
+ mutex_exit(&sc->sc_lock);
- return ETIMEDOUT;
+ return 1;
}
static int
-plmmc_pio_wait(struct plmmc_softc *sc, struct sdmmc_command *cmd)
+plmmc_wait_cmd(struct plmmc_softc *sc)
{
- uint32_t bit = (cmd->c_flags & SCF_CMD_READ) ?
- MMCI_INT_RX_DATA_AVAIL : MMCI_INT_TX_FIFO_EMPTY;
+ int error = 0;
+
+ KASSERT(mutex_owned(&sc->sc_lock));
- MMCI_WRITE(sc, MMCI_CLEAR_REG, bit);
- const int error = plmmc_wait_status(sc,
- bit | MMCI_INT_DATA_END | MMCI_INT_DATA_BLOCK_END, hz*2);
- sc->sc_intr_status &= ~bit;
+ while (error == 0) {
+ if (sc->sc_intr_status & MMCI_INT_CMD_TIMEOUT) {
+ error = ETIMEDOUT;
+ break;
+ } else if (sc->sc_intr_status & MMCI_INT_CMD_RESP_END) {
+ break;
+ }
+
+ error = cv_timedwait(&sc->sc_intr_cv, &sc->sc_lock, hz * 2);
+ if (error != 0)
+ break;
+ }
return error;
}
@@ -212,22 +274,21 @@ static int
plmmc_pio_transfer(struct plmmc_softc *sc, struct sdmmc_command *cmd,
int xferlen)
{
- uint32_t *datap = (uint32_t *)cmd->c_buf;
- int i;
+ int error = 0;
- for (i = 0; i < xferlen / 4; i++) {
- if (plmmc_pio_wait(sc, cmd))
- return ETIMEDOUT;
- if (cmd->c_flags & SCF_CMD_READ) {
- datap[i] = MMCI_READ(sc, MMCI_FIFO_REG);
- } else {
- MMCI_WRITE(sc, MMCI_FIFO_REG, datap[i]);
- }
- cmd->c_resid -= 4;
- cmd->c_buf += 4;
+ while (sc->sc_fifo_resid > 0 && error == 0) {
+ error = cv_timedwait(&sc->sc_intr_cv,
+ &sc->sc_lock, hz * 5);
+ if (error != 0)
+ break;
+
+ if (sc->sc_intr_status & MMCI_INT_DATA_TIMEOUT)
+ error = ETIMEDOUT;
+ else if (sc->sc_intr_status & MMCI_INT_DATA_CRC_FAIL)
+ error = EIO;
}
- return 0;
+ return error;
}
static int
@@ -321,10 +382,15 @@ plmmc_do_command(sdmmc_chipset_handle_t
struct plmmc_softc *sc = sch;
uint32_t cmdval = MMCI_COMMAND_ENABLE;
- KASSERT(mutex_owned(&sc->sc_intr_lock));
+ KASSERT(mutex_owned(&sc->sc_lock));
const int xferlen = min(cmd->c_resid, PLMMC_MAXXFER);
+ sc->sc_cmd = cmd;
+ sc->sc_fifo_resid = xferlen;
+ sc->sc_status_mask = ~0U;
+ sc->sc_intr_status = 0;
+
#ifdef PLMMC_DEBUG
device_printf(sc->sc_dev,
"opcode %d flags %#x datalen %d resid %d xferlen %d\n",
@@ -334,13 +400,7 @@ plmmc_do_command(sdmmc_chipset_handle_t
MMCI_WRITE(sc, MMCI_COMMAND_REG, 0);
MMCI_WRITE(sc, MMCI_MASK0_REG, 0);
MMCI_WRITE(sc, MMCI_CLEAR_REG, 0xffffffff);
- MMCI_WRITE(sc, MMCI_MASK0_REG,
- MMCI_INT_CMD_TIMEOUT | MMCI_INT_DATA_TIMEOUT |
- MMCI_INT_RX_DATA_AVAIL | MMCI_INT_TX_FIFO_EMPTY |
- MMCI_INT_DATA_END | MMCI_INT_DATA_BLOCK_END |
- MMCI_INT_CMD_RESP_END | MMCI_INT_CMD_SENT);
-
- sc->sc_intr_status = 0;
+ MMCI_WRITE(sc, MMCI_MASK0_REG, PLMMC_INT_DATA_MASK | PLMMC_INT_CMD_MASK);
if (cmd->c_flags & SCF_RSP_PRESENT)
cmdval |= MMCI_COMMAND_RESPONSE;
@@ -378,6 +438,10 @@ plmmc_do_command(sdmmc_chipset_handle_t
if (xferlen > 0) {
cmd->c_error = plmmc_pio_transfer(sc, cmd, xferlen);
if (cmd->c_error) {
+#ifdef PLMMC_DEBUG
+ device_printf(sc->sc_dev,
+ "MMCI_STATUS_REG = %08x\n", MMCI_READ(sc, MMCI_STATUS_REG));
+#endif
device_printf(sc->sc_dev,
"error (%d) waiting for xfer\n", cmd->c_error);
goto done;
@@ -385,12 +449,7 @@ plmmc_do_command(sdmmc_chipset_handle_t
}
if ((cmd->c_flags & SCF_RSP_PRESENT) && cmd->c_resid == 0) {
- cmd->c_error = plmmc_wait_status(sc,
- MMCI_INT_CMD_RESP_END|MMCI_INT_CMD_TIMEOUT, hz * 2);
- if (cmd->c_error == 0 &&
- (sc->sc_intr_status & MMCI_INT_CMD_TIMEOUT)) {
- cmd->c_error = ETIMEDOUT;
- }
+ cmd->c_error = plmmc_wait_cmd(sc);
if (cmd->c_error) {
#ifdef PLMMC_DEBUG
device_printf(sc->sc_dev,
@@ -419,14 +478,15 @@ plmmc_do_command(sdmmc_chipset_handle_t
}
done:
+ sc->sc_cmd = NULL;
+
MMCI_WRITE(sc, MMCI_COMMAND_REG, 0);
MMCI_WRITE(sc, MMCI_MASK0_REG, 0);
MMCI_WRITE(sc, MMCI_CLEAR_REG, 0xffffffff);
MMCI_WRITE(sc, MMCI_DATA_CNT_REG, 0);
#ifdef PLMMC_DEBUG
- device_printf(sc->sc_dev, "MMCI_STATUS_REG = %#x\n",
- MMCI_READ(sc, MMCI_STATUS_REG));
+ device_printf(sc->sc_dev, "status = %#x\n", sc->sc_intr_status);
#endif
}
@@ -440,7 +500,7 @@ plmmc_exec_command(sdmmc_chipset_handle_
cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen);
#endif
- mutex_enter(&sc->sc_intr_lock);
+ mutex_enter(&sc->sc_lock);
cmd->c_resid = cmd->c_datalen;
cmd->c_buf = cmd->c_data;
do {
@@ -460,7 +520,7 @@ plmmc_exec_command(sdmmc_chipset_handle_
}
} while (cmd->c_resid > 0 && cmd->c_error == 0);
cmd->c_flags |= SCF_ITSDONE;
- mutex_exit(&sc->sc_intr_lock);
+ mutex_exit(&sc->sc_lock);
}
static void
Index: src/sys/dev/ic/pl181var.h
diff -u src/sys/dev/ic/pl181var.h:1.2 src/sys/dev/ic/pl181var.h:1.3
--- src/sys/dev/ic/pl181var.h:1.2 Fri Jun 2 11:01:15 2017
+++ src/sys/dev/ic/pl181var.h Mon Feb 19 19:00:42 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: pl181var.h,v 1.2 2017/06/02 11:01:15 jmcneill Exp $ */
+/* $NetBSD: pl181var.h,v 1.3 2018/02/19 19:00:42 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -33,15 +33,17 @@ struct plmmc_softc {
device_t sc_dev;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
- void *sc_ih;
unsigned int sc_clock_freq;
unsigned int sc_max_freq;
device_t sc_sdmmc_dev;
- kmutex_t sc_intr_lock;
+ kmutex_t sc_lock;
kcondvar_t sc_intr_cv;
-
uint32_t sc_intr_status;
+
+ struct sdmmc_command *sc_cmd;
+ int sc_fifo_resid;
+ uint32_t sc_status_mask;
};
void plmmc_init(struct plmmc_softc *);