diff options
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 1088 |
1 files changed, 973 insertions, 115 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 552a34dc4f82..b5b2c8612663 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -30,8 +30,12 @@ #include <linux/mmc/card.h> #include <linux/mmc/sdio.h> #include <linux/mmc/slot-gpio.h> +#include <linux/mmc/sdio.h> + +#include <trace/events/mmc.h> #include "sdhci.h" +#include "cmdq_hci.h" #define DRIVER_NAME "sdhci" @@ -45,6 +49,9 @@ #define MAX_TUNING_LOOP 40 +#define SDHCI_DBG_DUMP_RS_INTERVAL (10 * HZ) +#define SDHCI_DBG_DUMP_RS_BURST 2 + static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -52,10 +59,13 @@ static void sdhci_finish_data(struct sdhci_host *); static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); +static int sdhci_enhanced_strobe(struct mmc_host *mmc); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static int sdhci_pre_dma_transfer(struct sdhci_host *host, struct mmc_data *data); static int sdhci_do_get_cd(struct sdhci_host *host); +static bool sdhci_check_state(struct sdhci_host *); +static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable); #ifdef CONFIG_PM static int sdhci_runtime_pm_get(struct sdhci_host *host); @@ -79,60 +89,89 @@ 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 host->irq = %d\n", + mmc_hostname(mmc), host->clock, mmc->clk_gated, + mmc->claimer->comm, host->pwr, + (host->flags & SDHCI_HOST_IRQ_STATUS)); + #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", - sdhci_readw(host, SDHCI_ACMD12_ERR), + pr_info(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", + host->auto_cmd_err_sts, 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 ": Resp 1: 0x%08x | Resp 0: 0x%08x\n", + sdhci_readl(host, SDHCI_RESPONSE + 0x4), + sdhci_readl(host, SDHCI_RESPONSE)); + pr_info(DRIVER_NAME ": Resp 3: 0x%08x | Resp 2: 0x%08x\n", + sdhci_readl(host, SDHCI_RESPONSE + 0xC), + sdhci_readl(host, SDHCI_RESPONSE + 0x8)); + 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"); + if (host->ops->dump_vendor_regs) + host->ops->dump_vendor_regs(host); + sdhci_dump_state(host); + pr_info(DRIVER_NAME ": ===========================================\n"); } /*****************************************************************************\ @@ -177,6 +216,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; +retry_reset: sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); if (mask & SDHCI_RESET_ALL) { @@ -187,19 +227,58 @@ void sdhci_reset(struct sdhci_host *host, u8 mask) } /* Wait max 100 ms */ - timeout = 100; + timeout = 100000; + + if (host->ops->check_power_status && host->pwr && + (mask & SDHCI_RESET_ALL)) + host->ops->check_power_status(host, REQ_BUS_OFF); + + /* clear pending normal/error interrupt status */ + sdhci_writel(host, sdhci_readl(host, SDHCI_INT_STATUS), + SDHCI_INT_STATUS); /* hw clears the bit when it's done */ while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { if (timeout == 0) { pr_err("%s: Reset 0x%x never completed.\n", mmc_hostname(host->mmc), (int)mask); + if ((host->quirks2 & SDHCI_QUIRK2_USE_RESET_WORKAROUND) + && host->ops->reset_workaround) { + if (!host->reset_wa_applied) { + /* + * apply the workaround and issue + * reset again. + */ + host->ops->reset_workaround(host, 1); + host->reset_wa_applied = 1; + host->reset_wa_cnt++; + goto retry_reset; + } else { + pr_err("%s: Reset 0x%x failed with workaround\n", + mmc_hostname(host->mmc), + (int)mask); + /* clear the workaround */ + host->ops->reset_workaround(host, 0); + host->reset_wa_applied = 0; + } + } + sdhci_dumpregs(host); return; } timeout--; - mdelay(1); + udelay(1); } + + if ((host->quirks2 & SDHCI_QUIRK2_USE_RESET_WORKAROUND) && + host->ops->reset_workaround && host->reset_wa_applied) { + pr_info("%s: Reset 0x%x successful with workaround\n", + mmc_hostname(host->mmc), (int)mask); + /* clear the workaround */ + host->ops->reset_workaround(host, 0); + host->reset_wa_applied = 0; + } + } EXPORT_SYMBOL_GPL(sdhci_reset); @@ -221,6 +300,8 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) /* Resetting the controller clears many */ host->preset_enabled = false; } + if (host->is_crypto_en) + host->crypto_reset_reqd = true; } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); @@ -236,7 +317,7 @@ static void sdhci_init(struct sdhci_host *host, int soft) SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | - SDHCI_INT_RESPONSE; + SDHCI_INT_RESPONSE | SDHCI_INT_AUTO_CMD_ERR; sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); @@ -279,9 +360,12 @@ static void sdhci_led_control(struct led_classdev *led, struct sdhci_host *host = container_of(led, struct sdhci_host, led); unsigned long flags; + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + return; + spin_lock_irqsave(&host->lock, flags); - if (host->runtime_suspended) + if (host->runtime_suspended || sdhci_check_state(host)) goto out; if (brightness == LED_OFF) @@ -598,7 +682,10 @@ static void sdhci_adma_table_post(struct sdhci_host *host, void *align; char *buffer; unsigned long flags; - bool has_unaligned; + bool has_unaligned = false; + u32 command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)); + + trace_mmc_adma_table_post(command, data->sg_len); if (data->flags & MMC_DATA_READ) direction = DMA_FROM_DEVICE; @@ -648,6 +735,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) u8 count; struct mmc_data *data = cmd->data; unsigned target_timeout, current_timeout; + u32 curr_clk = 0; /* In KHz */ /* * If the host controller provides us with an incorrect timeout @@ -693,7 +781,14 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) * (1) / (2) > 2^6 */ count = 0; - current_timeout = (1 << 13) * 1000 / host->timeout_clk; + if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK) { + curr_clk = host->clock / 1000; + if (host->quirks2 & SDHCI_QUIRK2_DIVIDE_TOUT_BY_4) + curr_clk /= 4; + current_timeout = (1 << 13) * 1000 / curr_clk; + } else { + current_timeout = (1 << 13) * 1000 / host->timeout_clk; + } while (current_timeout < target_timeout) { count++; current_timeout <<= 1; @@ -701,10 +796,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) break; } - if (count >= 0xF) { - DBG("%s: Too large timeout 0x%x requested for CMD%d!\n", - mmc_hostname(host->mmc), count, cmd->opcode); - count = 0xE; + if (!(host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT)) { + if (count >= 0xF) { + DBG("%s: Too large timeout 0x%x requested for CMD%d!\n", + mmc_hostname(host->mmc), count, cmd->opcode); + count = 0xE; + } } return count; @@ -736,6 +833,17 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) } } +static void sdhci_set_blk_size_reg(struct sdhci_host *host, unsigned int blksz, + unsigned int sdma_boundary) +{ + if (host->flags & SDHCI_USE_ADMA) + sdhci_writew(host, SDHCI_MAKE_BLKSZ(0, blksz), + SDHCI_BLOCK_SIZE); + else + sdhci_writew(host, SDHCI_MAKE_BLKSZ(sdma_boundary, blksz), + SDHCI_BLOCK_SIZE); +} + static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) { u8 ctrl; @@ -751,7 +859,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) return; /* Sanity checks */ - BUG_ON(data->blksz * data->blocks > 524288); + BUG_ON(data->blksz * data->blocks > host->mmc->max_req_size); BUG_ON(data->blksz > host->mmc->max_blk_size); BUG_ON(data->blocks > 65535); @@ -762,6 +870,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) host->flags |= SDHCI_REQ_USE_DMA; + if ((host->quirks2 & SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING) && + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) + host->flags &= ~SDHCI_REQ_USE_DMA; + /* * FIXME: This doesn't account for merging when mapping the * scatterlist. @@ -828,6 +940,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) if (host->flags & SDHCI_REQ_USE_DMA) { if (host->flags & SDHCI_USE_ADMA) { + trace_mmc_adma_table_pre(cmd->opcode, data->sg_len); ret = sdhci_adma_table_pre(host, data); if (ret) { /* @@ -898,8 +1011,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) sdhci_set_transfer_irqs(host); /* Set the DMA boundary value and block size */ - sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, - data->blksz), SDHCI_BLOCK_SIZE); + sdhci_set_blk_size_reg(host, data->blksz, SDHCI_DEFAULT_BOUNDARY_ARG); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); } @@ -942,8 +1054,19 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, } } - if (data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) { mode |= SDHCI_TRNS_READ; + if (host->ops->toggle_cdr) { + if ((cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) || + (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400) || + (cmd->opcode == MMC_SEND_TUNING_BLOCK)) + host->ops->toggle_cdr(host, false); + else + host->ops->toggle_cdr(host, true); + } + } + if (host->ops->toggle_cdr && (data->flags & MMC_DATA_WRITE)) + host->ops->toggle_cdr(host, false); if (host->flags & SDHCI_REQ_USE_DMA) mode |= SDHCI_TRNS_DMA; @@ -1017,7 +1140,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) WARN_ON(host->cmd); /* Wait max 10 ms */ - timeout = 10; + timeout = 10000; mask = SDHCI_CMD_INHIBIT; if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) @@ -1038,7 +1161,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) return; } timeout--; - mdelay(1); + udelay(1); } timeout = jiffies; @@ -1084,6 +1207,9 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) flags |= SDHCI_CMD_DATA; + if (cmd->data) + host->data_start_time = ktime_get(); + trace_mmc_cmd_rw_start(cmd->opcode, cmd->arg, cmd->flags); sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); } EXPORT_SYMBOL_GPL(sdhci_send_command); @@ -1110,10 +1236,9 @@ static void sdhci_finish_command(struct sdhci_host *host) } } - host->cmd->error = 0; - /* Finished CMD23, now send actual command. */ if (host->cmd == host->mrq->sbc) { + host->cmd->error = 0; host->cmd = NULL; sdhci_send_command(host, host->mrq->cmd); } else { @@ -1173,7 +1298,8 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) host->mmc->actual_clock = 0; - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + if (host->clock) + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_INT_CLK_RST) mdelay(1); @@ -1257,6 +1383,10 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clock_set: if (real_div) host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; + + if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK) + div = 0; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; @@ -1264,7 +1394,7 @@ clock_set: sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); /* Wait max 20 ms */ - timeout = 20; + timeout = 20000; while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { @@ -1274,7 +1404,7 @@ clock_set: return; } timeout--; - mdelay(1); + udelay(1); } clk |= SDHCI_CLOCK_CARD_EN; @@ -1328,6 +1458,8 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, if (pwr == 0) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + if (host->ops->check_power_status) + host->ops->check_power_status(host, REQ_BUS_OFF); if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) sdhci_runtime_pm_bus_off(host); vdd = 0; @@ -1336,20 +1468,27 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, * Spec says that we should clear the power reg before setting * a new value. Some controllers don't seem to like this though. */ - if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) + if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - + if (host->ops->check_power_status) + host->ops->check_power_status(host, REQ_BUS_OFF); + } /* * At least the Marvell CaFe chip gets confused if we set the * voltage and set turn on power at the same time, so set the * voltage first. */ - if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) { sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->ops->check_power_status) + host->ops->check_power_status(host, REQ_BUS_ON); + } pwr |= SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->ops->check_power_status) + host->ops->check_power_status(host, REQ_BUS_ON); if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) sdhci_runtime_pm_bus_on(host); @@ -1369,6 +1508,132 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, * * \*****************************************************************************/ +static int sdhci_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->platform_bus_voting) + host->ops->platform_bus_voting(host, 1); + + return 0; +} + +static int sdhci_disable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->platform_bus_voting) + host->ops->platform_bus_voting(host, 0); + + return 0; +} + +static void sdhci_notify_halt(struct mmc_host *mmc, bool halt) +{ + struct sdhci_host *host = mmc_priv(mmc); + + pr_debug("%s: halt notification was sent, halt=%d\n", + mmc_hostname(mmc), halt); + if (host->flags & SDHCI_USE_64_BIT_DMA) { + if (halt) + host->desc_sz = 16; + else + host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; + } +} + +static inline void sdhci_update_power_policy(struct sdhci_host *host, + enum sdhci_power_policy policy) +{ + host->power_policy = policy; +} + +static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state) +{ + int err = 0; + struct sdhci_host *host = mmc_priv(mmc); + + switch (state) { + case MMC_LOAD_HIGH: + sdhci_update_power_policy(host, SDHCI_PERFORMANCE_MODE); + break; + case MMC_LOAD_LOW: + sdhci_update_power_policy(host, SDHCI_POWER_SAVE_MODE); + break; + default: + err = -EINVAL; + break; + } + + if (host->ops->notify_load) + err = host->ops->notify_load(host, state); + + return err; +} + +static bool sdhci_check_state(struct sdhci_host *host) +{ + if (!host->clock || !host->pwr) + return true; + else + return false; +} + +static bool sdhci_check_auto_tuning(struct sdhci_host *host, + struct mmc_command *cmd) +{ + if (((cmd->opcode != MMC_READ_SINGLE_BLOCK) && + (cmd->opcode != MMC_READ_MULTIPLE_BLOCK) && + (cmd->opcode != SD_IO_RW_EXTENDED)) || (host->clock < 100000000)) + return false; + else if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 || + host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) + return true; + else + return false; +} + +static int sdhci_get_tuning_cmd(struct sdhci_host *host) +{ + if (!host->mmc || !host->mmc->card) + return 0; + /* + * If we are here, all conditions have already been true + * and the card can either be an eMMC or SD/SDIO + */ + if (mmc_card_mmc(host->mmc->card)) + return MMC_SEND_TUNING_BLOCK_HS200; + else + return MMC_SEND_TUNING_BLOCK; +} + +static int sdhci_crypto_cfg(struct sdhci_host *host, struct mmc_request *mrq, + u32 slot) +{ + int err = 0; + + if (host->crypto_reset_reqd && host->ops->crypto_engine_reset) { + err = host->ops->crypto_engine_reset(host); + if (err) { + pr_err("%s: crypto reset failed\n", + mmc_hostname(host->mmc)); + goto out; + } + host->crypto_reset_reqd = false; + } + + if (host->ops->crypto_engine_cfg) { + err = host->ops->crypto_engine_cfg(host, mrq, slot); + if (err) { + pr_err("%s: failed to configure crypto\n", + mmc_hostname(host->mmc)); + goto out; + } + } +out: + return err; +} + static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; @@ -1378,16 +1643,41 @@ 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; + mmc_request_done(host->mmc, mrq); + sdhci_runtime_pm_put(host); + return; + } - /* Firstly check card presence */ - present = mmc->ops->get_cd(mmc); + /* + * Firstly check card presence from cd-gpio. The return could + * be one of the following possibilities: + * negative: cd-gpio is not available + * zero: cd-gpio is used, and card is removed + * one: cd-gpio is used, and card is present + */ + present = sdhci_do_get_cd(host); + if (present < 0) { + /* If polling, assume that the card is always present. */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + present = 1; + else + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; + } spin_lock_irqsave(&host->lock, flags); WARN_ON(host->mrq != NULL); #ifndef SDHCI_USE_LEDS_CLASS - sdhci_activate_led(host); + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL)) + sdhci_activate_led(host); #endif /* @@ -1407,6 +1697,22 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } else { + if (host->ops->config_auto_tuning_cmd) { + if (sdhci_check_auto_tuning(host, mrq->cmd)) + host->ops->config_auto_tuning_cmd(host, true, + sdhci_get_tuning_cmd(host)); + else + host->ops->config_auto_tuning_cmd(host, false, + sdhci_get_tuning_cmd(host)); + } + + if (host->is_crypto_en) { + spin_unlock_irqrestore(&host->lock, flags); + if (sdhci_crypto_cfg(host, mrq, 0)) + goto end_req; + spin_lock_irqsave(&host->lock, flags); + } + if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) sdhci_send_command(host, mrq->sbc); else @@ -1415,6 +1721,15 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) mmiowb(); spin_unlock_irqrestore(&host->lock, flags); + return; +end_req: + mrq->cmd->error = -EIO; + if (mrq->data) + mrq->data->error = -EIO; + host->mrq = NULL; + sdhci_dumpregs(host); + mmc_request_done(host->mmc, mrq); + sdhci_runtime_pm_put(host); } void sdhci_set_bus_width(struct sdhci_host *host, int width) @@ -1463,38 +1778,50 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) } EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); +void sdhci_cfg_irq(struct sdhci_host *host, bool enable, bool sync) +{ + if (enable && !(host->flags & SDHCI_HOST_IRQ_STATUS)) { + enable_irq(host->irq); + host->flags |= SDHCI_HOST_IRQ_STATUS; + } else if (!enable && (host->flags & SDHCI_HOST_IRQ_STATUS)) { + if (sync) + disable_irq(host->irq); + else + disable_irq_nosync(host->irq); + host->flags &= ~SDHCI_HOST_IRQ_STATUS; + } +} +EXPORT_SYMBOL(sdhci_cfg_irq); + static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; u8 ctrl; struct mmc_host *mmc = host->mmc; - - spin_lock_irqsave(&host->lock, flags); + int ret; if (host->flags & SDHCI_DEVICE_DEAD) { - spin_unlock_irqrestore(&host->lock, flags); if (!IS_ERR(mmc->supply.vmmc) && ios->power_mode == MMC_POWER_OFF) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); return; } - /* - * Reset the chip on each power off. - * Should clear out any weird states. - */ - if (ios->power_mode == MMC_POWER_OFF) { - sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); - sdhci_reinit(host); - } - if (host->version >= SDHCI_SPEC_300 && (ios->power_mode == MMC_POWER_UP) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); - if (!ios->clock || ios->clock != host->clock) { + spin_lock_irqsave(&host->lock, flags); + if (host->mmc && host->mmc->card && + mmc_card_sdio(host->mmc->card)) + sdhci_cfg_irq(host, false, false); + + if (ios->clock && + ((ios->clock != host->clock) || (ios->timing != host->timing))) { + spin_unlock_irqrestore(&host->lock, flags); host->ops->set_clock(host, ios->clock); + spin_lock_irqsave(&host->lock, flags); host->clock = ios->clock; if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && @@ -1509,8 +1836,45 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) host->mmc->max_busy_timeout /= host->timeout_clk; } } + if (ios->clock && host->sdio_irq_async_status) + sdhci_enable_sdio_irq_nolock(host, false); + spin_unlock_irqrestore(&host->lock, flags); - sdhci_set_power(host, ios->power_mode, ios->vdd); + /* + * The controller clocks may be off during power-up and we may end up + * enabling card clock before giving power to the card. Hence, during + * MMC_POWER_UP enable the controller clock and turn-on the regulators. + * The mmc_power_up would provide the necessary delay before turning on + * the clocks to the card. + */ + if (ios->power_mode & MMC_POWER_UP) { + if (host->ops->enable_controller_clock) { + ret = host->ops->enable_controller_clock(host); + if (ret) { + pr_err("%s: enabling controller clock: failed: %d\n", + mmc_hostname(host->mmc), ret); + } else { + sdhci_set_power(host, ios->power_mode, ios->vdd); + } + } + } + + spin_lock_irqsave(&host->lock, flags); + if (!host->clock) { + if (host->mmc && host->mmc->card && + mmc_card_sdio(host->mmc->card)) + sdhci_cfg_irq(host, true, false); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + spin_unlock_irqrestore(&host->lock, flags); + + if (!host->ops->enable_controller_clock && (ios->power_mode & + (MMC_POWER_UP | + MMC_POWER_ON))) + sdhci_set_power(host, ios->power_mode, ios->vdd); + + spin_lock_irqsave(&host->lock, flags); if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); @@ -1578,7 +1942,11 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ - host->ops->set_clock(host, host->clock); + if (ios->clock) { + spin_unlock_irqrestore(&host->lock, flags); + host->ops->set_clock(host, host->clock); + spin_lock_irqsave(&host->lock, flags); + } } /* Reset SD Clock Enable */ @@ -1605,10 +1973,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - host->ops->set_clock(host, host->clock); + if (ios->clock) { + spin_unlock_irqrestore(&host->lock, flags); + host->ops->set_clock(host, host->clock); + spin_lock_irqsave(&host->lock, flags); + } } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + spin_unlock_irqrestore(&host->lock, flags); /* * Some (ENE) controllers go apeshit on some ios operation, * signalling timeout and CRC errors even on CMD0. Resetting @@ -1617,8 +1990,25 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); - mmiowb(); + /* + * Reset the chip on each power off. + * Should clear out any weird states. + */ + if (ios->power_mode == MMC_POWER_OFF) { + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + sdhci_reinit(host); + sdhci_set_power(host, ios->power_mode, ios->vdd); + } + if (!ios->clock) + host->ops->set_clock(host, ios->clock); + + spin_lock_irqsave(&host->lock, flags); + if (host->mmc && host->mmc->card && + mmc_card_sdio(host->mmc->card)) + sdhci_cfg_irq(host, true, false); spin_unlock_irqrestore(&host->lock, flags); + + mmiowb(); } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -1730,16 +2120,28 @@ static int sdhci_get_ro(struct mmc_host *mmc) static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { - if (!(host->flags & SDHCI_DEVICE_DEAD)) { + u16 ctrl = 0; + + if (host->flags & SDHCI_DEVICE_DEAD) + return; + + if (mmc_card_and_host_support_async_int(host->mmc)) { + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); if (enable) - host->ier |= SDHCI_INT_CARD_INT; + ctrl |= SDHCI_CTRL_ASYNC_INT_ENABLE; else - host->ier &= ~SDHCI_INT_CARD_INT; - - sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); - sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); - mmiowb(); + ctrl &= ~SDHCI_CTRL_ASYNC_INT_ENABLE; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); } + + if (enable) + host->ier |= SDHCI_INT_CARD_INT; + else + host->ier &= ~SDHCI_INT_CARD_INT; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + mmiowb(); } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1782,6 +2184,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, /* Set 1.8V Signal Enable in the Host Control2 register to 0 */ ctrl &= ~SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + if (host->ops->check_power_status) + host->ops->check_power_status(host, REQ_IO_HIGH); if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000, @@ -1821,6 +2225,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, */ ctrl |= SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + if (host->ops->check_power_status) + host->ops->check_power_status(host, REQ_IO_LOW); /* Some controller need to do more when switching */ if (host->ops->voltage_switch) @@ -1891,6 +2297,19 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) return 0; } +static int sdhci_enhanced_strobe(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + int err = 0; + + sdhci_runtime_pm_get(host); + if (host->ops->enhanced_strobe) + err = host->ops->enhanced_strobe(host); + sdhci_runtime_pm_put(host); + + return err; +} + static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); @@ -1920,9 +2339,10 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) switch (host->timing) { /* HS400 tuning is done in HS200 mode */ case MMC_TIMING_MMC_HS400: - err = -EINVAL; - goto out_unlock; - + if (!(mmc->caps2 & MMC_CAP2_HS400_POST_TUNING)) { + err = -EINVAL; + goto out_unlock; + } case MMC_TIMING_MMC_HS200: /* * Periodic re-tuning for HS400 is not expected to be needed, so @@ -2000,14 +2420,11 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) */ if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) { if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), - SDHCI_BLOCK_SIZE); + sdhci_set_blk_size_reg(host, 128, 7); else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), - SDHCI_BLOCK_SIZE); + sdhci_set_blk_size_reg(host, 64, 7); } else { - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), - SDHCI_BLOCK_SIZE); + sdhci_set_blk_size_reg(host, 64, 7); } /* @@ -2110,6 +2527,9 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) if (host->version < SDHCI_SPEC_300) return; + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_PRESET_VALUE) + return; + /* * We only enable or disable Preset Value if they are not already * enabled or disabled respectively. Otherwise, we bail out. @@ -2147,6 +2567,8 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, DMA_TO_DEVICE : DMA_FROM_DEVICE); data->host_cookie = COOKIE_UNMAPPED; } + if (host->ops->post_req) + host->ops->post_req(host, mrq); } static int sdhci_pre_dma_transfer(struct sdhci_host *host, @@ -2183,6 +2605,9 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, if (host->flags & SDHCI_REQ_USE_DMA) sdhci_pre_dma_transfer(host, mrq->data); + + if (host->ops->pre_req) + host->ops->pre_req(host, mrq); } static void sdhci_card_event(struct mmc_host *mmc) @@ -2216,7 +2641,29 @@ static void sdhci_card_event(struct mmc_host *mmc) spin_unlock_irqrestore(&host->lock, flags); } +static int sdhci_late_init(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->init) + host->ops->init(host); + + return 0; +} + +static void sdhci_force_err_irq(struct mmc_host *mmc, u64 errmask) +{ + struct sdhci_host *host = mmc_priv(mmc); + u16 mask = errmask & 0xFFFF; + + pr_err("%s: Force raise error mask:0x%04x\n", __func__, mask); + sdhci_runtime_pm_get(host); + sdhci_writew(host, mask, SDHCI_SET_INT_ERROR); + sdhci_runtime_pm_put(host); +} + static const struct mmc_host_ops sdhci_ops = { + .init = sdhci_late_init, .request = sdhci_request, .post_req = sdhci_post_req, .pre_req = sdhci_pre_req, @@ -2228,9 +2675,15 @@ static const struct mmc_host_ops sdhci_ops = { .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .prepare_hs400_tuning = sdhci_prepare_hs400_tuning, .execute_tuning = sdhci_execute_tuning, + .enhanced_strobe = sdhci_enhanced_strobe, .select_drive_strength = sdhci_select_drive_strength, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, + .enable = sdhci_enable, + .disable = sdhci_disable, + .notify_load = sdhci_notify_load, + .notify_halt = sdhci_notify_halt, + .force_err_irq = sdhci_force_err_irq, }; /*****************************************************************************\ @@ -2282,14 +2735,19 @@ static void sdhci_tasklet_finish(unsigned long param) controllers do not like that. */ sdhci_do_reset(host, SDHCI_RESET_CMD); sdhci_do_reset(host, SDHCI_RESET_DATA); + } else { + if (host->quirks2 & SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT) + sdhci_reset(host, SDHCI_RESET_DATA); } host->mrq = NULL; host->cmd = NULL; host->data = NULL; + host->auto_cmd_err_sts = 0; #ifndef SDHCI_USE_LEDS_CLASS - sdhci_deactivate_led(host); + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL)) + sdhci_deactivate_led(host); #endif mmiowb(); @@ -2314,6 +2772,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 { @@ -2338,6 +2801,7 @@ static void sdhci_timeout_timer(unsigned long data) static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) { + u16 auto_cmd_status; BUG_ON(intmask == 0); if (!host->cmd) { @@ -2348,13 +2812,49 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) return; } + trace_mmc_cmd_rw_end(host->cmd->opcode, intmask, + sdhci_readl(host, SDHCI_RESPONSE)); + if (intmask & SDHCI_INT_TIMEOUT) host->cmd->error = -ETIMEDOUT; else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) host->cmd->error = -EILSEQ; + if (intmask & SDHCI_INT_AUTO_CMD_ERR) { + auto_cmd_status = host->auto_cmd_err_sts; + pr_err_ratelimited("%s: %s: AUTO CMD err sts 0x%08x\n", + mmc_hostname(host->mmc), __func__, auto_cmd_status); + if (auto_cmd_status & (SDHCI_AUTO_CMD12_NOT_EXEC | + SDHCI_AUTO_CMD_INDEX_ERR | + SDHCI_AUTO_CMD_ENDBIT_ERR)) + host->cmd->error = -EIO; + else if (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT_ERR) + host->cmd->error = -ETIMEDOUT; + else if (auto_cmd_status & SDHCI_AUTO_CMD_CRC_ERR) + host->cmd->error = -EILSEQ; + } + if (host->cmd->error) { + /* + * If this command initiates a data phase and a response + * CRC error is signalled, the card can start transferring + * data - the card may have received the command without + * error. We must not terminate the mmc_request early. + * + * If the card did not receive the command or returned an + * error which prevented it sending data, the data phase + * will time out. + * + * Even in case of cmd INDEX OR ENDBIT error we + * handle it the same way. + */ + if (host->cmd->data && + (((intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) == + SDHCI_INT_CRC) || (host->cmd->error == -EILSEQ))) { + host->cmd = NULL; + return; + } tasklet_schedule(&host->finish_tasklet); return; } @@ -2428,13 +2928,17 @@ 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); + command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)); + trace_mmc_data_rw_end(command, intmask); + /* CMD19 generates _only_ Buffer Read Ready interrupt */ if (intmask & SDHCI_INT_DATA_AVAIL) { - command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)); - if (command == MMC_SEND_TUNING_BLOCK || - command == MMC_SEND_TUNING_BLOCK_HS200) { + if (!(host->quirks2 & SDHCI_QUIRK2_NON_STANDARD_TUNING) && + (command == MMC_SEND_TUNING_BLOCK || + command == MMC_SEND_TUNING_BLOCK_HS200)) { host->tuning_done = 1; wake_up(&host->buf_ready_int); return; @@ -2465,6 +2969,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) host->busy_handle = 1; return; } + if (host->quirks2 & + SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD) + return; } pr_err("%s: Got data interrupt 0x%08x even " @@ -2480,8 +2987,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) else if (intmask & SDHCI_INT_DATA_END_BIT) host->data->error = -EILSEQ; else if ((intmask & SDHCI_INT_DATA_CRC) && - SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) - != MMC_BUS_TEST_R) + (command != MMC_BUS_TEST_R)) host->data->error = -EILSEQ; else if (intmask & SDHCI_INT_ADMA_ERROR) { pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); @@ -2490,10 +2996,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)) { + 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 && __ratelimit(&host->dbg_dump_rs)) { + pr_err("%s: data txfr (0x%08x) error: %d after %lld ms\n", + mmc_hostname(host->mmc), intmask, + host->data->error, ktime_to_ms(ktime_sub( + ktime_get(), host->data_start_time))); + sdhci_dumpregs(host); + } sdhci_finish_data(host); - else { + } else { if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) sdhci_transfer_pio(host); @@ -2539,6 +3060,58 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) } } +#ifdef CONFIG_MMC_CQ_HCI +static int sdhci_get_cmd_err(u32 intmask) +{ + if (intmask & SDHCI_INT_TIMEOUT) + return -ETIMEDOUT; + else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | + SDHCI_INT_INDEX)) + return -EILSEQ; + return 0; +} + +static int sdhci_get_data_err(u32 intmask) +{ + if (intmask & SDHCI_INT_DATA_TIMEOUT) + return -ETIMEDOUT; + else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) + return -EILSEQ; + else if (intmask & SDHCI_INT_ADMA_ERROR) + return -EIO; + return 0; +} + +static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask) +{ + int err = 0; + u32 mask = 0; + irqreturn_t ret; + + if (intmask & SDHCI_INT_CMD_MASK) + err = sdhci_get_cmd_err(intmask); + else if (intmask & SDHCI_INT_DATA_MASK) + err = sdhci_get_data_err(intmask); + + ret = cmdq_irq(host->mmc, err); + if (err) { + /* Clear the error interrupts */ + mask = intmask & SDHCI_INT_ERROR_MASK; + sdhci_writel(host, mask, SDHCI_INT_STATUS); + } + return ret; + +} + +#else +static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask) +{ + pr_err("%s: Received cmdq-irq when disabled !!!!\n", + mmc_hostname(host->mmc)); + return IRQ_NONE; +} +#endif + static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result = IRQ_NONE; @@ -2553,6 +3126,31 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) return IRQ_NONE; } + if (!host->clock && host->mmc->card && + mmc_card_sdio(host->mmc->card)) { + if (!mmc_card_and_host_support_async_int(host->mmc)) { + spin_unlock(&host->lock); + return IRQ_NONE; + } + /* + * async card interrupt is level sensitive and received + * when clocks are off. + * If sdio card has asserted async interrupt, in that + * case we need to disable host->irq. + * Later we can disable card interrupt and re-enable + * host->irq. + */ + + pr_debug("%s: %s: sdio_async intr. received\n", + mmc_hostname(host->mmc), __func__); + sdhci_cfg_irq(host, false, false); + host->sdio_irq_async_status = true; + host->thread_isr |= SDHCI_INT_CARD_INT; + result = IRQ_WAKE_THREAD; + spin_unlock(&host->lock); + return result; + } + intmask = sdhci_readl(host, SDHCI_INT_STATUS); if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; @@ -2560,6 +3158,19 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) } do { + if (host->mmc->card && mmc_card_cmdq(host->mmc->card) && + !mmc_host_halt(host->mmc) && !mmc_host_cq_disable(host->mmc)) { + pr_debug("*** %s: cmdq intr: 0x%08x\n", + mmc_hostname(host->mmc), + intmask); + result = sdhci_cmdq_irq(host, intmask); + if (result == IRQ_HANDLED) + goto out; + } + + if (intmask & SDHCI_INT_AUTO_CMD_ERR) + host->auto_cmd_err_sts = sdhci_readw(host, + SDHCI_AUTO_CMD_ERR); /* Clear selected interrupts. */ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | SDHCI_INT_BUS_POWER); @@ -2598,12 +3209,20 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) result = IRQ_WAKE_THREAD; } - if (intmask & SDHCI_INT_CMD_MASK) + if (intmask & SDHCI_INT_CMD_MASK) { + if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) && + (host->clock <= 400000)) + udelay(40); sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask); + } - if (intmask & SDHCI_INT_DATA_MASK) + if (intmask & SDHCI_INT_DATA_MASK) { + if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) && + (host->clock <= 400000)) + udelay(40); sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); + } if (intmask & SDHCI_INT_BUS_POWER) pr_err("%s: Card is consuming too much power!\n", @@ -2662,8 +3281,11 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) sdio_run_irqs(host->mmc); spin_lock_irqsave(&host->lock, flags); - if (host->flags & SDHCI_SDIO_IRQ_ENABLED) + if (host->flags & SDHCI_SDIO_IRQ_ENABLED) { + if (host->sdio_irq_async_status) + host->sdio_irq_async_status = false; sdhci_enable_sdio_irq_nolock(host, true); + } spin_unlock_irqrestore(&host->lock, flags); } @@ -2880,11 +3502,216 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, host->mmc_host_ops = sdhci_ops; mmc->ops = &host->mmc_host_ops; + spin_lock_init(&host->lock); + ratelimit_state_init(&host->dbg_dump_rs, SDHCI_DBG_DUMP_RS_INTERVAL, + SDHCI_DBG_DUMP_RS_BURST); + return host; } EXPORT_SYMBOL_GPL(sdhci_alloc_host); +#ifdef CONFIG_MMC_CQ_HCI +static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + u8 ctrl; + + if (host->version >= SDHCI_SPEC_200) { + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (host->flags & SDHCI_USE_ADMA_64BIT) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + } + if (host->ops->toggle_cdr) + host->ops->toggle_cdr(host, false); +} + +static void sdhci_cmdq_clear_set_irqs(struct mmc_host *mmc, bool clear) +{ + struct sdhci_host *host = mmc_priv(mmc); + u32 ier = 0; + + ier &= ~SDHCI_INT_ALL_MASK; + + if (clear) { + ier = SDHCI_INT_CMDQ_EN | SDHCI_INT_ERROR_MASK; + sdhci_writel(host, ier, SDHCI_INT_ENABLE); + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); + } else { + ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | + SDHCI_INT_INDEX | SDHCI_INT_END_BIT | + SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | + SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | + SDHCI_INT_AUTO_CMD_ERR; + sdhci_writel(host, ier, SDHCI_INT_ENABLE); + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); + } +} + +static void sdhci_cmdq_set_data_timeout(struct mmc_host *mmc, u32 val) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_writeb(host, val, SDHCI_TIMEOUT_CONTROL); +} + +static void sdhci_cmdq_dump_vendor_regs(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_dumpregs(host); +} + +static int sdhci_cmdq_init(struct sdhci_host *host, struct mmc_host *mmc, + bool dma64) +{ + return cmdq_init(host->cq_host, mmc, dma64); +} + +static void sdhci_cmdq_set_block_size(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_set_blk_size_reg(host, 512, 0); +} + +static void sdhci_enhanced_strobe_mask(struct mmc_host *mmc, bool set) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->enhanced_strobe_mask) + host->ops->enhanced_strobe_mask(host, set); +} + +static void sdhci_cmdq_clear_set_dumpregs(struct mmc_host *mmc, bool set) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (host->ops->clear_set_dumpregs) + host->ops->clear_set_dumpregs(host, set); +} +static int sdhci_cmdq_crypto_cfg(struct mmc_host *mmc, + struct mmc_request *mrq, u32 slot) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (!host->is_crypto_en) + return 0; + + return sdhci_crypto_cfg(host, mrq, slot); +} + +static void sdhci_cmdq_crypto_cfg_reset(struct mmc_host *mmc, unsigned int slot) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (!host->is_crypto_en) + return; + + if (host->ops->crypto_cfg_reset) + host->ops->crypto_cfg_reset(host, slot); +} + +static void sdhci_cmdq_post_cqe_halt(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_writel(host, sdhci_readl(host, SDHCI_INT_ENABLE) | + SDHCI_INT_RESPONSE, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); +} +#else +static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc) +{ + +} +static void sdhci_cmdq_clear_set_irqs(struct mmc_host *mmc, bool clear) +{ + +} + +static void sdhci_cmdq_set_data_timeout(struct mmc_host *mmc, u32 val) +{ + +} + +static void sdhci_cmdq_dump_vendor_regs(struct mmc_host *mmc) +{ + +} + +static int sdhci_cmdq_init(struct sdhci_host *host, struct mmc_host *mmc, + bool dma64) +{ + return -ENOSYS; +} + +static void sdhci_cmdq_set_block_size(struct mmc_host *mmc) +{ + +} + +static void sdhci_enhanced_strobe_mask(struct mmc_host *mmc, bool set) +{ + +} + +static void sdhci_cmdq_clear_set_dumpregs(struct mmc_host *mmc, bool set) +{ + +} +static int sdhci_cmdq_crypto_cfg(struct mmc_host *mmc, + struct mmc_request *mrq, u32 slot) +{ + return 0; +} + +static void sdhci_cmdq_crypto_cfg_reset(struct mmc_host *mmc, unsigned int slot) +{ + +} +static void sdhci_cmdq_post_cqe_halt(struct mmc_host *mmc) +{ +} +#endif + +static const struct cmdq_host_ops sdhci_cmdq_ops = { + .clear_set_irqs = sdhci_cmdq_clear_set_irqs, + .set_data_timeout = sdhci_cmdq_set_data_timeout, + .dump_vendor_regs = sdhci_cmdq_dump_vendor_regs, + .set_block_size = sdhci_cmdq_set_block_size, + .clear_set_dumpregs = sdhci_cmdq_clear_set_dumpregs, + .enhanced_strobe_mask = sdhci_enhanced_strobe_mask, + .crypto_cfg = sdhci_cmdq_crypto_cfg, + .crypto_cfg_reset = sdhci_cmdq_crypto_cfg_reset, + .post_cqe_halt = sdhci_cmdq_post_cqe_halt, + .set_transfer_params = sdhci_cmdq_set_transfer_params, +}; + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +static int sdhci_is_adma2_64bit(struct sdhci_host *host) +{ + u32 caps; + + caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : + sdhci_readl(host, SDHCI_CAPABILITIES); + + if (caps & SDHCI_CAN_64BIT) + return 1; + return 0; +} +#else +static int sdhci_is_adma2_64bit(struct sdhci_host *host) +{ + return 0; +} +#endif + int sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc; @@ -2957,7 +3784,7 @@ int sdhci_add_host(struct sdhci_host *host) * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to * implement. */ - if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) + if (sdhci_is_adma2_64bit(host)) host->flags |= SDHCI_USE_64_BIT_DMA; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { @@ -3112,6 +3939,9 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + if (caps[0] & SDHCI_CAN_ASYNC_INT) + mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE; + if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; @@ -3144,7 +3974,8 @@ int sdhci_add_host(struct sdhci_host *host) if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && !(mmc->caps & MMC_CAP_NONREMOVABLE) && - IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc))) + (IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc)) && + !(mmc->caps2 & MMC_CAP2_NONHOTPLUG))) mmc->caps |= MMC_CAP_NEEDS_POLL; /* If there are external regulators, get them */ @@ -3241,10 +4072,15 @@ int sdhci_add_host(struct sdhci_host *host) * value. */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); - if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) { - int curr = regulator_get_current_limit(mmc->supply.vmmc); - if (curr > 0) { + if (!max_current_caps) { + u32 curr = 0; + + if (!IS_ERR(mmc->supply.vmmc)) + curr = regulator_get_current_limit(mmc->supply.vmmc); + else if (host->ops->get_current_limit) + curr = host->ops->get_current_limit(host); + if (curr > 0) { /* convert to SDHCI_MAX_CURRENT format */ curr = curr/1000; /* convert to mA */ curr = curr/SDHCI_MAX_CURRENT_MULTIPLIER; @@ -3309,8 +4145,6 @@ int sdhci_add_host(struct sdhci_host *host) return -ENODEV; } - spin_lock_init(&host->lock); - /* * Maximum number of segments. Depends on if the hardware * can do scatter/gather or not. @@ -3376,6 +4210,8 @@ int sdhci_add_host(struct sdhci_host *host) init_waitqueue_head(&host->buf_ready_int); + host->flags |= SDHCI_HOST_IRQ_STATUS; + sdhci_init(host, 0); ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, @@ -3391,33 +4227,54 @@ int sdhci_add_host(struct sdhci_host *host) #endif #ifdef SDHCI_USE_LEDS_CLASS - snprintf(host->led_name, sizeof(host->led_name), - "%s::", mmc_hostname(mmc)); - host->led.name = host->led_name; - host->led.brightness = LED_OFF; - host->led.default_trigger = mmc_hostname(mmc); - host->led.brightness_set = sdhci_led_control; - - ret = led_classdev_register(mmc_dev(mmc), &host->led); - if (ret) { - pr_err("%s: Failed to register LED device: %d\n", - mmc_hostname(mmc), ret); - goto reset; + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL)) { + snprintf(host->led_name, sizeof(host->led_name), + "%s::", mmc_hostname(mmc)); + host->led.name = host->led_name; + host->led.brightness = LED_OFF; + host->led.default_trigger = mmc_hostname(mmc); + host->led.brightness_set = sdhci_led_control; + + ret = led_classdev_register(mmc_dev(mmc), &host->led); + if (ret) { + pr_err("%s: Failed to register LED device: %d\n", + mmc_hostname(mmc), ret); + goto reset; + } } #endif mmiowb(); - mmc_add_host(mmc); + if (host->quirks2 & SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR) { + host->ier = (host->ier & ~SDHCI_INT_DATA_END_BIT); + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + } + + if (mmc->caps2 & MMC_CAP2_CMD_QUEUE) { + bool dma64 = (host->flags & SDHCI_USE_64_BIT_DMA) ? + true : false; + ret = sdhci_cmdq_init(host, mmc, dma64); + if (ret) + pr_err("%s: CMDQ init: failed (%d)\n", + mmc_hostname(host->mmc), ret); + else + host->cq_host->ops = &sdhci_cmdq_ops; + } - pr_info("%s: SDHCI controller on %s [%s] using %s\n", - mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), + pr_info("%s: SDHCI controller on %s [%s] using %s in %s mode\n", + mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), (host->flags & SDHCI_USE_ADMA) ? - (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : - (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); + ((host->flags & SDHCI_USE_64_BIT_DMA) ? + "64-bit ADMA" : "32-bit ADMA") : + ((host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"), + ((mmc->caps2 & MMC_CAP2_CMD_QUEUE) && !ret) ? + "CMDQ" : "legacy"); sdhci_enable_card_detection(host); + mmc_add_host(mmc); return 0; #ifdef SDHCI_USE_LEDS_CLASS @@ -3458,10 +4315,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) sdhci_disable_card_detection(host); - mmc_remove_host(mmc); + mmc_remove_host(host->mmc); #ifdef SDHCI_USE_LEDS_CLASS - led_classdev_unregister(&host->led); + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL)) + led_classdev_unregister(&host->led); #endif if (!dead) |