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 <jmcne...@invisible.ca> @@ -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) {