From bf24108e88215f372bc9a3800872305e41436084 Mon Sep 17 00:00:00 2001 From: jmcneill Date: Thu, 23 Jan 2020 23:53:55 +0000 Subject: [PATCH] More SDIO stability and performance fixes --- sys/dev/ic/dwc_mmc.c | 59 ++++++++++++++++++++++++++++++++-------- sys/dev/ic/dwc_mmc_var.h | 4 ++- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/sys/dev/ic/dwc_mmc.c b/sys/dev/ic/dwc_mmc.c index 55dc5388f01c..91dd7cb5de41 100644 --- a/sys/dev/ic/dwc_mmc.c +++ b/sys/dev/ic/dwc_mmc.c @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc.c,v 1.21 2020/01/22 23:19:12 jmcneill Exp $ */ +/* $NetBSD: dwc_mmc.c,v 1.22 2020/01/23 23:53:55 jmcneill Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill @@ -27,7 +27,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.21 2020/01/22 23:19:12 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.22 2020/01/23 23:53:55 jmcneill Exp $"); #include #include @@ -297,6 +297,11 @@ dwc_mmc_write_protect(sdmmc_chipset_handle_t sch) static int dwc_mmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) { + struct dwc_mmc_softc *sc = sch; + + if (ocr == 0) + sc->sc_card_inited = false; + return 0; } @@ -573,6 +578,7 @@ dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) uint32_t cmdval = DWC_MMC_CMD_START; int retry, error; uint32_t imask; + u_int reg; #ifdef DWC_MMC_DEBUG aprint_normal_dev(sc->sc_dev, @@ -581,24 +587,41 @@ dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) cmd->c_blklen); #endif - mutex_enter(&sc->sc_intr_lock); + mutex_enter(&sc->sc_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); + mutex_exit(&sc->sc_lock); return; } sc->sc_curcmd = cmd; MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff); + if (!sc->sc_card_inited) { + cmdval |= DWC_MMC_CMD_SEND_INIT_SEQ; + sc->sc_card_inited = true; + } + if (ISSET(sc->sc_flags, DWC_MMC_F_USE_HOLD_REG)) cmdval |= DWC_MMC_CMD_USE_HOLD_REG; - if (cmd->c_opcode == MMC_GO_IDLE_STATE) - cmdval |= DWC_MMC_CMD_SEND_INIT_SEQ; + switch (cmd->c_opcode) { + case SD_IO_RW_DIRECT: + reg = (cmd->c_arg >> SD_ARG_CMD52_REG_SHIFT) & + SD_ARG_CMD52_REG_MASK; + if (reg != 0x6) /* func abort / card reset */ + break; + /* FALLTHROUGH */ + case MMC_GO_IDLE_STATE: + case MMC_STOP_TRANSMISSION: + case MMC_INACTIVE_STATE: + cmdval |= DWC_MMC_CMD_STOP_ABORT_CMD; + break; + } + if (cmd->c_flags & SCF_RSP_PRESENT) cmdval |= DWC_MMC_CMD_RSP_EXP; if (cmd->c_flags & SCF_RSP_136) @@ -613,10 +636,10 @@ dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) MMC_WRITE(sc, DWC_MMC_GCTRL, MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_FIFORESET); - for (retry = 0; retry < 100; retry++) { + for (retry = 0; retry < 100000; retry++) { if (!(MMC_READ(sc, DWC_MMC_DMAC) & DWC_MMC_DMAC_SOFTRESET)) break; - kpause("dwcmmcfifo", false, uimax(mstohz(1), 1), &sc->sc_intr_lock); + delay(1); } cmdval |= DWC_MMC_CMD_DATA_EXP | DWC_MMC_CMD_WAIT_PRE_OVER; @@ -671,6 +694,16 @@ dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) } sc->sc_wait_cmd = true; + if ((cmdval & DWC_MMC_CMD_WAIT_PRE_OVER) != 0) { + for (retry = 0; retry < 10000; retry++) { + if (!(MMC_READ(sc, DWC_MMC_STATUS) & DWC_MMC_STATUS_CARD_DATA_BUSY)) + break; + delay(1); + } + } + + mutex_enter(&sc->sc_intr_lock); + MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode); if (sc->sc_wait_dma) @@ -688,6 +721,8 @@ dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) } } + mutex_exit(&sc->sc_intr_lock); + if (cmd->c_error == 0 && cmd->c_datalen > 0) dwc_mmc_dma_complete(sc, cmd); @@ -720,8 +755,6 @@ done: MMC_WRITE(sc, DWC_MMC_IDIE, 0); MMC_WRITE(sc, DWC_MMC_RINT, 0x7fff); MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff); - sc->sc_curcmd = NULL; - mutex_exit(&sc->sc_intr_lock); if (cmd->c_error) { #ifdef DWC_MMC_DEBUG @@ -731,9 +764,12 @@ done: for (retry = 0; retry < 100; retry++) { if (!(MMC_READ(sc, DWC_MMC_DMAC) & DWC_MMC_DMAC_SOFTRESET)) break; - kpause("dwcmmcrst", false, uimax(mstohz(1), 1), NULL); + kpause("dwcmmcrst", false, uimax(mstohz(1), 1), &sc->sc_lock); } } + + sc->sc_curcmd = NULL; + mutex_exit(&sc->sc_lock); } static void @@ -788,6 +824,7 @@ dwc_mmc_init(struct dwc_mmc_softc *sc) if (sc->sc_intr_cardmask == 0) sc->sc_intr_cardmask = DWC_MMC_INT_SDIO_INT(0); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO); cv_init(&sc->sc_intr_cv, "dwcmmcirq"); diff --git a/sys/dev/ic/dwc_mmc_var.h b/sys/dev/ic/dwc_mmc_var.h index 0a3c071469f8..71e6e030b206 100644 --- a/sys/dev/ic/dwc_mmc_var.h +++ b/sys/dev/ic/dwc_mmc_var.h @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc_var.h,v 1.12 2020/01/22 23:19:12 jmcneill Exp $ */ +/* $NetBSD: dwc_mmc_var.h,v 1.13 2020/01/23 23:53:55 jmcneill Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill @@ -44,8 +44,10 @@ struct dwc_mmc_softc { uint32_t sc_fifo_depth; u_int sc_clock_freq; u_int sc_bus_width; + bool sc_card_inited; void *sc_ih; + kmutex_t sc_lock; kmutex_t sc_intr_lock; kcondvar_t sc_intr_cv;