From a93eca74ce9b74cb6e6611664b1cc7f86030511b Mon Sep 17 00:00:00 2001 From: Asutosh Das Date: Wed, 20 Mar 2013 22:53:40 +0530 Subject: mmc: sdhci: Check device state before starting a request This patch checks the device state before starting a request. It also prints out useful information in case of error conditions. Change-Id: Iaf87bb069c3ffb13c9b3f174c07c25d612bdcee9 Signed-off-by: Asutosh Das [venkatg@codeaurora.org: remove pm related stuff] Signed-off-by: Venkat Gopalakrishnan [subhashj@codeaurora.org: fixed merge conflicts] Signed-off-by: Subhash Jadavani --- drivers/mmc/host/sdhci.c | 96 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5adddc4b30ce..7c52867bc5c3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -79,60 +79,80 @@ static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) } #endif +static void sdhci_dump_state(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + + #ifdef CONFIG_MMC_CLKGATE + pr_info("%s: clk: %d clk-gated: %d claimer: %s pwr: %d\n", + mmc_hostname(mmc), host->clock, mmc->clk_gated, + mmc->claimer->comm, host->pwr); + #else + pr_info("%s: clk: %d claimer: %s pwr: %d\n", + mmc_hostname(mmc), host->clock, + mmc->claimer->comm, host->pwr); + #endif + pr_info("%s: rpmstatus[pltfm](runtime-suspend:usage_count:disable_depth)(%d:%d:%d)\n", + mmc_hostname(mmc), mmc->parent->power.runtime_status, + atomic_read(&mmc->parent->power.usage_count), + mmc->parent->power.disable_depth); +} + static void sdhci_dumpregs(struct sdhci_host *host) { - pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", + pr_info(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", mmc_hostname(host->mmc)); - pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", + pr_info(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", sdhci_readl(host, SDHCI_DMA_ADDRESS), sdhci_readw(host, SDHCI_HOST_VERSION)); - pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", + pr_info(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", sdhci_readw(host, SDHCI_BLOCK_SIZE), sdhci_readw(host, SDHCI_BLOCK_COUNT)); - pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", + pr_info(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", sdhci_readl(host, SDHCI_ARGUMENT), sdhci_readw(host, SDHCI_TRANSFER_MODE)); - pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", + pr_info(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", sdhci_readl(host, SDHCI_PRESENT_STATE), sdhci_readb(host, SDHCI_HOST_CONTROL)); - pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", + pr_info(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", sdhci_readb(host, SDHCI_POWER_CONTROL), sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); - pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", + pr_info(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), sdhci_readw(host, SDHCI_CLOCK_CONTROL)); - pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", + pr_info(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), sdhci_readl(host, SDHCI_INT_STATUS)); - pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", + pr_info(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", sdhci_readl(host, SDHCI_INT_ENABLE), sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); - pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", + pr_info(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", sdhci_readw(host, SDHCI_ACMD12_ERR), sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); - pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", + pr_info(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", sdhci_readl(host, SDHCI_CAPABILITIES), sdhci_readl(host, SDHCI_CAPABILITIES_1)); - pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", + pr_info(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", sdhci_readw(host, SDHCI_COMMAND), sdhci_readl(host, SDHCI_MAX_CURRENT)); - pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", + pr_info(DRIVER_NAME ": Host ctl2: 0x%08x\n", sdhci_readw(host, SDHCI_HOST_CONTROL2)); if (host->flags & SDHCI_USE_ADMA) { if (host->flags & SDHCI_USE_64_BIT_DMA) - pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", + pr_info(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", readl(host->ioaddr + SDHCI_ADMA_ERROR), readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); else - pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + pr_info(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", readl(host->ioaddr + SDHCI_ADMA_ERROR), readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); } - pr_debug(DRIVER_NAME ": ===========================================\n"); + sdhci_dump_state(host); + pr_info(DRIVER_NAME ": ===========================================\n"); } /*****************************************************************************\ @@ -1393,6 +1413,14 @@ static int sdhci_disable(struct mmc_host *mmc) return 0; } +static bool sdhci_check_state(struct sdhci_host *host) +{ + if (!host->clock || !host->pwr) + return true; + else + return false; +} + static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; @@ -1402,6 +1430,15 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host = mmc_priv(mmc); sdhci_runtime_pm_get(host); + if (sdhci_check_state(host)) { + sdhci_dump_state(host); + WARN(1, "sdhci in bad state"); + mrq->cmd->error = -EIO; + if (mrq->data) + mrq->data->error = -EIO; + tasklet_schedule(&host->finish_tasklet); + return; + } /* Firstly check card presence */ present = sdhci_do_get_cd(host); @@ -2348,6 +2385,11 @@ static void sdhci_timeout_timer(unsigned long data) sdhci_dumpregs(host); if (host->data) { + pr_info("%s: bytes to transfer: %d transferred: %d\n", + mmc_hostname(host->mmc), + (host->data->blksz * host->data->blocks), + (sdhci_readw(host, SDHCI_BLOCK_SIZE) & 0xFFF) * + sdhci_readw(host, SDHCI_BLOCK_COUNT)); host->data->error = -ETIMEDOUT; sdhci_finish_data(host); } else { @@ -2482,6 +2524,7 @@ static void sdhci_adma_show_error(struct sdhci_host *host) { } static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { u32 command; + bool pr_msg = false; BUG_ON(intmask == 0); /* CMD19 generates _only_ Buffer Read Ready interrupt */ @@ -2544,10 +2587,25 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (host->ops->adma_workaround) host->ops->adma_workaround(host, intmask); } - - if (host->data->error) + if (host->data->error) { + if ((intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT)) && + (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING)) { + command = SDHCI_GET_CMD(sdhci_readw(host, + SDHCI_COMMAND)); + if ((command != MMC_SEND_TUNING_BLOCK_HS200) && + (command != MMC_SEND_TUNING_BLOCK)) + pr_msg = true; + } else { + pr_msg = true; + } + if (pr_msg) { + pr_err("%s: data txfr (0x%08x) error: %d\n", + mmc_hostname(host->mmc), intmask, + host->data->error); + sdhci_dumpregs(host); + } sdhci_finish_data(host); - else { + } else { if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) sdhci_transfer_pio(host); -- cgit v1.2.3