Module Name: src
Committed By: jmcneill
Date: Thu Oct 3 13:49:33 UTC 2019
Modified Files:
src/sys/arch/arm/sunxi: sunxi_mmc.c
Log Message:
Rework interrupt handling to reduce the number of wakeups required for
each command. Fix SDIO interrupt handling while here.
To generate a diff of this commit:
cvs rdiff -u -r1.37 -r1.38 src/sys/arch/arm/sunxi/sunxi_mmc.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/arm/sunxi/sunxi_mmc.c
diff -u src/sys/arch/arm/sunxi/sunxi_mmc.c:1.37 src/sys/arch/arm/sunxi/sunxi_mmc.c:1.38
--- src/sys/arch/arm/sunxi/sunxi_mmc.c:1.37 Thu Sep 5 17:25:23 2019
+++ src/sys/arch/arm/sunxi/sunxi_mmc.c Thu Oct 3 13:49:33 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_mmc.c,v 1.37 2019/09/05 17:25:23 bouyer Exp $ */
+/* $NetBSD: sunxi_mmc.c,v 1.38 2019/10/03 13:49:33 jmcneill Exp $ */
/*-
* Copyright (c) 2014-2017 Jared McNeill <[email protected]>
@@ -29,7 +29,7 @@
#include "opt_sunximmc.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.37 2019/09/05 17:25:23 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.38 2019/10/03 13:49:33 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -98,6 +98,7 @@ static void sunxi_mmc_attach_i(device_t)
static int sunxi_mmc_intr(void *);
static int sunxi_mmc_dmabounce_setup(struct sunxi_mmc_softc *);
static int sunxi_mmc_idma_setup(struct sunxi_mmc_softc *);
+static void sunxi_mmc_dma_complete(struct sunxi_mmc_softc *, struct sdmmc_command *);
static int sunxi_mmc_host_reset(sdmmc_chipset_handle_t);
static uint32_t sunxi_mmc_host_ocr(sdmmc_chipset_handle_t);
@@ -153,7 +154,6 @@ struct sunxi_mmc_softc {
void *sc_ih;
kmutex_t sc_intr_lock;
kcondvar_t sc_intr_cv;
- kcondvar_t sc_idst_cv;
int sc_mmc_width;
int sc_mmc_present;
@@ -175,10 +175,6 @@ struct sunxi_mmc_softc {
void *sc_dmabounce_buf;
size_t sc_dmabounce_buflen;
- uint32_t sc_intr_rint;
- uint32_t sc_intr_card;
- uint32_t sc_idma_idst;
-
struct clk *sc_clk_ahb;
struct clk *sc_clk_mmc;
struct clk *sc_clk_output;
@@ -198,6 +194,12 @@ struct sunxi_mmc_softc {
bool sc_non_removable;
bool sc_broken_cd;
+
+ uint32_t sc_intr_card;
+ struct sdmmc_command *sc_curcmd;
+ bool sc_wait_dma;
+ bool sc_wait_cmd;
+ bool sc_wait_data;
};
CFATTACH_DECL_NEW(sunxi_mmc, sizeof(struct sunxi_mmc_softc),
@@ -351,8 +353,7 @@ sunxi_mmc_attach(device_t parent, device
sc->sc_bst = faa->faa_bst;
sc->sc_dmat = faa->faa_dmat;
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO);
- cv_init(&sc->sc_intr_cv, "awinmmcirq");
- cv_init(&sc->sc_idst_cv, "awinmmcdma");
+ cv_init(&sc->sc_intr_cv, "sunximmcirq");
if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
aprint_error(": couldn't map registers\n");
@@ -598,78 +599,89 @@ static int
sunxi_mmc_intr(void *priv)
{
struct sunxi_mmc_softc *sc = priv;
- uint32_t idst, rint, imask;
+ struct sdmmc_command *cmd;
+ uint32_t idst, mint, imask;
mutex_enter(&sc->sc_intr_lock);
idst = MMC_READ(sc, SUNXI_MMC_IDST);
- rint = MMC_READ(sc, SUNXI_MMC_RINT);
- if (!idst && !rint) {
+ mint = MMC_READ(sc, SUNXI_MMC_MINT);
+ if (!idst && !mint) {
mutex_exit(&sc->sc_intr_lock);
return 0;
}
MMC_WRITE(sc, SUNXI_MMC_IDST, idst);
- MMC_WRITE(sc, SUNXI_MMC_RINT, rint);
-
- DPRINTF(sc->sc_dev, "mmc intr idst=%08X rint=%08X\n",
- idst, rint);
+ MMC_WRITE(sc, SUNXI_MMC_RINT, mint);
- if (idst != 0) {
- MMC_WRITE(sc, SUNXI_MMC_IDIE, 0);
- sc->sc_idma_idst |= idst;
- cv_broadcast(&sc->sc_idst_cv);
- }
+ cmd = sc->sc_curcmd;
- if ((rint & ~SUNXI_MMC_INT_SDIO_INT) != 0) {
- imask = MMC_READ(sc, SUNXI_MMC_IMASK);
- MMC_WRITE(sc, SUNXI_MMC_IMASK, imask & SUNXI_MMC_INT_SDIO_INT);
- sc->sc_intr_rint |= (rint & ~SUNXI_MMC_INT_SDIO_INT);
- cv_broadcast(&sc->sc_intr_cv);
- }
+ DPRINTF(sc->sc_dev, "mmc intr idst=%08X mint=%08X\n",
+ idst, mint);
- if ((rint & SUNXI_MMC_INT_SDIO_INT) != 0) {
+ /* Handle SDIO card interrupt */
+ if ((mint & SUNXI_MMC_INT_SDIO_INT) != 0) {
imask = MMC_READ(sc, SUNXI_MMC_IMASK);
MMC_WRITE(sc, SUNXI_MMC_IMASK, imask & ~SUNXI_MMC_INT_SDIO_INT);
sdmmc_card_intr(sc->sc_sdmmc_dev);
}
- mutex_exit(&sc->sc_intr_lock);
+ /* Error interrupts take priority over command and transfer interrupts */
+ if (cmd != NULL && (mint & SUNXI_MMC_INT_ERROR) != 0) {
+ imask = MMC_READ(sc, SUNXI_MMC_IMASK);
+ MMC_WRITE(sc, SUNXI_MMC_IMASK, imask & ~SUNXI_MMC_INT_ERROR);
+ if ((mint & SUNXI_MMC_INT_RESP_TIMEOUT) != 0) {
+ cmd->c_error = ETIMEDOUT;
+ /* Wait for command to complete */
+ sc->sc_wait_data = sc->sc_wait_dma = false;
+ if (cmd->c_opcode != SD_IO_SEND_OP_COND &&
+ cmd->c_opcode != SD_IO_RW_DIRECT)
+ device_printf(sc->sc_dev, "host controller timeout, mint=0x%08x\n", mint);
+ } else {
+ device_printf(sc->sc_dev, "host controller error, mint=0x%08x\n", mint);
+ cmd->c_error = EIO;
+ SET(cmd->c_flags, SCF_ITSDONE);
+ goto done;
+ }
+ }
- return 1;
-}
+ if (cmd != NULL && (idst & SUNXI_MMC_IDST_RECEIVE_INT) != 0) {
+ MMC_WRITE(sc, SUNXI_MMC_IDIE, 0);
+ if (sc->sc_wait_dma == false)
+ device_printf(sc->sc_dev, "unexpected DMA receive interrupt\n");
+ sc->sc_wait_dma = false;
+ }
-static int
-sunxi_mmc_wait_rint(struct sunxi_mmc_softc *sc, uint32_t mask,
- int secs, bool poll)
-{
- int retry;
- int error;
+ if (cmd != NULL && (mint & SUNXI_MMC_INT_CMD_DONE) != 0) {
+ imask = MMC_READ(sc, SUNXI_MMC_IMASK);
+ MMC_WRITE(sc, SUNXI_MMC_IMASK, imask & ~SUNXI_MMC_INT_CMD_DONE);
+ if (sc->sc_wait_cmd == false)
+ device_printf(sc->sc_dev, "unexpected command complete interrupt\n");
+ sc->sc_wait_cmd = false;
+ }
- KASSERT(mutex_owned(&sc->sc_intr_lock));
+ const uint32_t dmadone_mask = SUNXI_MMC_INT_AUTO_CMD_DONE|SUNXI_MMC_INT_DATA_OVER;
+ if (cmd != NULL && (mint & dmadone_mask) != 0) {
+ imask = MMC_READ(sc, SUNXI_MMC_IMASK);
+ MMC_WRITE(sc, SUNXI_MMC_IMASK, imask & ~dmadone_mask);
+ if (sc->sc_wait_data == false)
+ device_printf(sc->sc_dev, "unexpected data complete interrupt\n");
+ sc->sc_wait_data = false;
+ }
- if (sc->sc_intr_rint & mask)
- return 0;
+ if (cmd != NULL &&
+ sc->sc_wait_dma == false &&
+ sc->sc_wait_cmd == false &&
+ sc->sc_wait_data == false) {
+ SET(cmd->c_flags, SCF_ITSDONE);
+ }
- if (poll) {
- retry = secs * 1000;
- while (retry > 0) {
- sc->sc_intr_rint |= MMC_READ(sc, SUNXI_MMC_RINT);
- if (sc->sc_intr_rint & mask)
- return 0;
- delay(1000);
- --retry;
- }
- return ETIMEDOUT;
- } else {
- struct bintime timeout = { .sec = secs, .frac = 0 };
- const struct bintime epsilon = { .sec = 1, .frac = 0 };
- while ((sc->sc_intr_rint & mask) == 0) {
- error = cv_timedwaitbt(&sc->sc_intr_cv,
- &sc->sc_intr_lock, &timeout, &epsilon);
- if (error != 0)
- return error;
- }
- return 0;
+done:
+ if (cmd != NULL && ISSET(cmd->c_flags, SCF_ITSDONE)) {
+ cv_broadcast(&sc->sc_intr_cv);
}
+
+ mutex_exit(&sc->sc_intr_lock);
+
+ return 1;
}
static int
@@ -1039,8 +1051,6 @@ sunxi_mmc_dma_prepare(struct sunxi_mmc_s
bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0,
sc->sc_idma_size, BUS_DMASYNC_PREWRITE);
- sc->sc_idma_idst = 0;
-
MMC_WRITE(sc, SUNXI_MMC_DLBA, desc_paddr);
MMC_WRITE(sc, SUNXI_MMC_FTRGLEVEL, sc->sc_config->dma_ftrglevel);
@@ -1066,6 +1076,7 @@ static void
sunxi_mmc_dma_complete(struct sunxi_mmc_softc *sc, struct sdmmc_command *cmd)
{
MMC_WRITE(sc, SUNXI_MMC_DMAC, 0);
+ MMC_WRITE(sc, SUNXI_MMC_IDIE, 0);
bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0,
sc->sc_idma_size, BUS_DMASYNC_POSTWRITE);
@@ -1088,16 +1099,24 @@ sunxi_mmc_exec_command(sdmmc_chipset_han
{
struct sunxi_mmc_softc *sc = sch;
uint32_t cmdval = SUNXI_MMC_CMD_START;
- uint32_t imask, oimask;
- const bool poll = (cmd->c_flags & SCF_POLL) != 0;
- int retry;
+ uint32_t imask;
+ int retry, error;
DPRINTF(sc->sc_dev,
- "opcode %d flags 0x%x data %p datalen %d blklen %d poll %d\n",
+ "opcode %d flags 0x%x data %p datalen %d blklen %d\n",
cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen,
- cmd->c_blklen, poll);
+ cmd->c_blklen);
mutex_enter(&sc->sc_intr_lock);
+ if (sc->sc_curcmd != NULL) {
+ device_printf(sc->sc_dev,
+ "WARNING: driver submitted a command while the controller was busy\n");
+ cmd->c_error = EBUSY;
+ SET(cmd->c_flags, SCF_ITSDONE);
+ mutex_exit(&sc->sc_intr_lock);
+ return;
+ }
+ sc->sc_curcmd = cmd;
if (cmd->c_opcode == 0)
cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ;
@@ -1108,8 +1127,7 @@ sunxi_mmc_exec_command(sdmmc_chipset_han
if (cmd->c_flags & SCF_RSP_CRC)
cmdval |= SUNXI_MMC_CMD_CHECK_RSP_CRC;
- imask = oimask = MMC_READ(sc, SUNXI_MMC_IMASK);
- imask |= SUNXI_MMC_INT_ERROR;
+ imask = SUNXI_MMC_INT_ERROR | SUNXI_MMC_INT_CMD_DONE;
if (cmd->c_datalen > 0) {
unsigned int nblks;
@@ -1132,79 +1150,50 @@ sunxi_mmc_exec_command(sdmmc_chipset_han
MMC_WRITE(sc, SUNXI_MMC_BLKSZ, cmd->c_blklen);
MMC_WRITE(sc, SUNXI_MMC_BYTECNT, nblks * cmd->c_blklen);
- } else {
- imask |= SUNXI_MMC_INT_CMD_DONE;
}
- MMC_WRITE(sc, SUNXI_MMC_IMASK, imask);
- MMC_WRITE(sc, SUNXI_MMC_RINT, 0xffff);
-
- sc->sc_intr_rint = 0;
+ MMC_WRITE(sc, SUNXI_MMC_IMASK, imask | sc->sc_intr_card);
+ MMC_WRITE(sc, SUNXI_MMC_RINT, 0x7fff);
MMC_WRITE(sc, SUNXI_MMC_A12A,
(cmdval & SUNXI_MMC_CMD_SEND_AUTO_STOP) ? 0 : 0xffff);
MMC_WRITE(sc, SUNXI_MMC_ARG, cmd->c_arg);
- DPRINTF(sc->sc_dev, "cmdval = %08x\n", cmdval);
-
- if (cmd->c_datalen == 0) {
- MMC_WRITE(sc, SUNXI_MMC_CMD, cmdval | cmd->c_opcode);
- } else {
- cmd->c_resid = cmd->c_datalen;
+ cmd->c_resid = cmd->c_datalen;
+ if (cmd->c_resid > 0) {
cmd->c_error = sunxi_mmc_dma_prepare(sc, cmd);
- MMC_WRITE(sc, SUNXI_MMC_CMD, cmdval | cmd->c_opcode);
- if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_CMD_READ)) {
- const uint32_t idst_mask = SUNXI_MMC_IDST_RECEIVE_INT;
-
- retry = 10;
- while ((sc->sc_idma_idst & idst_mask) == 0) {
- if (retry-- == 0) {
- cmd->c_error = ETIMEDOUT;
- break;
- }
- cv_timedwait(&sc->sc_idst_cv,
- &sc->sc_intr_lock, hz);
- }
+ if (cmd->c_error != 0) {
+ SET(cmd->c_flags, SCF_ITSDONE);
+ goto done;
}
+ sc->sc_wait_dma = ISSET(cmd->c_flags, SCF_CMD_READ);
+ sc->sc_wait_data = true;
+ } else {
+ sc->sc_wait_dma = false;
+ sc->sc_wait_data = false;
}
+ sc->sc_wait_cmd = true;
- cmd->c_error = sunxi_mmc_wait_rint(sc,
- SUNXI_MMC_INT_ERROR|SUNXI_MMC_INT_CMD_DONE, 5, poll);
- if (cmd->c_error == 0 && (sc->sc_intr_rint & SUNXI_MMC_INT_ERROR)) {
- if (sc->sc_intr_rint & SUNXI_MMC_INT_RESP_TIMEOUT) {
- cmd->c_error = ETIMEDOUT;
- } else {
- cmd->c_error = EIO;
- }
- }
- if (cmd->c_error) {
- DPRINTF(sc->sc_dev,
- "cmd failed, error %d\n", cmd->c_error);
- goto done;
- }
-
- if (cmd->c_datalen > 0) {
- sunxi_mmc_dma_complete(sc, cmd);
+ DPRINTF(sc->sc_dev, "cmdval = %08x\n", cmdval);
- cmd->c_error = sunxi_mmc_wait_rint(sc,
- SUNXI_MMC_INT_ERROR|
- SUNXI_MMC_INT_AUTO_CMD_DONE|
- SUNXI_MMC_INT_DATA_OVER,
- 5, poll);
- if (cmd->c_error == 0 &&
- (sc->sc_intr_rint & SUNXI_MMC_INT_ERROR)) {
- cmd->c_error = ETIMEDOUT;
- }
- if (cmd->c_error) {
- DPRINTF(sc->sc_dev,
- "data timeout, rint = %08x\n",
- sc->sc_intr_rint);
- cmd->c_error = ETIMEDOUT;
+ MMC_WRITE(sc, SUNXI_MMC_CMD, cmdval | cmd->c_opcode);
+
+ struct bintime timeout = { .sec = 15, .frac = 0 };
+ const struct bintime epsilon = { .sec = 1, .frac = 0 };
+ while (!ISSET(cmd->c_flags, SCF_ITSDONE)) {
+ error = cv_timedwaitbt(&sc->sc_intr_cv,
+ &sc->sc_intr_lock, &timeout, &epsilon);
+ if (error != 0) {
+ cmd->c_error = error;
+ SET(cmd->c_flags, SCF_ITSDONE);
goto done;
}
}
+ if (cmd->c_error == 0 && cmd->c_datalen > 0)
+ sunxi_mmc_dma_complete(sc, cmd);
+
if (cmd->c_flags & SCF_RSP_PRESENT) {
if (cmd->c_flags & SCF_RSP_136) {
cmd->c_resp[0] = MMC_READ(sc, SUNXI_MMC_RESP0);
@@ -1226,11 +1215,11 @@ sunxi_mmc_exec_command(sdmmc_chipset_han
}
done:
- cmd->c_flags |= SCF_ITSDONE;
- MMC_WRITE(sc, SUNXI_MMC_IMASK,
- (oimask & ~SUNXI_MMC_INT_SDIO_INT) | sc->sc_intr_card);
- MMC_WRITE(sc, SUNXI_MMC_RINT, 0xffff);
+ KASSERT(ISSET(cmd->c_flags, SCF_ITSDONE));
+ MMC_WRITE(sc, SUNXI_MMC_IMASK, sc->sc_intr_card);
+ MMC_WRITE(sc, SUNXI_MMC_RINT, 0x7fff);
MMC_WRITE(sc, SUNXI_MMC_IDST, 0x337);
+ sc->sc_curcmd = NULL;
mutex_exit(&sc->sc_intr_lock);
if (cmd->c_error) {