diff options
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 457 |
1 files changed, 293 insertions, 164 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6fdd7a70f229..ecd0d4350e8a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -14,6 +14,7 @@ */ #include <linux/delay.h> +#include <linux/ktime.h> #include <linux/highmem.h> #include <linux/io.h> #include <linux/module.h> @@ -37,7 +38,10 @@ #define DRIVER_NAME "sdhci" #define DBG(f, x...) \ - pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) + +#define SDHCI_DUMP(f, x...) \ + pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) #define MAX_TUNING_LOOP 40 @@ -48,61 +52,68 @@ static void sdhci_finish_data(struct sdhci_host *); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); -static void sdhci_dumpregs(struct sdhci_host *host) -{ - pr_err(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", - mmc_hostname(host->mmc)); - - pr_err(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", - sdhci_readl(host, SDHCI_DMA_ADDRESS), - sdhci_readw(host, SDHCI_HOST_VERSION)); - pr_err(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", - sdhci_readw(host, SDHCI_BLOCK_SIZE), - sdhci_readw(host, SDHCI_BLOCK_COUNT)); - pr_err(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", - sdhci_readl(host, SDHCI_ARGUMENT), - sdhci_readw(host, SDHCI_TRANSFER_MODE)); - pr_err(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", - sdhci_readl(host, SDHCI_PRESENT_STATE), - sdhci_readb(host, SDHCI_HOST_CONTROL)); - pr_err(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", - sdhci_readb(host, SDHCI_POWER_CONTROL), - sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); - pr_err(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", - sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), - sdhci_readw(host, SDHCI_CLOCK_CONTROL)); - pr_err(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", - sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), - sdhci_readl(host, SDHCI_INT_STATUS)); - pr_err(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", - sdhci_readl(host, SDHCI_INT_ENABLE), - sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); - pr_err(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_err(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", - sdhci_readl(host, SDHCI_CAPABILITIES), - sdhci_readl(host, SDHCI_CAPABILITIES_1)); - pr_err(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", - sdhci_readw(host, SDHCI_COMMAND), - sdhci_readl(host, SDHCI_MAX_CURRENT)); - pr_err(DRIVER_NAME ": Host ctl2: 0x%08x\n", - sdhci_readw(host, SDHCI_HOST_CONTROL2)); +void sdhci_dumpregs(struct sdhci_host *host) +{ + SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n"); + + SDHCI_DUMP("Sys addr: 0x%08x | Version: 0x%08x\n", + sdhci_readl(host, SDHCI_DMA_ADDRESS), + sdhci_readw(host, SDHCI_HOST_VERSION)); + SDHCI_DUMP("Blk size: 0x%08x | Blk cnt: 0x%08x\n", + sdhci_readw(host, SDHCI_BLOCK_SIZE), + sdhci_readw(host, SDHCI_BLOCK_COUNT)); + SDHCI_DUMP("Argument: 0x%08x | Trn mode: 0x%08x\n", + sdhci_readl(host, SDHCI_ARGUMENT), + sdhci_readw(host, SDHCI_TRANSFER_MODE)); + SDHCI_DUMP("Present: 0x%08x | Host ctl: 0x%08x\n", + sdhci_readl(host, SDHCI_PRESENT_STATE), + sdhci_readb(host, SDHCI_HOST_CONTROL)); + SDHCI_DUMP("Power: 0x%08x | Blk gap: 0x%08x\n", + sdhci_readb(host, SDHCI_POWER_CONTROL), + sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); + SDHCI_DUMP("Wake-up: 0x%08x | Clock: 0x%08x\n", + sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), + sdhci_readw(host, SDHCI_CLOCK_CONTROL)); + SDHCI_DUMP("Timeout: 0x%08x | Int stat: 0x%08x\n", + sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), + sdhci_readl(host, SDHCI_INT_STATUS)); + SDHCI_DUMP("Int enab: 0x%08x | Sig enab: 0x%08x\n", + sdhci_readl(host, SDHCI_INT_ENABLE), + sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); + SDHCI_DUMP("AC12 err: 0x%08x | Slot int: 0x%08x\n", + sdhci_readw(host, SDHCI_ACMD12_ERR), + sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); + SDHCI_DUMP("Caps: 0x%08x | Caps_1: 0x%08x\n", + sdhci_readl(host, SDHCI_CAPABILITIES), + sdhci_readl(host, SDHCI_CAPABILITIES_1)); + SDHCI_DUMP("Cmd: 0x%08x | Max curr: 0x%08x\n", + sdhci_readw(host, SDHCI_COMMAND), + sdhci_readl(host, SDHCI_MAX_CURRENT)); + SDHCI_DUMP("Resp[0]: 0x%08x | Resp[1]: 0x%08x\n", + sdhci_readl(host, SDHCI_RESPONSE), + sdhci_readl(host, SDHCI_RESPONSE + 4)); + SDHCI_DUMP("Resp[2]: 0x%08x | Resp[3]: 0x%08x\n", + sdhci_readl(host, SDHCI_RESPONSE + 8), + sdhci_readl(host, SDHCI_RESPONSE + 12)); + SDHCI_DUMP("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_err(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_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", - readl(host->ioaddr + SDHCI_ADMA_ERROR), - readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + if (host->flags & SDHCI_USE_64_BIT_DMA) { + SDHCI_DUMP("ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", + sdhci_readl(host, SDHCI_ADMA_ERROR), + sdhci_readl(host, SDHCI_ADMA_ADDRESS_HI), + sdhci_readl(host, SDHCI_ADMA_ADDRESS)); + } else { + SDHCI_DUMP("ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + sdhci_readl(host, SDHCI_ADMA_ERROR), + sdhci_readl(host, SDHCI_ADMA_ADDRESS)); + } } - pr_err(DRIVER_NAME ": ===========================================\n"); + SDHCI_DUMP("============================================\n"); } +EXPORT_SYMBOL_GPL(sdhci_dumpregs); /*****************************************************************************\ * * @@ -165,7 +176,7 @@ static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) void sdhci_reset(struct sdhci_host *host, u8 mask) { - unsigned long timeout; + ktime_t timeout; sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); @@ -177,18 +188,17 @@ void sdhci_reset(struct sdhci_host *host, u8 mask) } /* Wait max 100 ms */ - timeout = 100; + timeout = ktime_add_ms(ktime_get(), 100); /* hw clears the bit when it's done */ while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { - if (timeout == 0) { + if (ktime_after(ktime_get(), timeout)) { pr_err("%s: Reset 0x%x never completed.\n", mmc_hostname(host->mmc), (int)mask); sdhci_dumpregs(host); return; } - timeout--; - mdelay(1); + udelay(10); } } EXPORT_SYMBOL_GPL(sdhci_reset); @@ -215,15 +225,8 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) } } -static void sdhci_init(struct sdhci_host *host, int soft) +static void sdhci_set_default_irqs(struct sdhci_host *host) { - struct mmc_host *mmc = host->mmc; - - if (soft) - sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); - else - sdhci_do_reset(host, SDHCI_RESET_ALL); - host->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 | @@ -236,6 +239,20 @@ static void sdhci_init(struct sdhci_host *host, int soft) sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_init(struct sdhci_host *host, int soft) +{ + struct mmc_host *mmc = host->mmc; + + if (soft) + sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + else + sdhci_do_reset(host, SDHCI_RESET_ALL); + + sdhci_set_default_irqs(host); + + host->cqe_on = false; if (soft) { /* force clock reconfiguration */ @@ -485,8 +502,7 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host, return data->sg_count; sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - data->flags & MMC_DATA_WRITE ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); + mmc_get_dma_dir(data)); if (sg_count == 0) return -ENOSPC; @@ -715,8 +731,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) } if (count >= 0xF) { - DBG("%s: Too large timeout 0x%x requested for CMD%d!\n", - mmc_hostname(host->mmc), count, cmd->opcode); + DBG("Too large timeout 0x%x requested for CMD%d!\n", + count, cmd->opcode); count = 0xE; } @@ -1346,23 +1362,22 @@ EXPORT_SYMBOL_GPL(sdhci_calc_clk); void sdhci_enable_clk(struct sdhci_host *host, u16 clk) { - unsigned long timeout; + ktime_t timeout; clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); /* Wait max 20 ms */ - timeout = 20; + timeout = ktime_add_ms(ktime_get(), 20); while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { - if (timeout == 0) { + if (ktime_after(ktime_get(), timeout)) { pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc)); sdhci_dumpregs(host); return; } - timeout--; - mdelay(1); + udelay(10); } clk |= SDHCI_CLOCK_CARD_EN; @@ -1391,9 +1406,7 @@ static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode, { struct mmc_host *mmc = host->mmc; - spin_unlock_irq(&host->lock); mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); - spin_lock_irq(&host->lock); if (mode != MMC_POWER_OFF) sdhci_writeb(host, SDHCI_POWER_ON, SDHCI_POWER_CONTROL); @@ -1570,19 +1583,15 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) } EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); -static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sdhci_host *host = mmc_priv(mmc); - unsigned long flags; u8 ctrl; if (ios->power_mode == MMC_POWER_UNDEFINED) return; - spin_lock_irqsave(&host->lock, flags); - 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); @@ -1728,8 +1737,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); } +EXPORT_SYMBOL_GPL(sdhci_set_ios); static int sdhci_get_cd(struct mmc_host *mmc) { @@ -1823,11 +1832,14 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) } } -static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) +void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; + if (enable) + pm_runtime_get_noresume(host->mmc->parent); + spin_lock_irqsave(&host->lock, flags); if (enable) host->flags |= SDHCI_SDIO_IRQ_ENABLED; @@ -1836,10 +1848,14 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) sdhci_enable_sdio_irq_nolock(host, enable); spin_unlock_irqrestore(&host->lock, flags); + + if (!enable) + pm_runtime_put_noidle(host->mmc->parent); } +EXPORT_SYMBOL_GPL(sdhci_enable_sdio_irq); -static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, - struct mmc_ios *ios) +int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) { struct sdhci_host *host = mmc_priv(mmc); u16 ctrl; @@ -1931,6 +1947,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return 0; } } +EXPORT_SYMBOL_GPL(sdhci_start_signal_voltage_switch); static int sdhci_card_busy(struct mmc_host *mmc) { @@ -1995,8 +2012,7 @@ static void sdhci_reset_tuning(struct sdhci_host *host) sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); } -static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode, - unsigned long flags) +static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode) { sdhci_reset_tuning(host); @@ -2005,9 +2021,7 @@ static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode, sdhci_end_tuning(host); - spin_unlock_irqrestore(&host->lock, flags); mmc_abort_tuning(host->mmc, opcode); - spin_lock_irqsave(&host->lock, flags); } /* @@ -2017,12 +2031,14 @@ static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode, * interrupt setup is different to other commands and there is no timeout * interrupt so special handling is needed. */ -static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode, - unsigned long flags) +static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) { struct mmc_host *mmc = host->mmc; struct mmc_command cmd = {}; struct mmc_request mrq = {}; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); cmd.opcode = opcode; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -2056,17 +2072,16 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode, host->tuning_done = 0; + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); /* Wait for Buffer Read Ready interrupt */ wait_event_timeout(host->buf_ready_int, (host->tuning_done == 1), msecs_to_jiffies(50)); - spin_lock_irqsave(&host->lock, flags); } -static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode, - unsigned long flags) +static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) { int i; @@ -2077,12 +2092,12 @@ static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode, for (i = 0; i < MAX_TUNING_LOOP; i++) { u16 ctrl; - sdhci_send_tuning(host, opcode, flags); + sdhci_send_tuning(host, opcode); if (!host->tuning_done) { pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", mmc_hostname(host->mmc)); - sdhci_abort_tuning(host, opcode, flags); + sdhci_abort_tuning(host, opcode); return; } @@ -2093,9 +2108,9 @@ static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode, break; } - /* eMMC spec does not require a delay between tuning cycles */ - if (opcode == MMC_SEND_TUNING_BLOCK) - mdelay(1); + /* Spec does not require a delay between tuning cycles */ + if (host->tuning_delay > 0) + mdelay(host->tuning_delay); } pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", @@ -2107,12 +2122,9 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); int err = 0; - unsigned long flags; unsigned int tuning_count = 0; bool hs400_tuning; - spin_lock_irqsave(&host->lock, flags); - hs400_tuning = host->flags & SDHCI_HS400_TUNING; if (host->tuning_mode == SDHCI_TUNING_MODE_1) @@ -2129,7 +2141,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) /* HS400 tuning is done in HS200 mode */ case MMC_TIMING_MMC_HS400: err = -EINVAL; - goto out_unlock; + goto out; case MMC_TIMING_MMC_HS200: /* @@ -2150,44 +2162,31 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) /* FALLTHROUGH */ default: - goto out_unlock; + goto out; } if (host->ops->platform_execute_tuning) { - spin_unlock_irqrestore(&host->lock, flags); err = host->ops->platform_execute_tuning(host, opcode); - spin_lock_irqsave(&host->lock, flags); - goto out_unlock; + goto out; } host->mmc->retune_period = tuning_count; + if (host->tuning_delay < 0) + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK; + sdhci_start_tuning(host); - __sdhci_execute_tuning(host, opcode, flags); + __sdhci_execute_tuning(host, opcode); sdhci_end_tuning(host); -out_unlock: +out: host->flags &= ~SDHCI_HS400_TUNING; - spin_unlock_irqrestore(&host->lock, flags); return err; } EXPORT_SYMBOL_GPL(sdhci_execute_tuning); -static int sdhci_select_drive_strength(struct mmc_card *card, - unsigned int max_dtr, int host_drv, - int card_drv, int *drv_type) -{ - struct sdhci_host *host = mmc_priv(card->host); - - if (!host->ops->select_drive_strength) - return 0; - - return host->ops->select_drive_strength(host, card, max_dtr, host_drv, - card_drv, drv_type); -} - static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) { /* Host Controller v3.00 defines preset value registers */ @@ -2225,8 +2224,7 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, if (data->host_cookie != COOKIE_UNMAPPED) dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - data->flags & MMC_DATA_WRITE ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); + mmc_get_dma_dir(data)); data->host_cookie = COOKIE_UNMAPPED; } @@ -2301,7 +2299,6 @@ 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, - .select_drive_strength = sdhci_select_drive_strength, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, }; @@ -2343,8 +2340,7 @@ static bool sdhci_request_done(struct sdhci_host *host) if (data && data->host_cookie == COOKIE_MAPPED) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - (data->flags & MMC_DATA_READ) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + mmc_get_dma_dir(data)); data->host_cookie = COOKIE_UNMAPPED; } } @@ -2509,7 +2505,6 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) #ifdef CONFIG_MMC_DEBUG static void sdhci_adma_show_error(struct sdhci_host *host) { - const char *name = mmc_hostname(host->mmc); void *desc = host->adma_table; sdhci_dumpregs(host); @@ -2518,14 +2513,14 @@ static void sdhci_adma_show_error(struct sdhci_host *host) struct sdhci_adma2_64_desc *dma_desc = desc; if (host->flags & SDHCI_USE_64_BIT_DMA) - DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", - name, desc, le32_to_cpu(dma_desc->addr_hi), + DBG("%p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", + desc, le32_to_cpu(dma_desc->addr_hi), le32_to_cpu(dma_desc->addr_lo), le16_to_cpu(dma_desc->len), le16_to_cpu(dma_desc->cmd)); else - DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", - name, desc, le32_to_cpu(dma_desc->addr_lo), + DBG("%p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", + desc, le32_to_cpu(dma_desc->addr_lo), le16_to_cpu(dma_desc->len), le16_to_cpu(dma_desc->cmd)); @@ -2641,10 +2636,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + SDHCI_DEFAULT_BOUNDARY_SIZE; host->data->bytes_xfered = dmanow - dmastart; - DBG("%s: DMA base 0x%08x, transferred 0x%06x bytes," - " next 0x%08x\n", - mmc_hostname(host->mmc), dmastart, - host->data->bytes_xfered, dmanow); + DBG("DMA base 0x%08x, transferred 0x%06x bytes, next 0x%08x\n", + dmastart, host->data->bytes_xfered, dmanow); sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); } @@ -2684,14 +2677,19 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) } do { + DBG("IRQ status 0x%08x\n", intmask); + + if (host->ops->irq) { + intmask = host->ops->irq(host, intmask); + if (!intmask) + goto cont; + } + /* Clear selected interrupts. */ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | SDHCI_INT_BUS_POWER); sdhci_writel(host, mask, SDHCI_INT_STATUS); - DBG("*** %s got interrupt: 0x%08x\n", - mmc_hostname(host->mmc), intmask); - if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT; @@ -2751,7 +2749,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) unexpected |= intmask; sdhci_writel(host, intmask, SDHCI_INT_STATUS); } - +cont: if (result == IRQ_NONE) result = IRQ_HANDLED; @@ -2850,8 +2848,6 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_disable_card_detection(host); mmc_retune_timer_stop(host->mmc); - if (host->tuning_mode != SDHCI_TUNING_MODE_3) - mmc_retune_needed(host->mmc); if (!device_may_wakeup(mmc_dev(host->mmc))) { host->ier = 0; @@ -2912,8 +2908,6 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) unsigned long flags; mmc_retune_timer_stop(host->mmc); - if (host->tuning_mode != SDHCI_TUNING_MODE_3) - mmc_retune_needed(host->mmc); spin_lock_irqsave(&host->lock, flags); host->ier &= SDHCI_INT_CARD_INT; @@ -2984,6 +2978,119 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host); /*****************************************************************************\ * * + * Command Queue Engine (CQE) helpers * + * * +\*****************************************************************************/ + +void sdhci_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + u8 ctrl; + + spin_lock_irqsave(&host->lock, flags); + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (host->flags & SDHCI_USE_64_BIT_DMA) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + + sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 512), + SDHCI_BLOCK_SIZE); + + /* Set maximum timeout */ + sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL); + + host->ier = host->cqe_ier; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + + host->cqe_on = true; + + pr_debug("%s: sdhci: CQE on, IRQ mask %#x, IRQ status %#x\n", + mmc_hostname(mmc), host->ier, + sdhci_readl(host, SDHCI_INT_STATUS)); + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +} +EXPORT_SYMBOL_GPL(sdhci_cqe_enable); + +void sdhci_cqe_disable(struct mmc_host *mmc, bool recovery) +{ + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + sdhci_set_default_irqs(host); + + host->cqe_on = false; + + if (recovery) { + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); + } + + pr_debug("%s: sdhci: CQE off, IRQ mask %#x, IRQ status %#x\n", + mmc_hostname(mmc), host->ier, + sdhci_readl(host, SDHCI_INT_STATUS)); + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +} +EXPORT_SYMBOL_GPL(sdhci_cqe_disable); + +bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error, + int *data_error) +{ + u32 mask; + + if (!host->cqe_on) + return false; + + if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC)) + *cmd_error = -EILSEQ; + else if (intmask & SDHCI_INT_TIMEOUT) + *cmd_error = -ETIMEDOUT; + else + *cmd_error = 0; + + if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) + *data_error = -EILSEQ; + else if (intmask & SDHCI_INT_DATA_TIMEOUT) + *data_error = -ETIMEDOUT; + else if (intmask & SDHCI_INT_ADMA_ERROR) + *data_error = -EIO; + else + *data_error = 0; + + /* Clear selected interrupts. */ + mask = intmask & host->cqe_ier; + sdhci_writel(host, mask, SDHCI_INT_STATUS); + + if (intmask & SDHCI_INT_BUS_POWER) + pr_err("%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); + + intmask &= ~(host->cqe_ier | SDHCI_INT_ERROR); + if (intmask) { + sdhci_writel(host, intmask, SDHCI_INT_STATUS); + pr_err("%s: CQE: Unexpected interrupt 0x%08x.\n", + mmc_hostname(host->mmc), intmask); + sdhci_dumpregs(host); + } + + return true; +} +EXPORT_SYMBOL_GPL(sdhci_cqe_irq); + +/*****************************************************************************\ + * * * Device allocation/registration * * * \*****************************************************************************/ @@ -3007,6 +3114,11 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, host->flags = SDHCI_SIGNALING_330; + host->cqe_ier = SDHCI_CQE_INT_MASK; + host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK; + + host->tuning_delay = -1; + return host; } @@ -3289,20 +3401,22 @@ int sdhci_setup_host(struct sdhci_host *host) if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; + + if (host->caps & SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; + if (host->timeout_clk == 0) { - if (host->ops->get_timeout_clock) { - host->timeout_clk = - host->ops->get_timeout_clock(host); - } else { + if (!host->ops->get_timeout_clock) { pr_err("%s: Hardware doesn't specify timeout clock frequency.\n", mmc_hostname(mmc)); ret = -ENODEV; goto undma; } - } - if (host->caps & SDHCI_TIMEOUT_CLK_UNIT) - host->timeout_clk *= 1000; + host->timeout_clk = + DIV_ROUND_UP(host->ops->get_timeout_clock(host), + 1000); + } if (override_timeout_clk) host->timeout_clk = override_timeout_clk; @@ -3324,9 +3438,9 @@ int sdhci_setup_host(struct sdhci_host *host) !(host->flags & SDHCI_USE_SDMA)) && !(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) { host->flags |= SDHCI_AUTO_CMD23; - DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc)); + DBG("Auto-CMD23 available\n"); } else { - DBG("%s: Auto-CMD23 unavailable\n", mmc_hostname(mmc)); + DBG("Auto-CMD23 unavailable\n"); } /* @@ -3590,6 +3704,22 @@ undma: } EXPORT_SYMBOL_GPL(sdhci_setup_host); +void sdhci_cleanup_host(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + + if (!IS_ERR(mmc->supply.vqmmc)) + regulator_disable(mmc->supply.vqmmc); + + if (host->align_buffer) + dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz + + host->adma_table_sz, host->align_buffer, + host->align_addr); + host->adma_table = NULL; + host->align_buffer = NULL; +} +EXPORT_SYMBOL_GPL(sdhci_cleanup_host); + int __sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; @@ -3654,16 +3784,6 @@ unirq: untasklet: tasklet_kill(&host->finish_tasklet); - if (!IS_ERR(mmc->supply.vqmmc)) - regulator_disable(mmc->supply.vqmmc); - - if (host->align_buffer) - dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz + - host->adma_table_sz, host->align_buffer, - host->align_addr); - host->adma_table = NULL; - host->align_buffer = NULL; - return ret; } EXPORT_SYMBOL_GPL(__sdhci_add_host); @@ -3676,7 +3796,16 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) return ret; - return __sdhci_add_host(host); + ret = __sdhci_add_host(host); + if (ret) + goto cleanup; + + return 0; + +cleanup: + sdhci_cleanup_host(host); + + return ret; } EXPORT_SYMBOL_GPL(sdhci_add_host); |