diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/block.c | 7 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 8 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/slot-gpio.c | 42 | ||||
-rw-r--r-- | drivers/mmc/host/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mmc/host/cqhci-core.c | 4 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 3 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 4 | ||||
-rw-r--r-- | drivers/mmc/host/moxart-mmc.c | 16 | ||||
-rw-r--r-- | drivers/mmc/host/mtk-sd.c | 99 | ||||
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 12 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-of-arasan.c | 29 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-omap.c | 61 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pci-o2micro.c | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-sprd.c | 13 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 23 |
17 files changed, 267 insertions, 66 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 431af5e8be2f..61590cf7a7b1 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2442,9 +2442,14 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, /* used in ->open, must be set before add_disk: */ if (area_type == MMC_BLK_DATA_AREA_MAIN) dev_set_drvdata(&card->dev, md); - device_add_disk(md->parent, md->disk, mmc_disk_attr_groups); + ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups); + if (ret) + goto err_cleanup_queue; return md; + err_cleanup_queue: + blk_cleanup_queue(md->disk->queue); + blk_mq_free_tag_set(&md->queue.tag_set); err_kfree: kfree(md); out: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 29e58ffae379..b1c1716dacf0 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1224,6 +1224,14 @@ static int mmc_select_hs400(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_HS400); mmc_set_bus_speed(card); + if (host->ops->execute_hs400_tuning) { + mmc_retune_disable(host); + err = host->ops->execute_hs400_tuning(host, card); + mmc_retune_enable(host); + if (err) + goto out_err; + } + if (host->ops->hs400_complete) host->ops->hs400_complete(host); diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index ae25ffc2e870..e5e94567a9a9 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -38,7 +38,6 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_can_ext_csd(struct mmc_card *card); -int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal); bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd, unsigned int timeout_ms); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 05e907451df9..dd2a4b6ab6ad 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -39,24 +39,24 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) int mmc_gpio_alloc(struct mmc_host *host) { - struct mmc_gpio *ctx = devm_kzalloc(host->parent, - sizeof(*ctx), GFP_KERNEL); - - if (ctx) { - ctx->cd_debounce_delay_ms = 200; - ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, - "%s cd", dev_name(host->parent)); - if (!ctx->cd_label) - return -ENOMEM; - ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, - "%s ro", dev_name(host->parent)); - if (!ctx->ro_label) - return -ENOMEM; - host->slot.handler_priv = ctx; - host->slot.cd_irq = -EINVAL; - } + const char *devname = dev_name(host->parent); + struct mmc_gpio *ctx; + + ctx = devm_kzalloc(host->parent, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->cd_debounce_delay_ms = 200; + ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s cd", devname); + if (!ctx->cd_label) + return -ENOMEM; + ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname); + if (!ctx->ro_label) + return -ENOMEM; + host->slot.handler_priv = ctx; + host->slot.cd_irq = -EINVAL; - return ctx ? 0 : -ENOMEM; + return 0; } int mmc_gpio_get_ro(struct mmc_host *host) @@ -178,6 +178,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, if (IS_ERR(desc)) return PTR_ERR(desc); + /* Update default label if no con_id provided */ + if (!con_id) + gpiod_set_consumer_name(desc, ctx->cd_label); + if (debounce) { ret = gpiod_set_debounce(desc, debounce); if (ret < 0) @@ -226,6 +230,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, if (IS_ERR(desc)) return PTR_ERR(desc); + /* Update default label if no con_id provided */ + if (!con_id) + gpiod_set_consumer_name(desc, ctx->ro_label); + if (debounce) { ret = gpiod_set_debounce(desc, debounce); if (ret < 0) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 95b3511b0560..ed1bab8f3a8f 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -315,15 +315,17 @@ config MMC_SDHCI_TEGRA If unsure, say N. config MMC_SDHCI_S3C - tristate "SDHCI support on Samsung S3C SoC" + tristate "SDHCI support on Samsung S3C/S5P/Exynos SoC" depends on MMC_SDHCI depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST help This selects the Secure Digital Host Controller Interface (SDHCI) often referrered to as the HSMMC block in some of the Samsung S3C - range of SoC. + (S3C2416, S3C2443, S3C6410), S5Pv210 and Exynos (Exynso4210, + Exynos4412) SoCs. - If you have a controller with this interface, say Y or M here. + If you have a controller with this interface (thereforeyou build for + such Samsung SoC), say Y or M here. If unsure, say N. diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index 38559a956330..ca8329d55f43 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -899,8 +899,8 @@ static bool cqhci_timeout(struct mmc_host *mmc, struct mmc_request *mrq, spin_unlock_irqrestore(&cq_host->lock, flags); if (timed_out) { - pr_err("%s: cqhci: timeout for tag %d\n", - mmc_hostname(mmc), tag); + pr_err("%s: cqhci: timeout for tag %d, qcnt %d\n", + mmc_hostname(mmc), tag, cq_host->qcnt); cqhci_dumpregs(cq_host); } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 380f9aa56eb2..1e8f1bb3cad7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2086,7 +2086,8 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t) * delayed. Allowing the transfer to take place * avoids races and keeps things simple. */ - if (err != -ETIMEDOUT) { + if (err != -ETIMEDOUT && + host->dir_status == DW_MCI_RECV_STATUS) { state = STATE_SENDING_DATA; continue; } diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 3765e2f4ad98..c9cacd4d5b22 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1394,6 +1394,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } else if (host->variant->busy_timeout && busy_resp && status & MCI_DATATIMEOUT) { cmd->error = -ETIMEDOUT; + /* + * This will wake up mmci_irq_thread() which will issue + * a hardware reset of the MMCI block. + */ host->irq_action = IRQ_WAKE_THREAD; } else { cmd->resp[0] = readl(base + MMCIRESPONSE0); diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 6c9d38132f74..7b9fcef490de 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -621,6 +621,14 @@ static int moxart_probe(struct platform_device *pdev) ret = -EPROBE_DEFER; goto out; } + if (!IS_ERR(host->dma_chan_tx)) { + dma_release_channel(host->dma_chan_tx); + host->dma_chan_tx = NULL; + } + if (!IS_ERR(host->dma_chan_rx)) { + dma_release_channel(host->dma_chan_rx); + host->dma_chan_rx = NULL; + } dev_dbg(dev, "PIO mode transfer enabled\n"); host->have_dma = false; } else { @@ -675,6 +683,10 @@ static int moxart_probe(struct platform_device *pdev) return 0; out: + if (!IS_ERR_OR_NULL(host->dma_chan_tx)) + dma_release_channel(host->dma_chan_tx); + if (!IS_ERR_OR_NULL(host->dma_chan_rx)) + dma_release_channel(host->dma_chan_rx); if (mmc) mmc_free_host(mmc); return ret; @@ -687,9 +699,9 @@ static int moxart_remove(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, NULL); - if (!IS_ERR(host->dma_chan_tx)) + if (!IS_ERR_OR_NULL(host->dma_chan_tx)) dma_release_channel(host->dma_chan_tx); - if (!IS_ERR(host->dma_chan_rx)) + if (!IS_ERR_OR_NULL(host->dma_chan_rx)) dma_release_channel(host->dma_chan_rx); mmc_remove_host(mmc); mmc_free_host(mmc); diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 4dfc246c5f95..b124cfee05a1 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/iopoll.h> #include <linux/ioport.h> #include <linux/irq.h> #include <linux/of_address.h> @@ -258,6 +259,7 @@ #define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* RW */ #define MSDC_PAD_TUNE_CMD_SEL (0x1 << 21) /* RW */ +#define PAD_DS_TUNE_DLY_SEL (0x1 << 0) /* RW */ #define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */ #define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */ #define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */ @@ -301,6 +303,11 @@ #define PAD_CMD_RD_RXDLY_SEL (0x1 << 11) /* RW */ #define PAD_CMD_TX_DLY (0x1f << 12) /* RW */ +/* EMMC50_PAD_DS_TUNE mask */ +#define PAD_DS_DLY_SEL (0x1 << 16) /* RW */ +#define PAD_DS_DLY1 (0x1f << 10) /* RW */ +#define PAD_DS_DLY3 (0x1f << 0) /* RW */ + #define REQ_CMD_EIO (0x1 << 0) #define REQ_CMD_TMO (0x1 << 1) #define REQ_DAT_ERR (0x1 << 2) @@ -448,11 +455,13 @@ struct msdc_host { bool vqmmc_enabled; u32 latch_ck; u32 hs400_ds_delay; + u32 hs400_ds_dly3; u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */ u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */ bool hs400_cmd_resp_sel_rising; /* cmd response sample selection for HS400 */ bool hs400_mode; /* current eMMC will run at hs400 mode */ + bool hs400_tuning; /* hs400 mode online tuning */ bool internal_cd; /* Use internal card-detect logic */ bool cqhci; /* support eMMC hw cmdq */ struct msdc_save_para save_para; /* used when gate HCLK */ @@ -961,7 +970,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) } static inline u32 msdc_cmd_find_resp(struct msdc_host *host, - struct mmc_request *mrq, struct mmc_command *cmd) + struct mmc_command *cmd) { u32 resp; @@ -997,7 +1006,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode */ u32 opcode = cmd->opcode; - u32 resp = msdc_cmd_find_resp(host, mrq, cmd); + u32 resp = msdc_cmd_find_resp(host, cmd); u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7); host->cmd_rsp = resp; @@ -1043,8 +1052,8 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, return rawcmd; } -static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, - struct mmc_command *cmd, struct mmc_data *data) +static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd, + struct mmc_data *data) { bool read; @@ -1112,8 +1121,7 @@ static void msdc_recheck_sdio_irq(struct msdc_host *host) } } -static void msdc_track_cmd_data(struct msdc_host *host, - struct mmc_command *cmd, struct mmc_data *data) +static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd) { if (host->error) dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n", @@ -1134,7 +1142,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); - msdc_track_cmd_data(host, mrq->cmd, mrq->data); + msdc_track_cmd_data(host, mrq->cmd); if (mrq->data) msdc_unprepare_data(host, mrq->data); if (host->error) @@ -1190,7 +1198,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, if (!sbc_error && !(events & MSDC_INT_CMDRDY)) { if (events & MSDC_INT_CMDTMO || (cmd->opcode != MMC_SEND_TUNING_BLOCK && - cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)) + cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 && + !host->hs400_tuning)) /* * should not clear fifo/interrupt as the tune data * may have alreay come when cmd19/cmd21 gets response @@ -1287,7 +1296,8 @@ static void msdc_cmd_next(struct msdc_host *host, if ((cmd->error && !(cmd->error == -EILSEQ && (cmd->opcode == MMC_SEND_TUNING_BLOCK || - cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) || + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200 || + host->hs400_tuning))) || (mrq->sbc && mrq->sbc->error)) msdc_request_done(host, mrq); else if (cmd == mrq->sbc) @@ -1295,7 +1305,7 @@ static void msdc_cmd_next(struct msdc_host *host, else if (!cmd->data) msdc_request_done(host, mrq); else - msdc_start_data(host, mrq, cmd, cmd->data); + msdc_start_data(host, cmd, cmd->data); } static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -2251,6 +2261,67 @@ static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) return 0; } +static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card) +{ + struct msdc_host *host = mmc_priv(mmc); + struct msdc_delay_phase dly1_delay; + u32 val, result_dly1 = 0; + u8 *ext_csd; + int i, ret; + + if (host->top_base) { + sdr_set_bits(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY_SEL); + if (host->hs400_ds_dly3) + sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY3, host->hs400_ds_dly3); + } else { + sdr_set_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY_SEL); + if (host->hs400_ds_dly3) + sdr_set_field(host->base + PAD_DS_TUNE, + PAD_DS_TUNE_DLY3, host->hs400_ds_dly3); + } + + host->hs400_tuning = true; + for (i = 0; i < PAD_DELAY_MAX; i++) { + if (host->top_base) + sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY1, i); + else + sdr_set_field(host->base + PAD_DS_TUNE, + PAD_DS_TUNE_DLY1, i); + ret = mmc_get_ext_csd(card, &ext_csd); + if (!ret) + result_dly1 |= (1 << i); + } + host->hs400_tuning = false; + + dly1_delay = get_best_delay(host, result_dly1); + if (dly1_delay.maxlen == 0) { + dev_err(host->dev, "Failed to get DLY1 delay!\n"); + goto fail; + } + if (host->top_base) + sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY1, dly1_delay.final_phase); + else + sdr_set_field(host->base + PAD_DS_TUNE, + PAD_DS_TUNE_DLY1, dly1_delay.final_phase); + + if (host->top_base) + val = readl(host->top_base + EMMC50_PAD_DS_TUNE); + else + val = readl(host->base + PAD_DS_TUNE); + + dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val); + + return 0; + +fail: + dev_err(host->dev, "Failed to tuning DS pin delay!\n"); + return -EIO; +} + static void msdc_hw_reset(struct mmc_host *mmc) { struct msdc_host *host = mmc_priv(mmc); @@ -2330,6 +2401,7 @@ static void msdc_cqe_enable(struct mmc_host *mmc) static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery) { struct msdc_host *host = mmc_priv(mmc); + unsigned int val = 0; /* disable cmdq irq */ sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INT_CMDQ); @@ -2339,6 +2411,9 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery) if (recovery) { sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1); + if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CFG, val, + !(val & MSDC_DMA_CFG_STS), 1, 3000))) + return; msdc_reset_hw(host); } } @@ -2377,6 +2452,7 @@ static const struct mmc_host_ops mt_msdc_ops = { .card_busy = msdc_card_busy, .execute_tuning = msdc_execute_tuning, .prepare_hs400_tuning = msdc_prepare_hs400_tuning, + .execute_hs400_tuning = msdc_execute_hs400_tuning, .hw_reset = msdc_hw_reset, }; @@ -2396,6 +2472,9 @@ static void msdc_of_property_parse(struct platform_device *pdev, of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay", &host->hs400_ds_delay); + of_property_read_u32(pdev->dev.of_node, "mediatek,hs400-ds-dly3", + &host->hs400_ds_dly3); + of_property_read_u32(pdev->dev.of_node, "mediatek,hs200-cmd-int-delay", &host->hs200_cmd_int_delay); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 2f8038d69f67..9dafcbf969d9 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -702,11 +702,6 @@ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) #else -static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) -{ - return 0; -} - static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) { } @@ -1515,7 +1510,7 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) * REVISIT: should be moved to sdio core and made more * general e.g. by expanding the DT bindings of child nodes * to provide a mechanism to provide this information: - * Documentation/devicetree/bindings/mmc/mmc-card.txt + * Documentation/devicetree/bindings/mmc/mmc-card.yaml */ np = of_get_compatible_child(np, "ti,wl1251"); @@ -2086,6 +2081,7 @@ static int omap_hsmmc_resume(struct device *dev) } #endif +#ifdef CONFIG_PM static int omap_hsmmc_runtime_suspend(struct device *dev) { struct omap_hsmmc_host *host; @@ -2153,11 +2149,11 @@ static int omap_hsmmc_runtime_resume(struct device *dev) spin_unlock_irqrestore(&host->irq_lock, flags); return 0; } +#endif static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume) - .runtime_suspend = omap_hsmmc_runtime_suspend, - .runtime_resume = omap_hsmmc_runtime_resume, + SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL) }; static struct platform_driver omap_hsmmc_driver = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 737e2bfdedc2..6a2e5a468424 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -191,6 +191,13 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = { .hiword_update = false, }; +static const struct sdhci_arasan_soc_ctl_map thunderbay_soc_ctl_map = { + .baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 }, + .clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 }, + .support64b = { .reg = 0x4, .width = 1, .shift = 24 }, + .hiword_update = false, +}; + static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = { .baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 }, .clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 }, @@ -456,6 +463,15 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; +static const struct sdhci_pltfm_data sdhci_arasan_thunderbay_pdata = { + .ops = &sdhci_arasan_cqe_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC | + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, +}; + #ifdef CONFIG_PM_SLEEP /** * sdhci_arasan_suspend - Suspend method for the driver @@ -1132,6 +1148,12 @@ static struct sdhci_arasan_of_data sdhci_arasan_generic_data = { .clk_ops = &arasan_clk_ops, }; +static const struct sdhci_arasan_of_data sdhci_arasan_thunderbay_data = { + .soc_ctl_map = &thunderbay_soc_ctl_map, + .pdata = &sdhci_arasan_thunderbay_pdata, + .clk_ops = &arasan_clk_ops, +}; + static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = { .ops = &sdhci_arasan_cqe_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | @@ -1265,6 +1287,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "intel,keembay-sdhci-5.1-sdio", .data = &intel_keembay_sdio_data, }, + { + .compatible = "intel,thunderbay-sdhci-5.1", + .data = &sdhci_arasan_thunderbay_data, + }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", @@ -1626,7 +1652,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") || of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") || - of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) { + of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio") || + of_device_is_compatible(np, "intel,thunderbay-sdhci-5.1")) { sdhci_arasan_update_clockmultiplier(host, 0x0); sdhci_arasan_update_support64b(host, 0x0); diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 8f4d1f003f65..9422c9819b77 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -21,6 +21,8 @@ #include "sdhci-pltfm.h" +#define SDHCI_OMAP_SYSCONFIG 0x110 + #define SDHCI_OMAP_CON 0x12c #define CON_DW8 BIT(5) #define CON_DMA_MASTER BIT(20) @@ -62,6 +64,8 @@ #define SDHCI_OMAP_IE 0x234 #define INT_CC_EN BIT(0) +#define SDHCI_OMAP_ISE 0x238 + #define SDHCI_OMAP_AC12 0x23c #define AC12_V1V8_SIGEN BIT(19) #define AC12_SCLK_SEL BIT(23) @@ -113,6 +117,8 @@ struct sdhci_omap_host { u32 hctl; u32 sysctl; u32 capa; + u32 ie; + u32 ise; }; static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host); @@ -682,7 +688,24 @@ static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode, { struct mmc_host *mmc = host->mmc; - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); +} + +/* + * MMCHS_HL_HWINFO has the MADMA_EN bit set if the controller instance + * is connected to L3 interconnect and is bus master capable. Note that + * the MMCHS_HL_HWINFO register is in the module registers before the + * omap registers and sdhci registers. The offset can vary for omap + * registers depending on the SoC. Do not use sdhci_omap_readl() here. + */ +static bool sdhci_omap_has_adma(struct sdhci_omap_host *omap_host, int offset) +{ + /* MMCHS_HL_HWINFO register is only available on omap4 and later */ + if (offset < 0x200) + return false; + + return readl(omap_host->base + 4) & 1; } static int sdhci_omap_enable_dma(struct sdhci_host *host) @@ -792,6 +815,11 @@ static void sdhci_omap_reset(struct sdhci_host *host, u8 mask) struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); unsigned long limit = MMC_TIMEOUT_US; unsigned long i = 0; + u32 sysc; + + /* Save target module sysconfig configured by SoC PM layer */ + if (mask & SDHCI_RESET_ALL) + sysc = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCONFIG); /* Don't reset data lines during tuning operation */ if (omap_host->is_tuning) @@ -811,10 +839,15 @@ static void sdhci_omap_reset(struct sdhci_host *host, u8 mask) dev_err(mmc_dev(host->mmc), "Timeout waiting on controller reset in %s\n", __func__); - return; + + goto restore_sysc; } sdhci_reset(host, mask); + +restore_sysc: + if (mask & SDHCI_RESET_ALL) + sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCONFIG, sysc); } #define CMD_ERR_MASK (SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX |\ @@ -1192,10 +1225,19 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning; host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq; - /* Switch to external DMA only if there is the "dmas" property */ - if (of_find_property(dev->of_node, "dmas", NULL)) + /* + * Switch to external DMA only if there is the "dmas" property and + * ADMA is not available on the controller instance. + */ + if (device_property_present(dev, "dmas") && + !sdhci_omap_has_adma(omap_host, offset)) sdhci_switch_external_dma(host, true); + if (device_property_read_bool(dev, "ti,non-removable")) { + dev_warn_once(dev, "using old ti,non-removable property\n"); + mmc->caps |= MMC_CAP_NONREMOVABLE; + } + /* R1B responses is required to properly manage HW busy detection. */ mmc->caps |= MMC_CAP_NEED_RSP_BUSY; @@ -1244,14 +1286,23 @@ static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host) { omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL); + omap_host->sysctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL); omap_host->capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); + omap_host->ie = sdhci_omap_readl(omap_host, SDHCI_OMAP_IE); + omap_host->ise = sdhci_omap_readl(omap_host, SDHCI_OMAP_ISE); } +/* Order matters here, HCTL must be restored in two phases */ static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host) { - sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con); sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl); sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa); + sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl); + + sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, omap_host->sysctl); + sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con); + sdhci_omap_writel(omap_host, SDHCI_OMAP_IE, omap_host->ie); + sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise); } static int __maybe_unused sdhci_omap_suspend(struct device *dev) diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 51d55a87aebe..f045c1ee4667 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -489,7 +489,7 @@ static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip, ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI); if (!ret) { - pr_info("%s: unsupport msi, use INTx irq\n", + pr_info("%s: unsupported MSI, use INTx irq\n", mmc_hostname(host->mmc)); return; } diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 862f033d235d..9085f3932443 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -791,4 +791,3 @@ module_platform_driver(sdhci_s3c_driver); MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue"); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:s3c-sdhci"); diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index 11e375579cfb..f33e9349e4e6 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -8,6 +8,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/highmem.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -39,6 +40,9 @@ #define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21) #define SDHCI_SPRD_BIT_NEGRD_DLY_INV BIT(29) +#define SDHCI_SPRD_REG_32_DLL_STS0 0x210 +#define SDHCI_SPRD_DLL_LOCKED BIT(18) + #define SDHCI_SPRD_REG_32_BUSY_POSI 0x250 #define SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN BIT(25) #define SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN BIT(24) @@ -256,6 +260,15 @@ static void sdhci_sprd_enable_phy_dll(struct sdhci_host *host) sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG); /* wait 1ms */ usleep_range(1000, 1250); + + if (read_poll_timeout(sdhci_readl, tmp, (tmp & SDHCI_SPRD_DLL_LOCKED), + 2000, USEC_PER_SEC, false, host, SDHCI_SPRD_REG_32_DLL_STS0)) { + pr_err("%s: DLL locked fail!\n", mmc_hostname(host->mmc)); + pr_info("%s: DLL_STS0 : 0x%x, DLL_CFG : 0x%x\n", + mmc_hostname(host->mmc), + sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_STS0), + sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG)); + } } static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2d80a04e11d8..dbe87995596c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -930,7 +930,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, struct mmc_data *data; unsigned target_timeout, current_timeout; - *too_big = true; + *too_big = false; /* * If the host controller provides us with an incorrect timeout @@ -941,7 +941,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) return host->max_timeout_count; - /* Unspecified command, asume max */ + /* Unspecified command, assume max */ if (cmd == NULL) return host->max_timeout_count; @@ -968,17 +968,14 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, while (current_timeout < target_timeout) { count++; current_timeout <<= 1; - if (count > host->max_timeout_count) + if (count > host->max_timeout_count) { + if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) + DBG("Too large timeout 0x%x requested for CMD%d!\n", + count, cmd->opcode); + count = host->max_timeout_count; + *too_big = true; break; - } - - if (count > host->max_timeout_count) { - if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) - DBG("Too large timeout 0x%x requested for CMD%d!\n", - count, cmd->opcode); - count = host->max_timeout_count; - } else { - *too_big = false; + } } return count; @@ -3238,7 +3235,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p) -ETIMEDOUT : -EILSEQ; - if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { + if (sdhci_auto_cmd23(host, mrq)) { mrq->sbc->error = err; __sdhci_finish_mrq(host, mrq); return; |