diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-02 02:55:12 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-02 02:55:12 +0100 |
commit | 8a73c77c809a7342497b78a2b4555aa40506af94 (patch) | |
tree | a3d562d0693198512678ee3f5b415f657fea9e39 /drivers/mmc | |
parent | Merge tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi (diff) | |
parent | Merge branch 'fixes' into next (diff) | |
download | linux-8a73c77c809a7342497b78a2b4555aa40506af94.tar.xz linux-8a73c77c809a7342497b78a2b4555aa40506af94.zip |
Merge tag 'mmc-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC and MEMSTICK updates from Ulf Hansson:
"MMC core:
- Update maintainer and URL for the mmc-utils
- Set default label for slot-gpio in case of no con-id
- Convert MMC card DT bindings to a schema
- Add optional host specific tuning support for eMMC HS400
- Add error handling of add_disk()
MMC host:
- mtk-sd: Add host specific tuning support for eMMC HS400
- mtk-sd: Make DMA handling more robust
- dw_mmc: Prevent hangs for some data writes
- dw_mmc: Move away from using the ->init_card() callback
- mxs-mmc: Manage the regulator in the error path and in ->remove()
- sdhci-cadence: Add support for the Microchip MPFS variant
- sdhci-esdhc-imx: Add support for the NXP S32G2 variant
- sdhci-of-arasan: Add support for the Intel Thunder Bay variant
- sdhci-omap: Prepare to support more SoCs
- sdhci-omap: Add support for omap3 and omap4 variants
- sdhci-omap: Add support for power management
- sdhci-omap: Add support for system wakeups
- sdhci-msm: Add support for the msm8226 variant
- sdhci-sprd: Verify that the DLL locks according to spec
MEMSTICK:
- Add error handling of add_disk()
- A couple of small fixes and improvements"
* tag 'mmc-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (60 commits)
docs: mmc: update maintainer name and URL
mmc: dw_mmc: exynos: Fix spelling mistake "candiates" -> candidates
MAINTAINERS: drop obsolete file pattern in SDHCI DRIVER section
mmc: sdhci-esdhc-imx: add NXP S32G2 support
dt-bindings: mmc: fsl-imx-esdhc: add NXP S32G2 support
mmc: dw_mmc: Drop use of ->init_card() callback
mmc: sdhci-omap: Fix build if CONFIG_PM_SLEEP is not set
mmc: sdhci-omap: Remove forward declaration of sdhci_omap_context_save()
memstick: r592: Fix a UAF bug when removing the driver
mmc: mxs-mmc: disable regulator on error and in the remove function
mmc: sdhci-omap: Configure optional wakeirq
mmc: sdhci-omap: Allow SDIO card power off and enable aggressive PM
mmc: sdhci-omap: Implement PM runtime functions
mmc: sdhci-omap: Add omap_offset to support omap3 and earlier
mmc: sdhci-omap: Handle voltages to add support omap4
dt-bindings: sdhci-omap: Update binding for legacy SoCs
mmc: sdhci-pci: Remove dead code (rst_n_gpio et al)
mmc: sdhci-pci: Remove dead code (cd_gpio, cd_irq et al)
mmc: sdhci-pci: Remove dead code (struct sdhci_pci_data et al)
mmc: sdhci: Remove unused prototype declaration in the header
...
Diffstat (limited to 'drivers/mmc')
26 files changed, 551 insertions, 341 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 74882fa0f86d..90e1bcd03b46 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 ccc148cdb5ee..5af8494c31b5 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/Makefile b/drivers/mmc/host/Makefile index 14004cc09aaa..ea36d379bd3c 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -14,7 +14,6 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \ sdhci-pci-dwc-mshc.o sdhci-pci-gli.o -obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index 31f841231609..b0d30c35c390 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -902,8 +902,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-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 1f8a3c0ddfe1..c2dd29ef45c6 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -442,14 +442,14 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) return sample; } -static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) +static s8 dw_mci_exynos_get_best_clksmpl(u8 candidates) { const u8 iter = 8; u8 __c; s8 i, loc = -1; for (i = 0; i < iter; i++) { - __c = ror8(candiates, i); + __c = ror8(candidates, i); if ((__c & 0xc7) == 0xc7) { loc = i; goto out; @@ -457,7 +457,7 @@ static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) } for (i = 0; i < iter; i++) { - __c = ror8(candiates, i); + __c = ror8(candidates, i); if ((__c & 0x83) == 0x83) { loc = i; goto out; @@ -466,11 +466,11 @@ static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) /* * If there is no cadiates value, then it needs to return -EIO. - * If there are candiates values and don't find bset clk sample value, - * then use a first candiates clock sample value. + * If there are candidates values and don't find bset clk sample value, + * then use a first candidates clock sample value. */ for (i = 0; i < iter; i++) { - __c = ror8(candiates, i); + __c = ror8(candidates, i); if ((__c & 0x1) == 0x1) { loc = i; goto out; @@ -485,7 +485,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) struct dw_mci *host = slot->host; struct dw_mci_exynos_priv_data *priv = host->priv; struct mmc_host *mmc = slot->mmc; - u8 start_smpl, smpl, candiates = 0; + u8 start_smpl, smpl, candidates = 0; s8 found; int ret = 0; @@ -496,18 +496,18 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) smpl = dw_mci_exynos_move_next_clksmpl(host); if (!mmc_send_tuning(mmc, opcode, NULL)) - candiates |= (1 << smpl); + candidates |= (1 << smpl); } while (start_smpl != smpl); - found = dw_mci_exynos_get_best_clksmpl(candiates); + found = dw_mci_exynos_get_best_clksmpl(candidates); if (found >= 0) { dw_mci_exynos_set_clksmpl(host, found); priv->tuned_sample = found; } else { ret = -EIO; dev_warn(&mmc->class_dev, - "There is no candiates value about clksmpl!\n"); + "There is no candidates value about clksmpl!\n"); } return ret; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 380f9aa56eb2..d977f34f6b55 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1611,37 +1611,32 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) usleep_range(200, 300); } -static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card) +static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) { - struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + u32 clk_en_a_old; + u32 clk_en_a; /* * Low power mode will stop the card clock when idle. According to the * description of the CLKENA register we should disable low power mode * for SDIO cards if we need SDIO interrupts to work. */ - if (mmc->caps & MMC_CAP_SDIO_IRQ) { - const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; - u32 clk_en_a_old; - u32 clk_en_a; - clk_en_a_old = mci_readl(host, CLKENA); - - if (card->type == MMC_TYPE_SDIO || - card->type == MMC_TYPE_SD_COMBO) { - set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); - clk_en_a = clk_en_a_old & ~clken_low_pwr; - } else { - clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); - clk_en_a = clk_en_a_old | clken_low_pwr; - } + clk_en_a_old = mci_readl(host, CLKENA); + if (prepare) { + set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clk_en_a = clk_en_a_old & ~clken_low_pwr; + } else { + clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clk_en_a = clk_en_a_old | clken_low_pwr; + } - if (clk_en_a != clk_en_a_old) { - mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | - SDMMC_CMD_PRV_DAT_WAIT, 0); - } + if (clk_en_a != clk_en_a_old) { + mci_writel(host, CLKENA, clk_en_a); + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, + 0); } } @@ -1669,6 +1664,7 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + dw_mci_prepare_sdio_irq(slot, enb); __dw_mci_enable_sdio_irq(slot, enb); /* Avoid runtime suspending the device when SDIO IRQ is enabled */ @@ -1790,7 +1786,6 @@ static const struct mmc_host_ops dw_mci_ops = { .execute_tuning = dw_mci_execute_tuning, .card_busy = dw_mci_card_busy, .start_signal_voltage_switch = dw_mci_switch_voltage, - .init_card = dw_mci_init_card, .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, }; @@ -2086,7 +2081,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..16d1c7a43d33 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -566,37 +566,37 @@ static int moxart_probe(struct platform_device *pdev) if (!mmc) { dev_err(dev, "mmc_alloc_host failed\n"); ret = -ENOMEM; - goto out; + goto out_mmc; } ret = of_address_to_resource(node, 0, &res_mmc); if (ret) { dev_err(dev, "of_address_to_resource failed\n"); - goto out; + goto out_mmc; } irq = irq_of_parse_and_map(node, 0); if (irq <= 0) { dev_err(dev, "irq_of_parse_and_map failed\n"); ret = -EINVAL; - goto out; + goto out_mmc; } clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - goto out; + goto out_mmc; } reg_mmc = devm_ioremap_resource(dev, &res_mmc); if (IS_ERR(reg_mmc)) { ret = PTR_ERR(reg_mmc); - goto out; + goto out_mmc; } ret = mmc_of_parse(mmc); if (ret) - goto out; + goto out_mmc; host = mmc_priv(mmc); host->mmc = mmc; @@ -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,11 @@ 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); +out_mmc: if (mmc) mmc_free_host(mmc); return ret; @@ -687,9 +700,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 b06b4dcb7c78..943940b44e83 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/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 947581de7860..8c3655d3be96 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -552,6 +552,11 @@ static const struct of_device_id mxs_mmc_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids); +static void mxs_mmc_regulator_disable(void *regulator) +{ + regulator_disable(regulator); +} + static int mxs_mmc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -591,6 +596,11 @@ static int mxs_mmc_probe(struct platform_device *pdev) "Failed to enable vmmc regulator: %d\n", ret); goto out_mmc_free; } + + ret = devm_add_action_or_reset(&pdev->dev, mxs_mmc_regulator_disable, + reg_vmmc); + if (ret) + goto out_mmc_free; } ssp->clk = devm_clk_get(&pdev->dev, NULL); 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-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 8fe65f172a61..f1ef0d28b0dd 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -362,23 +362,11 @@ static inline bool sdhci_acpi_no_fixup_child_power(struct acpi_device *adev) static int bxt_get_cd(struct mmc_host *mmc) { int gpio_cd = mmc_gpio_get_cd(mmc); - struct sdhci_host *host = mmc_priv(mmc); - unsigned long flags; - int ret = 0; if (!gpio_cd) return 0; - spin_lock_irqsave(&host->lock, flags); - - if (host->flags & SDHCI_DEVICE_DEAD) - goto out; - - ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); -out: - spin_unlock_irqrestore(&host->lock, flags); - - return ret; + return sdhci_get_cd_nogpio(mmc); } static int intel_probe_slot(struct platform_device *pdev, struct acpi_device *adev) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index e658f0174242..afaf33707d46 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -196,6 +196,9 @@ */ #define ESDHC_FLAG_BROKEN_AUTO_CMD23 BIT(16) +/* ERR004536 is not applicable for the IP */ +#define ESDHC_FLAG_SKIP_ERR004536 BIT(17) + enum wp_types { ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ ESDHC_WP_CONTROLLER, /* mmc controller internal WP */ @@ -289,6 +292,13 @@ static const struct esdhc_soc_data usdhc_imx7d_data = { | ESDHC_FLAG_BROKEN_AUTO_CMD23, }; +static struct esdhc_soc_data usdhc_s32g2_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES + | ESDHC_FLAG_SKIP_ERR004536, +}; + static struct esdhc_soc_data usdhc_imx7ulp_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 @@ -347,6 +357,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, }, { .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, }, { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, }, + { .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -1375,8 +1386,10 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL * TO1.1, it's harmless for MX6SL */ - writel(readl(host->ioaddr + 0x6c) & ~BIT(7), - host->ioaddr + 0x6c); + if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) { + writel(readl(host->ioaddr + 0x6c) & ~BIT(7), + host->ioaddr + 0x6c); + } /* disable DLL_CTRL delay line settings */ writel(0x0, host->ioaddr + ESDHC_DLL_CTRL); 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..64e27c2821f9 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -12,8 +12,10 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/pm_wakeirq.h> #include <linux/regulator/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/sys_soc.h> @@ -21,7 +23,14 @@ #include "sdhci-pltfm.h" -#define SDHCI_OMAP_CON 0x12c +/* + * Note that the register offsets used here are from omap_regs + * base which is 0x100 for omap4 and later, and 0 for omap3 and + * earlier. + */ +#define SDHCI_OMAP_SYSCONFIG 0x10 + +#define SDHCI_OMAP_CON 0x2c #define CON_DW8 BIT(5) #define CON_DMA_MASTER BIT(20) #define CON_DDR BIT(19) @@ -31,20 +40,20 @@ #define CON_INIT BIT(1) #define CON_OD BIT(0) -#define SDHCI_OMAP_DLL 0x0134 +#define SDHCI_OMAP_DLL 0x34 #define DLL_SWT BIT(20) #define DLL_FORCE_SR_C_SHIFT 13 #define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT) #define DLL_FORCE_VALUE BIT(12) #define DLL_CALIB BIT(1) -#define SDHCI_OMAP_CMD 0x20c +#define SDHCI_OMAP_CMD 0x10c -#define SDHCI_OMAP_PSTATE 0x0224 +#define SDHCI_OMAP_PSTATE 0x124 #define PSTATE_DLEV_DAT0 BIT(20) #define PSTATE_DATI BIT(1) -#define SDHCI_OMAP_HCTL 0x228 +#define SDHCI_OMAP_HCTL 0x128 #define HCTL_SDBP BIT(8) #define HCTL_SDVS_SHIFT 9 #define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT) @@ -52,26 +61,28 @@ #define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT) #define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT) -#define SDHCI_OMAP_SYSCTL 0x22c +#define SDHCI_OMAP_SYSCTL 0x12c #define SYSCTL_CEN BIT(2) #define SYSCTL_CLKD_SHIFT 6 #define SYSCTL_CLKD_MASK 0x3ff -#define SDHCI_OMAP_STAT 0x230 +#define SDHCI_OMAP_STAT 0x130 -#define SDHCI_OMAP_IE 0x234 +#define SDHCI_OMAP_IE 0x134 #define INT_CC_EN BIT(0) -#define SDHCI_OMAP_AC12 0x23c +#define SDHCI_OMAP_ISE 0x138 + +#define SDHCI_OMAP_AC12 0x13c #define AC12_V1V8_SIGEN BIT(19) #define AC12_SCLK_SEL BIT(23) -#define SDHCI_OMAP_CAPA 0x240 +#define SDHCI_OMAP_CAPA 0x140 #define CAPA_VS33 BIT(24) #define CAPA_VS30 BIT(25) #define CAPA_VS18 BIT(26) -#define SDHCI_OMAP_CAPA2 0x0244 +#define SDHCI_OMAP_CAPA2 0x144 #define CAPA2_TSDR50 BIT(13) #define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */ @@ -89,7 +100,8 @@ #define SDHCI_OMAP_SPECIAL_RESET BIT(1) struct sdhci_omap_data { - u32 offset; + int omap_offset; /* Offset for omap regs from base */ + u32 offset; /* Offset for SDHCI regs from base */ u8 flags; }; @@ -107,12 +119,19 @@ struct sdhci_omap_host { struct pinctrl *pinctrl; struct pinctrl_state **pinctrl_state; + int wakeirq; bool is_tuning; + + /* Offset for omap specific registers from base */ + int omap_offset; + /* Omap specific context save */ u32 con; u32 hctl; u32 sysctl; u32 capa; + u32 ie; + u32 ise; }; static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host); @@ -121,13 +140,13 @@ static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host); static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host, unsigned int offset) { - return readl(host->base + offset); + return readl(host->base + host->omap_offset + offset); } static inline void sdhci_omap_writel(struct sdhci_omap_host *host, unsigned int offset, u32 data) { - writel(data, host->base + offset); + writel(data, host->base + host->omap_offset + offset); } static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host, @@ -172,7 +191,7 @@ static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host, } static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host, - unsigned int iov) + unsigned int iov_pbias) { int ret; struct sdhci_host *host = omap_host->host; @@ -183,14 +202,15 @@ static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host, return ret; if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_set_voltage(mmc->supply.vqmmc, iov, iov); - if (ret) { + /* Pick the right voltage to allow 3.0V for 3.3V nominal PBIAS */ + ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios); + if (ret < 0) { dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n"); return ret; } } - ret = sdhci_omap_set_pbias(omap_host, true, iov); + ret = sdhci_omap_set_pbias(omap_host, true, iov_pbias); if (ret) return ret; @@ -200,16 +220,28 @@ static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host, static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host, unsigned char signal_voltage) { - u32 reg; + u32 reg, capa; ktime_t timeout; reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL); reg &= ~HCTL_SDVS_MASK; - if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) - reg |= HCTL_SDVS_33; - else + switch (signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); + if (capa & CAPA_VS33) + reg |= HCTL_SDVS_33; + else if (capa & CAPA_VS30) + reg |= HCTL_SDVS_30; + else + dev_warn(omap_host->dev, "misconfigured CAPA: %08x\n", + capa); + break; + case MMC_SIGNAL_VOLTAGE_180: + default: reg |= HCTL_SDVS_18; + break; + } sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg); @@ -527,28 +559,32 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc, if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); - if (!(reg & CAPA_VS33)) + if (!(reg & (CAPA_VS30 | CAPA_VS33))) return -EOPNOTSUPP; + if (reg & CAPA_VS30) + iov = IOV_3V0; + else + iov = IOV_3V3; + sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage); reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); reg &= ~AC12_V1V8_SIGEN; sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg); - iov = IOV_3V3; } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); if (!(reg & CAPA_VS18)) return -EOPNOTSUPP; + iov = IOV_1V8; + sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage); reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); reg |= AC12_V1V8_SIGEN; sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg); - - iov = IOV_1V8; } else { return -EOPNOTSUPP; } @@ -682,7 +718,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 +845,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 +869,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 |\ @@ -877,34 +940,73 @@ static struct sdhci_ops sdhci_omap_ops = { .set_timeout = sdhci_omap_set_timeout, }; -static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host) +static unsigned int sdhci_omap_regulator_get_caps(struct device *dev, + const char *name) { - u32 reg; - int ret = 0; + struct regulator *reg; + unsigned int caps = 0; + + reg = regulator_get(dev, name); + if (IS_ERR(reg)) + return ~0U; + + if (regulator_is_supported_voltage(reg, 1700000, 1950000)) + caps |= SDHCI_CAN_VDD_180; + if (regulator_is_supported_voltage(reg, 2700000, 3150000)) + caps |= SDHCI_CAN_VDD_300; + if (regulator_is_supported_voltage(reg, 3150000, 3600000)) + caps |= SDHCI_CAN_VDD_330; + + regulator_put(reg); + + return caps; +} + +static int sdhci_omap_set_capabilities(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); struct device *dev = omap_host->dev; - struct regulator *vqmmc; + const u32 mask = SDHCI_CAN_VDD_180 | SDHCI_CAN_VDD_300 | SDHCI_CAN_VDD_330; + unsigned int pbias, vqmmc, caps = 0; + u32 reg; - vqmmc = regulator_get(dev, "vqmmc"); - if (IS_ERR(vqmmc)) { - ret = PTR_ERR(vqmmc); - goto reg_put; - } + pbias = sdhci_omap_regulator_get_caps(dev, "pbias"); + vqmmc = sdhci_omap_regulator_get_caps(dev, "vqmmc"); + caps = pbias & vqmmc; + + if (pbias != ~0U && vqmmc == ~0U) + dev_warn(dev, "vqmmc regulator missing for pbias\n"); + else if (caps == ~0U) + return 0; + + /* + * Quirk handling to allow 3.0V vqmmc with a valid 3.3V PBIAS. This is + * needed for 3.0V ldo9_reg on omap5 at least. + */ + if (pbias != ~0U && (pbias & SDHCI_CAN_VDD_330) && + (vqmmc & SDHCI_CAN_VDD_300)) + caps |= SDHCI_CAN_VDD_330; /* voltage capabilities might be set by boot loader, clear it */ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33); - if (regulator_is_supported_voltage(vqmmc, IOV_3V3, IOV_3V3)) - reg |= CAPA_VS33; - if (regulator_is_supported_voltage(vqmmc, IOV_1V8, IOV_1V8)) + if (caps & SDHCI_CAN_VDD_180) reg |= CAPA_VS18; + if (caps & SDHCI_CAN_VDD_300) + reg |= CAPA_VS30; + + if (caps & SDHCI_CAN_VDD_330) + reg |= CAPA_VS33; + sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg); -reg_put: - regulator_put(vqmmc); + host->caps &= ~mask; + host->caps |= caps; - return ret; + return 0; } static const struct sdhci_pltfm_data sdhci_omap_pdata = { @@ -920,26 +1022,56 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = { .ops = &sdhci_omap_ops, }; +static const struct sdhci_omap_data omap2430_data = { + .omap_offset = 0, + .offset = 0x100, +}; + +static const struct sdhci_omap_data omap3_data = { + .omap_offset = 0, + .offset = 0x100, +}; + +static const struct sdhci_omap_data omap4_data = { + .omap_offset = 0x100, + .offset = 0x200, + .flags = SDHCI_OMAP_SPECIAL_RESET, +}; + +static const struct sdhci_omap_data omap5_data = { + .omap_offset = 0x100, + .offset = 0x200, + .flags = SDHCI_OMAP_SPECIAL_RESET, +}; + static const struct sdhci_omap_data k2g_data = { + .omap_offset = 0x100, .offset = 0x200, }; static const struct sdhci_omap_data am335_data = { + .omap_offset = 0x100, .offset = 0x200, .flags = SDHCI_OMAP_SPECIAL_RESET, }; static const struct sdhci_omap_data am437_data = { + .omap_offset = 0x100, .offset = 0x200, .flags = SDHCI_OMAP_SPECIAL_RESET, }; static const struct sdhci_omap_data dra7_data = { + .omap_offset = 0x100, .offset = 0x200, .flags = SDHCI_OMAP_REQUIRE_IODELAY, }; static const struct of_device_id omap_sdhci_match[] = { + { .compatible = "ti,omap2430-sdhci", .data = &omap2430_data }, + { .compatible = "ti,omap3-sdhci", .data = &omap3_data }, + { .compatible = "ti,omap4-sdhci", .data = &omap4_data }, + { .compatible = "ti,omap5-sdhci", .data = &omap5_data }, { .compatible = "ti,dra7-sdhci", .data = &dra7_data }, { .compatible = "ti,k2g-sdhci", .data = &k2g_data }, { .compatible = "ti,am335-sdhci", .data = &am335_data }, @@ -1122,6 +1254,8 @@ static int sdhci_omap_probe(struct platform_device *pdev) omap_host->power_mode = MMC_POWER_UNDEFINED; omap_host->timing = MMC_TIMING_LEGACY; omap_host->flags = data->flags; + omap_host->omap_offset = data->omap_offset; + omap_host->con = -EINVAL; /* Prevent invalid restore on first resume */ host->ioaddr += offset; host->mapbase = regs->start + offset; @@ -1172,6 +1306,8 @@ static int sdhci_omap_probe(struct platform_device *pdev) * SYSCONFIG register of omap devices. The callback will be invoked * as part of pm_runtime_get_sync. */ + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 50); pm_runtime_enable(dev); ret = pm_runtime_resume_and_get(dev); if (ret) { @@ -1179,10 +1315,10 @@ static int sdhci_omap_probe(struct platform_device *pdev) goto err_rpm_disable; } - ret = sdhci_omap_set_capabilities(omap_host); + ret = sdhci_omap_set_capabilities(host); if (ret) { dev_err(dev, "failed to set system capabilities\n"); - goto err_put_sync; + goto err_rpm_put; } host->mmc_host_ops.start_signal_voltage_switch = @@ -1192,16 +1328,28 @@ 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; + /* Allow card power off and runtime PM for eMMC/SD card devices */ + mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM; + ret = sdhci_setup_host(host); if (ret) - goto err_put_sync; + goto err_rpm_put; ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host); if (ret) @@ -1211,15 +1359,38 @@ static int sdhci_omap_probe(struct platform_device *pdev) if (ret) goto err_cleanup_host; + /* + * SDIO devices can use the dat1 pin as a wake-up interrupt. Some + * devices like wl1xxx, use an out-of-band GPIO interrupt instead. + */ + omap_host->wakeirq = of_irq_get_byname(dev->of_node, "wakeup"); + if (omap_host->wakeirq == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_cleanup_host; + } + if (omap_host->wakeirq > 0) { + device_init_wakeup(dev, true); + ret = dev_pm_set_dedicated_wake_irq(dev, omap_host->wakeirq); + if (ret) { + device_init_wakeup(dev, false); + goto err_cleanup_host; + } + host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; err_cleanup_host: sdhci_cleanup_host(host); -err_put_sync: - pm_runtime_put_sync(dev); - +err_rpm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); err_rpm_disable: + pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); err_pltfm_free: @@ -1232,64 +1403,81 @@ static int sdhci_omap_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sdhci_host *host = platform_get_drvdata(pdev); + pm_runtime_get_sync(dev); sdhci_remove_host(host, true); + device_init_wakeup(dev, false); + dev_pm_clear_wake_irq(dev); + pm_runtime_dont_use_autosuspend(dev); pm_runtime_put_sync(dev); - pm_runtime_disable(dev); + /* Ensure device gets disabled despite userspace sysfs config */ + pm_runtime_force_suspend(dev); sdhci_pltfm_free(pdev); return 0; } -#ifdef CONFIG_PM_SLEEP -static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host) + +#ifdef CONFIG_PM +static void __maybe_unused 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); } -static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host) +/* Order matters here, HCTL must be restored in two phases */ +static void __maybe_unused 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) +static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); - sdhci_suspend_host(host); + sdhci_runtime_suspend_host(host); sdhci_omap_context_save(omap_host); pinctrl_pm_select_idle_state(dev); - pm_runtime_force_suspend(dev); - return 0; } -static int __maybe_unused sdhci_omap_resume(struct device *dev) +static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); - pm_runtime_force_resume(dev); - pinctrl_pm_select_default_state(dev); - sdhci_omap_context_restore(omap_host); + if (omap_host->con != -EINVAL) + sdhci_omap_context_restore(omap_host); - sdhci_resume_host(host); + sdhci_runtime_resume_host(host, 0); return 0; } #endif -static SIMPLE_DEV_PM_OPS(sdhci_omap_dev_pm_ops, sdhci_omap_suspend, - sdhci_omap_resume); + +static const struct dev_pm_ops sdhci_omap_dev_pm_ops = { + SET_RUNTIME_PM_OPS(sdhci_omap_runtime_suspend, + sdhci_omap_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; static struct platform_driver sdhci_omap_driver = { .probe = sdhci_omap_probe, diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index d0f2edfe296c..6f9877546830 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -17,8 +17,6 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/device.h> -#include <linux/mmc/host.h> -#include <linux/mmc/mmc.h> #include <linux/scatterlist.h> #include <linux/io.h> #include <linux/iopoll.h> @@ -26,11 +24,13 @@ #include <linux/pm_runtime.h> #include <linux/pm_qos.h> #include <linux/debugfs.h> -#include <linux/mmc/slot-gpio.h> -#include <linux/mmc/sdhci-pci-data.h> #include <linux/acpi.h> #include <linux/dmi.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/slot-gpio.h> + #ifdef CONFIG_X86 #include <asm/iosf_mbi.h> #endif @@ -345,73 +345,6 @@ static int pch_hc_probe_slot(struct sdhci_pci_slot *slot) return 0; } -#ifdef CONFIG_PM - -static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id) -{ - struct sdhci_pci_slot *slot = dev_id; - struct sdhci_host *host = slot->host; - - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); - return IRQ_HANDLED; -} - -static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) -{ - int err, irq, gpio = slot->cd_gpio; - - slot->cd_gpio = -EINVAL; - slot->cd_irq = -EINVAL; - - if (!gpio_is_valid(gpio)) - return; - - err = devm_gpio_request(&slot->chip->pdev->dev, gpio, "sd_cd"); - if (err < 0) - goto out; - - err = gpio_direction_input(gpio); - if (err < 0) - goto out_free; - - irq = gpio_to_irq(gpio); - if (irq < 0) - goto out_free; - - err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, "sd_cd", slot); - if (err) - goto out_free; - - slot->cd_gpio = gpio; - slot->cd_irq = irq; - - return; - -out_free: - devm_gpio_free(&slot->chip->pdev->dev, gpio); -out: - dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n"); -} - -static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) -{ - if (slot->cd_irq >= 0) - free_irq(slot->cd_irq, slot); -} - -#else - -static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) -{ -} - -static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) -{ -} - -#endif - static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; @@ -616,24 +549,6 @@ static int intel_select_drive_strength(struct mmc_card *card, return intel_host->drv_strength; } -static int sdhci_get_cd_nogpio(struct mmc_host *mmc) -{ - struct sdhci_host *host = mmc_priv(mmc); - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&host->lock, flags); - - if (host->flags & SDHCI_DEVICE_DEAD) - goto out; - - ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); -out: - spin_unlock_irqrestore(&host->lock, flags); - - return ret; -} - static int bxt_get_cd(struct mmc_host *mmc) { int gpio_cd = mmc_gpio_get_cd(mmc); @@ -2000,21 +1915,6 @@ int sdhci_pci_enable_dma(struct sdhci_host *host) return 0; } -static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) -{ - struct sdhci_pci_slot *slot = sdhci_priv(host); - int rst_n_gpio = slot->rst_n_gpio; - - if (!gpio_is_valid(rst_n_gpio)) - return; - gpio_set_value_cansleep(rst_n_gpio, 0); - /* For eMMC, minimum is 1us but give it 10us for good measure */ - udelay(10); - gpio_set_value_cansleep(rst_n_gpio, 1); - /* For eMMC, minimum is 200us but give it 300us for good measure */ - usleep_range(300, 1000); -} - static void sdhci_pci_hw_reset(struct sdhci_host *host) { struct sdhci_pci_slot *slot = sdhci_priv(host); @@ -2145,26 +2045,8 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( slot->chip = chip; slot->host = host; - slot->rst_n_gpio = -EINVAL; - slot->cd_gpio = -EINVAL; slot->cd_idx = -1; - /* Retrieve platform data if there is any */ - if (*sdhci_pci_get_data) - slot->data = sdhci_pci_get_data(pdev, slotno); - - if (slot->data) { - if (slot->data->setup) { - ret = slot->data->setup(slot->data); - if (ret) { - dev_err(&pdev->dev, "platform setup failed\n"); - goto free; - } - } - slot->rst_n_gpio = slot->data->rst_n_gpio; - slot->cd_gpio = slot->data->cd_gpio; - } - host->hw_name = "PCI"; host->ops = chip->fixes && chip->fixes->ops ? chip->fixes->ops : @@ -2188,17 +2070,6 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( goto cleanup; } - if (gpio_is_valid(slot->rst_n_gpio)) { - if (!devm_gpio_request(&pdev->dev, slot->rst_n_gpio, "eMMC_reset")) { - gpio_direction_output(slot->rst_n_gpio, 1); - slot->host->mmc->caps |= MMC_CAP_HW_RESET; - slot->hw_reset = sdhci_pci_gpio_hw_reset; - } else { - dev_warn(&pdev->dev, "failed to request rst_n_gpio\n"); - slot->rst_n_gpio = -EINVAL; - } - } - host->mmc->pm_caps = MMC_PM_KEEP_POWER; host->mmc->slotno = slotno; host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; @@ -2233,15 +2104,11 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( if (ret) goto remove; - sdhci_pci_add_own_cd(slot); - /* * Check if the chip needs a separate GPIO for card detect to wake up * from runtime suspend. If it is not there, don't allow runtime PM. - * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. */ - if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && - !gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0) + if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && slot->cd_idx < 0) chip->allow_runtime_pm = false; return slot; @@ -2251,10 +2118,6 @@ remove: chip->fixes->remove_slot(slot, 0); cleanup: - if (slot->data && slot->data->cleanup) - slot->data->cleanup(slot->data); - -free: sdhci_free_host(host); return ERR_PTR(ret); @@ -2265,8 +2128,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) int dead; u32 scratch; - sdhci_pci_remove_own_cd(slot); - dead = 0; scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); if (scratch == (u32)-1) @@ -2277,9 +2138,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) if (slot->chip->fixes && slot->chip->fixes->remove_slot) slot->chip->fixes->remove_slot(slot, dead); - if (slot->data && slot->data->cleanup) - slot->data->cleanup(slot->data); - sdhci_free_host(slot->host); } diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c deleted file mode 100644 index 18638fb363d8..000000000000 --- a/drivers/mmc/host/sdhci-pci-data.c +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include <linux/module.h> -#include <linux/mmc/sdhci-pci-data.h> - -struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno); -EXPORT_SYMBOL_GPL(sdhci_pci_get_data); 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-pci.h b/drivers/mmc/host/sdhci-pci.h index 8f90c4163bb5..5e3193278ff9 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -156,11 +156,6 @@ struct sdhci_pci_fixes { struct sdhci_pci_slot { struct sdhci_pci_chip *chip; struct sdhci_host *host; - struct sdhci_pci_data *data; - - int rst_n_gpio; - int cd_gpio; - int cd_irq; int cd_idx; bool cd_override_level; 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..269c86569402 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; @@ -2428,6 +2425,25 @@ static int sdhci_get_cd(struct mmc_host *mmc) return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); } +int sdhci_get_cd_nogpio(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&host->lock, flags); + + if (host->flags & SDHCI_DEVICE_DEAD) + goto out; + + ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); +out: + spin_unlock_irqrestore(&host->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(sdhci_get_cd_nogpio); + static int sdhci_check_ro(struct sdhci_host *host) { unsigned long flags; @@ -3238,7 +3254,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; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index e8d04e42a5af..bb883553d3b4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -750,7 +750,6 @@ static inline void *sdhci_priv(struct sdhci_host *host) return host->private; } -void sdhci_card_detect(struct sdhci_host *host); void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver, const u32 *caps, const u32 *caps1); int sdhci_setup_host(struct sdhci_host *host); @@ -775,6 +774,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host, unsigned short vdd); void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, unsigned short vdd); +int sdhci_get_cd_nogpio(struct mmc_host *mmc); void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq); int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq); void sdhci_set_bus_width(struct sdhci_host *host, int width); |