diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-19 01:54:05 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-19 01:54:05 +0300 |
commit | bbd60bffaf780464298cb7a39852f7f1065f1726 (patch) | |
tree | 1338cf7dd93214382127a53b88969e870fbb837a /drivers | |
parent | 307797159ac25fe5a2048bf5c6a5718298edca57 (diff) | |
parent | 7f38abf220e2c800a2c451372e9f07ed5fd0ea49 (diff) | |
download | linux-bbd60bffaf780464298cb7a39852f7f1065f1726.tar.xz |
Merge tag 'mmc-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson:
"Updates for MMC for v4.19.
MMC core:
- Add some fine-grained hooks to further support HS400 tuning
- Improve error path for bus width setting for HS400es
- Use a common method when checking R1 status
MMC host:
- renesas_sdhi: Add r8a77990 support
- renesas_sdhi: Add eMMC HS400 mode support
- tmio/renesas_sdhi: Improve tuning/clock management
- tmio: Add eMMC HS400 mode support
- sunxi: Add support for 3.3V eMMC DDR mode
- mmci: Initial support to manage variant specific callbacks
- sdhci: Don't try 3.3V I/O voltage if not supported
- sdhci-pci-dwc-mshc: Add driver to support Synopsys dwc mshc SDHCI PCI
- sdhci-of-dwcmshc: Add driver to support Synopsys DWC MSHC SDHCI
- sdhci-msm: Add support for new version sdcc V5
- sdhci-pci-o2micro: Add support for O2 eMMC HS200 mode
- sdhci-pci-o2micro: Add support for O2 hardware tuning
- sdhci-pci-o2micro: Add MSI interrupt support for O2 SD host
- sdhci-pci: Add support for Intel ICP
- sdhci-tegra: Prevent ACMD23 and HS200 mode on Tegra 3
- sdhci-tegra: Fix eMMC DDR52 mode
- sdhci-tegra: Improve clock management
- dw_mmc-rockchip: Document compatible string for px30
- sdhci-esdhc-imx: Add support for 3.3V eMMC DDR mode
- sdhci-of-esdhc: Set proper DMA mask for ls104x chips
- sdhci-of-esdhc: Improve clock management
- sdhci-of-arasan: Add a quirk to manage unstable clocks
- dw_mmc-exynos: Address potential external abort during system resume
- pxamci: Add support for common MMC DT bindings
- pxamci: Several cleanups and improvements
- pxamci: Merge immutable branch for pxa to switch to DMA slave maps"
* tag 'mmc-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (56 commits)
mmc: core: improve reasonableness of bus width setting for HS400es
mmc: tmio: remove unneeded variable in tmio_mmc_start_command()
mmc: renesas_sdhi: Fix sampling clock position selecting
mmc: tmio: Fix tuning flow
mmc: sunxi: remove output of virtual base address
dt-bindings: mmc: rockchip-dw-mshc: add description for px30
mmc: renesas_sdhi: Add r8a77990 support
mmc: sunxi: allow 3.3V DDR when DDR is available
mmc: mmci: Add and implement a ->dma_setup() callback for qcom dml
mmc: mmci: Initial support to manage variant specific callbacks
mmc: tegra: Force correct divider calculation on DDR50/52
mmc: sdhci: Add MSI interrupt support for O2 SD host
mmc: sdhci: Add support for O2 hardware tuning
mmc: sdhci: Export sdhci tuning function symbol
mmc: sdhci: Change O2 Host HS200 mode clock frequency to 200MHz
mmc: sdhci: Add support for O2 eMMC HS200 mode
mmc: tegra: Add and use tegra_sdhci_get_max_clock()
mmc: sdhci-esdhc-imx: fix indent
mmc: sdhci-esdhc-imx: disable clocks before changing frequency
mmc: tegra: prevent ACMD23 on Tegra 3
...
Diffstat (limited to 'drivers')
32 files changed, 1322 insertions, 484 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 281826d1fcca..50a5c340307b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2078,7 +2078,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; /* Do not retry else we can't see errors */ err = mmc_wait_for_cmd(card->host, &cmd, 0); - if (err || (cmd.resp[0] & 0xFDF92000)) { + if (err || R1_STATUS(cmd.resp[0])) { pr_err("error %d requesting status %#x\n", err, cmd.resp[0]); err = -EIO; @@ -2716,52 +2716,6 @@ void mmc_stop_host(struct mmc_host *host) mmc_release_host(host); } -int mmc_power_save_host(struct mmc_host *host) -{ - int ret = 0; - - pr_debug("%s: %s: powering down\n", mmc_hostname(host), __func__); - - mmc_bus_get(host); - - if (!host->bus_ops || host->bus_dead) { - mmc_bus_put(host); - return -EINVAL; - } - - if (host->bus_ops->power_save) - ret = host->bus_ops->power_save(host); - - mmc_bus_put(host); - - mmc_power_off(host); - - return ret; -} -EXPORT_SYMBOL(mmc_power_save_host); - -int mmc_power_restore_host(struct mmc_host *host) -{ - int ret; - - pr_debug("%s: %s: powering up\n", mmc_hostname(host), __func__); - - mmc_bus_get(host); - - if (!host->bus_ops || host->bus_dead) { - mmc_bus_put(host); - return -EINVAL; - } - - mmc_power_up(host, host->card->ocr); - ret = host->bus_ops->power_restore(host); - - mmc_bus_put(host); - - return ret; -} -EXPORT_SYMBOL(mmc_power_restore_host); - #ifdef CONFIG_PM_SLEEP /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 9d8f09ac0821..087ba68b2920 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -28,8 +28,6 @@ struct mmc_bus_ops { int (*resume)(struct mmc_host *); int (*runtime_suspend)(struct mmc_host *); int (*runtime_resume)(struct mmc_host *); - int (*power_save)(struct mmc_host *); - int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); int (*shutdown)(struct mmc_host *); int (*hw_reset)(struct mmc_host *); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4466f5de54d4..bc1bd2c25613 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1169,6 +1169,10 @@ static int mmc_select_hs400(struct mmc_card *card) /* Set host controller to HS timing */ mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + /* Prepare host to downgrade to HS timing */ + if (host->ops->hs400_downgrade) + host->ops->hs400_downgrade(host); + /* Reduce frequency to HS frequency */ max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); @@ -1209,6 +1213,9 @@ static int mmc_select_hs400(struct mmc_card *card) if (err) goto out_err; + if (host->ops->hs400_complete) + host->ops->hs400_complete(host); + return 0; out_err: @@ -1256,6 +1263,9 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_HS); + if (host->ops->hs400_downgrade) + host->ops->hs400_downgrade(host); + err = mmc_switch_status(card); if (err) goto out_err; @@ -1338,8 +1348,12 @@ static int mmc_select_hs400es(struct mmc_card *card) goto out_err; err = mmc_select_bus_width(card); - if (err < 0) + if (err != MMC_BUS_WIDTH_8) { + pr_err("%s: switch to 8bit bus width failed, err:%d\n", + mmc_hostname(host), err); + err = err < 0 ? err : -ENOTSUPP; goto out_err; + } /* Switch card to HS mode */ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 42d6aa89a48a..873b2aa0c155 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -417,7 +417,7 @@ static int mmc_switch_status_error(struct mmc_host *host, u32 status) if (status & R1_SPI_ILLEGAL_COMMAND) return -EBADMSG; } else { - if (status & 0xFDFFA000) + if (R1_STATUS(status)) pr_warn("%s: unexpected status %#x after switch\n", mmc_hostname(host), status); if (status & R1_SWITCH_ERROR) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index a86490dbca70..d8e17ea6126d 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1076,7 +1076,6 @@ static const struct mmc_bus_ops mmc_sdio_ops = { .resume = mmc_sdio_resume, .runtime_suspend = mmc_sdio_runtime_suspend, .runtime_resume = mmc_sdio_runtime_resume, - .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, .hw_reset = mmc_sdio_hw_reset, .sw_reset = mmc_sdio_sw_reset, diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 0581c199c996..694d0828215d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -176,6 +176,17 @@ config MMC_SDHCI_OF_HLWD If unsure, say N. +config MMC_SDHCI_OF_DWCMSHC + tristate "SDHCI OF support for the Synopsys DWC MSHC" + depends on MMC_SDHCI_PLTFM + depends on OF + depends on COMMON_CLK + help + This selects Synopsys DesignWare Cores Mobile Storage Controller + support. + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_CADENCE tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 85dc1322c3de..ce8398e6f2c0 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -11,7 +11,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o 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-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \ + sdhci-pci-dwc-mshc.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 @@ -82,6 +83,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o +obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index a84aa3f1ae85..ab47b018716a 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) return ret; } +#endif /* CONFIG_PM */ + +#ifdef CONFIG_PM_SLEEP +/** + * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code + * + * This ensures that device will be in runtime active state in + * dw_mci_exynos_resume_noirq after calling pm_runtime_force_resume() + */ +static int dw_mci_exynos_suspend_noirq(struct device *dev) +{ + pm_runtime_get_noresume(dev); + return pm_runtime_force_suspend(dev); +} /** * dw_mci_exynos_resume_noirq - Exynos-specific resume code @@ -186,12 +200,16 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) * * We run this code on all exynos variants because it doesn't hurt. */ - static int dw_mci_exynos_resume_noirq(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci_exynos_priv_data *priv = host->priv; u32 clksel; + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) @@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) mci_writel(host, CLKSEL, clksel); } + pm_runtime_put(dev); + return 0; } -#else -#define dw_mci_exynos_resume_noirq NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) { @@ -553,14 +571,11 @@ static int dw_mci_exynos_remove(struct platform_device *pdev) } static const struct dev_pm_ops dw_mci_exynos_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, + dw_mci_exynos_resume_noirq) SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_exynos_runtime_resume, NULL) - .resume_noirq = dw_mci_exynos_resume_noirq, - .thaw_noirq = dw_mci_exynos_resume_noirq, - .restore_noirq = dw_mci_exynos_resume_noirq, }; static struct platform_driver dw_mci_exynos_pltfm_driver = { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f1849775e47e..1841d250e9e2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -48,78 +48,6 @@ static unsigned int fmax = 515633; -/** - * struct variant_data - MMCI variant-specific quirks - * @clkreg: default value for MCICLOCK register - * @clkreg_enable: enable value for MMCICLOCK register - * @clkreg_8bit_bus_enable: enable value for 8 bit bus - * @clkreg_neg_edge_enable: enable value for inverted data/cmd output - * @datalength_bits: number of bits in the MMCIDATALENGTH register - * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY - * is asserted (likewise for RX) - * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY - * is asserted (likewise for RX) - * @data_cmd_enable: enable value for data commands. - * @st_sdio: enable ST specific SDIO logic - * @st_clkdiv: true if using a ST-specific clock divider algorithm - * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. - * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register - * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl - * register - * @datactrl_mask_sdio: SDIO enable mask in datactrl register - * @pwrreg_powerup: power up value for MMCIPOWER register - * @f_max: maximum clk frequency supported by the controller. - * @signal_direction: input/out direction of bus signals can be indicated - * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock - * @busy_detect: true if the variant supports busy detection on DAT0. - * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM - * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register - * indicating that the card is busy - * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for - * getting busy end detection interrupts - * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply - * @explicit_mclk_control: enable explicit mclk control in driver. - * @qcom_fifo: enables qcom specific fifo pio read logic. - * @qcom_dml: enables qcom specific dma glue for dma transfers. - * @reversed_irq_handling: handle data irq before cmd irq. - * @mmcimask1: true if variant have a MMCIMASK1 register. - * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS - * register. - * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register - */ -struct variant_data { - unsigned int clkreg; - unsigned int clkreg_enable; - unsigned int clkreg_8bit_bus_enable; - unsigned int clkreg_neg_edge_enable; - unsigned int datalength_bits; - unsigned int fifosize; - unsigned int fifohalfsize; - unsigned int data_cmd_enable; - unsigned int datactrl_mask_ddrmode; - unsigned int datactrl_mask_sdio; - bool st_sdio; - bool st_clkdiv; - bool blksz_datactrl16; - bool blksz_datactrl4; - u32 pwrreg_powerup; - u32 f_max; - bool signal_direction; - bool pwrreg_clkgate; - bool busy_detect; - u32 busy_dpsm_flag; - u32 busy_detect_flag; - u32 busy_detect_mask; - bool pwrreg_nopower; - bool explicit_mclk_control; - bool qcom_fifo; - bool qcom_dml; - bool reversed_irq_handling; - bool mmcimask1; - u32 start_err; - u32 opendrain; -}; - static struct variant_data variant_arm = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, @@ -280,6 +208,7 @@ static struct variant_data variant_qcom = { .mmcimask1 = true, .start_err = MCI_STARTBITERR, .opendrain = MCI_ROD, + .init = qcom_variant_init, }; /* Busy detection for the ST Micro variant */ @@ -489,7 +418,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) static void mmci_dma_setup(struct mmci_host *host) { const char *rxname, *txname; - struct variant_data *variant = host->variant; host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); @@ -537,9 +465,8 @@ static void mmci_dma_setup(struct mmci_host *host) host->mmc->max_seg_size = max_seg_size; } - if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel) - if (dml_hw_init(host, host->mmc->parent->of_node)) - variant->qcom_dml = false; + if (host->ops && host->ops->dma_setup) + host->ops->dma_setup(host); } /* @@ -1706,6 +1633,9 @@ static int mmci_probe(struct amba_device *dev, goto clk_disable; } + if (variant->init) + variant->init(host); + /* * The ARM and ST versions of the block have slightly different * clock divider equations which means that the minimum divider diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index f91cdf7f6dae..517591d219e9 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -195,8 +195,86 @@ #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain" struct clk; -struct variant_data; struct dma_chan; +struct mmci_host; + +/** + * struct variant_data - MMCI variant-specific quirks + * @clkreg: default value for MCICLOCK register + * @clkreg_enable: enable value for MMCICLOCK register + * @clkreg_8bit_bus_enable: enable value for 8 bit bus + * @clkreg_neg_edge_enable: enable value for inverted data/cmd output + * @datalength_bits: number of bits in the MMCIDATALENGTH register + * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY + * is asserted (likewise for RX) + * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY + * is asserted (likewise for RX) + * @data_cmd_enable: enable value for data commands. + * @st_sdio: enable ST specific SDIO logic + * @st_clkdiv: true if using a ST-specific clock divider algorithm + * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. + * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl + * register + * @datactrl_mask_sdio: SDIO enable mask in datactrl register + * @pwrreg_powerup: power up value for MMCIPOWER register + * @f_max: maximum clk frequency supported by the controller. + * @signal_direction: input/out direction of bus signals can be indicated + * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock + * @busy_detect: true if the variant supports busy detection on DAT0. + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register + * indicating that the card is busy + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for + * getting busy end detection interrupts + * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply + * @explicit_mclk_control: enable explicit mclk control in driver. + * @qcom_fifo: enables qcom specific fifo pio read logic. + * @qcom_dml: enables qcom specific dma glue for dma transfers. + * @reversed_irq_handling: handle data irq before cmd irq. + * @mmcimask1: true if variant have a MMCIMASK1 register. + * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS + * register. + * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register + */ +struct variant_data { + unsigned int clkreg; + unsigned int clkreg_enable; + unsigned int clkreg_8bit_bus_enable; + unsigned int clkreg_neg_edge_enable; + unsigned int datalength_bits; + unsigned int fifosize; + unsigned int fifohalfsize; + unsigned int data_cmd_enable; + unsigned int datactrl_mask_ddrmode; + unsigned int datactrl_mask_sdio; + bool st_sdio; + bool st_clkdiv; + bool blksz_datactrl16; + bool blksz_datactrl4; + u32 pwrreg_powerup; + u32 f_max; + bool signal_direction; + bool pwrreg_clkgate; + bool busy_detect; + u32 busy_dpsm_flag; + u32 busy_detect_flag; + u32 busy_detect_mask; + bool pwrreg_nopower; + bool explicit_mclk_control; + bool qcom_fifo; + bool qcom_dml; + bool reversed_irq_handling; + bool mmcimask1; + u32 start_err; + u32 opendrain; + void (*init)(struct mmci_host *host); +}; + +/* mmci variant callbacks */ +struct mmci_host_ops { + void (*dma_setup)(struct mmci_host *host); +}; struct mmci_host_next { struct dma_async_tx_descriptor *dma_desc; @@ -228,6 +306,7 @@ struct mmci_host { u32 mask1_reg; bool vqmmc_enabled; struct mmci_platform_data *plat; + struct mmci_host_ops *ops; struct variant_data *variant; struct pinctrl *pinctrl; struct pinctrl_state *pins_default; diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c index 00750c9d3514..be3fab5db83f 100644 --- a/drivers/mmc/host/mmci_qcom_dml.c +++ b/drivers/mmc/host/mmci_qcom_dml.c @@ -119,17 +119,20 @@ static int of_get_dml_pipe_index(struct device_node *np, const char *name) } /* Initialize the dml hardware connected to SD Card controller */ -int dml_hw_init(struct mmci_host *host, struct device_node *np) +static void qcom_dma_setup(struct mmci_host *host) { u32 config; void __iomem *base; int consumer_id, producer_id; + struct device_node *np = host->mmc->parent->of_node; consumer_id = of_get_dml_pipe_index(np, "tx"); producer_id = of_get_dml_pipe_index(np, "rx"); - if (producer_id < 0 || consumer_id < 0) - return -ENODEV; + if (producer_id < 0 || consumer_id < 0) { + host->variant->qcom_dml = false; + return; + } base = host->base + DML_OFFSET; @@ -172,6 +175,13 @@ int dml_hw_init(struct mmci_host *host, struct device_node *np) /* Make sure dml initialization is finished */ mb(); +} - return 0; +static struct mmci_host_ops qcom_variant_ops = { + .dma_setup = qcom_dma_setup, +}; + +void qcom_variant_init(struct mmci_host *host) +{ + host->ops = &qcom_variant_ops; } diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h index 6e405d09d534..fa16f6f4d4ad 100644 --- a/drivers/mmc/host/mmci_qcom_dml.h +++ b/drivers/mmc/host/mmci_qcom_dml.h @@ -16,12 +16,11 @@ #define __MMC_QCOM_DML_H__ #ifdef CONFIG_MMC_QCOM_DML -int dml_hw_init(struct mmci_host *host, struct device_node *np); +void qcom_variant_init(struct mmci_host *host); void dml_start_xfer(struct mmci_host *host, struct mmc_data *data); #else -static inline int dml_hw_init(struct mmci_host *host, struct device_node *np) +static inline void qcom_variant_init(struct mmci_host *host) { - return -ENOSYS; } static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) { diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 6c94474e36f4..f7ffbf1676b1 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -58,11 +58,11 @@ struct pxamci_host { void __iomem *base; struct clk *clk; unsigned long clkrate; - int irq; unsigned int clkrt; unsigned int cmdat; unsigned int imask; unsigned int power_mode; + unsigned long detect_delay_ms; struct pxamci_platform_data *pdata; struct mmc_request *mrq; @@ -72,64 +72,48 @@ struct pxamci_host { struct dma_chan *dma_chan_rx; struct dma_chan *dma_chan_tx; dma_cookie_t dma_cookie; - dma_addr_t sg_dma; unsigned int dma_len; - unsigned int dma_dir; - unsigned int dma_drcmrrx; - unsigned int dma_drcmrtx; - - struct regulator *vcc; }; -static inline void pxamci_init_ocr(struct pxamci_host *host) +static int pxamci_init_ocr(struct pxamci_host *host) { -#ifdef CONFIG_REGULATOR - host->vcc = devm_regulator_get_optional(mmc_dev(host->mmc), "vmmc"); - - if (IS_ERR(host->vcc)) - host->vcc = NULL; - else { - host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); - if (host->pdata && host->pdata->ocr_mask) - dev_warn(mmc_dev(host->mmc), - "ocr_mask/setpower will not be used\n"); - } -#endif - if (host->vcc == NULL) { + struct mmc_host *mmc = host->mmc; + int ret; + + ret = mmc_regulator_get_supply(mmc); + if (ret < 0) + return ret; + + if (IS_ERR(mmc->supply.vmmc)) { /* fall-back to platform data */ - host->mmc->ocr_avail = host->pdata ? + mmc->ocr_avail = host->pdata ? host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; } + + return 0; } static inline int pxamci_set_power(struct pxamci_host *host, unsigned char power_mode, unsigned int vdd) { + struct mmc_host *mmc = host->mmc; + struct regulator *supply = mmc->supply.vmmc; int on; - if (host->vcc) { - int ret; + if (!IS_ERR(supply)) + return mmc_regulator_set_ocr(mmc, supply, vdd); - if (power_mode == MMC_POWER_UP) { - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - if (ret) - return ret; - } else if (power_mode == MMC_POWER_OFF) { - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); - if (ret) - return ret; - } - } - if (!host->vcc && host->pdata && + if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) { on = ((1 << vdd) & host->pdata->ocr_mask); gpio_set_value(host->pdata->gpio_power, !!on ^ host->pdata->gpio_power_invert); } - if (!host->vcc && host->pdata && host->pdata->setpower) + + if (host->pdata && host->pdata->setpower) return host->pdata->setpower(mmc_dev(host->mmc), vdd); return 0; @@ -584,7 +568,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) { struct pxamci_host *host = mmc_priv(devid); - mmc_detect_change(devid, msecs_to_jiffies(host->pdata->detect_delay_ms)); + mmc_detect_change(devid, msecs_to_jiffies(host->detect_delay_ms)); return IRQ_HANDLED; } @@ -596,37 +580,30 @@ static const struct of_device_id pxa_mmc_dt_ids[] = { MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids); -static int pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev, + struct mmc_host *mmc) { - struct device_node *np = pdev->dev.of_node; - struct pxamci_platform_data *pdata; - u32 tmp; - - if (!np) - return 0; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; + struct device_node *np = pdev->dev.of_node; + struct pxamci_host *host = mmc_priv(mmc); + u32 tmp; + int ret; - pdata->gpio_card_detect = - of_get_named_gpio(np, "cd-gpios", 0); - pdata->gpio_card_ro = - of_get_named_gpio(np, "wp-gpios", 0); + if (!np) + return 0; /* pxa-mmc specific */ - pdata->gpio_power = - of_get_named_gpio(np, "pxa-mmc,gpio-power", 0); - if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0) - pdata->detect_delay_ms = tmp; + host->detect_delay_ms = tmp; - pdev->dev.platform_data = pdata; + ret = mmc_of_parse(mmc); + if (ret < 0) + return ret; - return 0; + return 0; } #else -static int pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev, + struct mmc_host *mmc) { return 0; } @@ -636,19 +613,16 @@ static int pxamci_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct pxamci_host *host = NULL; + struct device *dev = &pdev->dev; struct resource *r; - int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; - - ret = pxamci_of_init(pdev); - if (ret) - return ret; + int ret, irq; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); + mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev); if (!mmc) { ret = -ENOMEM; goto out; @@ -677,12 +651,16 @@ static int pxamci_probe(struct platform_device *pdev) */ mmc->max_blk_count = 65535; + ret = pxamci_of_init(pdev, mmc); + if (ret) + return ret; + host = mmc_priv(mmc); host->mmc = mmc; host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; - host->clk = devm_clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get(dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; @@ -697,7 +675,9 @@ static int pxamci_probe(struct platform_device *pdev) mmc->f_min = (host->clkrate + 63) / 64; mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate; - pxamci_init_ocr(host); + ret = pxamci_init_ocr(host); + if (ret < 0) + return ret; mmc->caps = 0; host->cmdat = 0; @@ -711,10 +691,9 @@ static int pxamci_probe(struct platform_device *pdev) spin_lock_init(&host->lock); host->res = r; - host->irq = irq; host->imask = MMC_I_MASK_ALL; - host->base = devm_ioremap_resource(&pdev->dev, r); + host->base = devm_ioremap_resource(dev, r); if (IS_ERR(host->base)) { ret = PTR_ERR(host->base); goto out; @@ -729,69 +708,76 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - ret = devm_request_irq(&pdev->dev, host->irq, pxamci_irq, 0, + ret = devm_request_irq(dev, irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; platform_set_drvdata(pdev, mmc); - host->dma_chan_rx = dma_request_slave_channel(&pdev->dev, "rx"); + host->dma_chan_rx = dma_request_slave_channel(dev, "rx"); if (host->dma_chan_rx == NULL) { - dev_err(&pdev->dev, "unable to request rx dma channel\n"); + dev_err(dev, "unable to request rx dma channel\n"); ret = -ENODEV; goto out; } - host->dma_chan_tx = dma_request_slave_channel(&pdev->dev, "tx"); + host->dma_chan_tx = dma_request_slave_channel(dev, "tx"); if (host->dma_chan_tx == NULL) { - dev_err(&pdev->dev, "unable to request tx dma channel\n"); + dev_err(dev, "unable to request tx dma channel\n"); ret = -ENODEV; goto out; } if (host->pdata) { - gpio_cd = host->pdata->gpio_card_detect; - gpio_ro = host->pdata->gpio_card_ro; - gpio_power = host->pdata->gpio_power; - } - if (gpio_is_valid(gpio_power)) { - ret = devm_gpio_request(&pdev->dev, gpio_power, - "mmc card power"); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", - gpio_power); - goto out; + int gpio_cd = host->pdata->gpio_card_detect; + int gpio_ro = host->pdata->gpio_card_ro; + int gpio_power = host->pdata->gpio_power; + + host->detect_delay_ms = host->pdata->detect_delay_ms; + + if (gpio_is_valid(gpio_power)) { + ret = devm_gpio_request(dev, gpio_power, + "mmc card power"); + if (ret) { + dev_err(dev, + "Failed requesting gpio_power %d\n", + gpio_power); + goto out; + } + gpio_direction_output(gpio_power, + host->pdata->gpio_power_invert); } - gpio_direction_output(gpio_power, - host->pdata->gpio_power_invert); - } - if (gpio_is_valid(gpio_ro)) { - ret = mmc_gpio_request_ro(mmc, gpio_ro); + + if (gpio_is_valid(gpio_ro)) { + ret = mmc_gpio_request_ro(mmc, gpio_ro); + if (ret) { + dev_err(dev, + "Failed requesting gpio_ro %d\n", + gpio_ro); + goto out; + } else { + mmc->caps2 |= host->pdata->gpio_card_ro_invert ? + 0 : MMC_CAP2_RO_ACTIVE_HIGH; + } + } + + if (gpio_is_valid(gpio_cd)) + ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", - gpio_ro); + dev_err(dev, "Failed requesting gpio_cd %d\n", + gpio_cd); goto out; - } else { - mmc->caps2 |= host->pdata->gpio_card_ro_invert ? - 0 : MMC_CAP2_RO_ACTIVE_HIGH; } - } - if (gpio_is_valid(gpio_cd)) - ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); - goto out; - } - - if (host->pdata && host->pdata->init) - host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); + if (host->pdata->init) + host->pdata->init(dev, pxamci_detect_irq, mmc); - if (gpio_is_valid(gpio_power) && host->pdata->setpower) - dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); - if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) - dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); + if (gpio_is_valid(gpio_power) && host->pdata->setpower) + dev_warn(dev, "gpio_power and setpower() both defined\n"); + if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) + dev_warn(dev, "gpio_ro and get_ro() both defined\n"); + } mmc_add_host(mmc); @@ -812,18 +798,12 @@ out: static int pxamci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); - int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; if (mmc) { struct pxamci_host *host = mmc_priv(mmc); mmc_remove_host(mmc); - if (host->pdata) { - gpio_cd = host->pdata->gpio_card_detect; - gpio_ro = host->pdata->gpio_card_ro; - gpio_power = host->pdata->gpio_power; - } if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); @@ -839,6 +819,7 @@ static int pxamci_remove(struct platform_device *pdev) mmc_free_host(mmc); } + return 0; } diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 45c015da2e75..777e32b0e410 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -212,6 +212,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, #define SH_MOBILE_SDHI_SCC_CKSEL 0x006 #define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008 #define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A +#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E /* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */ #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0) @@ -224,6 +225,9 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, #define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN BIT(0) /* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */ #define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) +/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */ +#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) +#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31) static inline u32 sd_scc_read32(struct tmio_mmc_host *host, struct renesas_sdhi *priv, int addr) @@ -244,33 +248,30 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host) priv = host_to_priv(host); - /* set sampling clock selection range */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, - 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); - /* Initialize SCC */ sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x0); - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, - SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL)); - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + /* set sampling clock selection range */ + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, + SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | + 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + /* Read TAPNUM */ return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >> SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & @@ -286,13 +287,95 @@ static void renesas_sdhi_prepare_tuning(struct tmio_mmc_host *host, sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap); } +static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + /* Set HS400 mode */ + sd_ctrl_write16(host, CTL_SDIF_MODE, 0x0001 | + sd_ctrl_read16(host, CTL_SDIF_MODE)); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, + (SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | + SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) | + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); + + /* Set the sampling clock selection range of HS400 mode */ + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, + SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | + 0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); + + + if (host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400) + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, + host->tap_set / 2); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, + SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); +} + +static void renesas_sdhi_reset_scc(struct tmio_mmc_host *host, + struct renesas_sdhi *priv) +{ + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, + ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL & + sd_scc_read32(host, priv, + SH_MOBILE_SDHI_SCC_CKSEL)); +} + +static void renesas_sdhi_disable_scc(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + + renesas_sdhi_reset_scc(host, priv); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, + ~SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN & + sd_scc_read32(host, priv, + SH_MOBILE_SDHI_SCC_DTCNTL)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); +} + +static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host, + struct renesas_sdhi *priv) +{ + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + /* Reset HS400 mode */ + sd_ctrl_write16(host, CTL_SDIF_MODE, ~0x0001 & + sd_ctrl_read16(host, CTL_SDIF_MODE)); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, + ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | + SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) & + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); +} + +static void renesas_sdhi_prepare_hs400_tuning(struct tmio_mmc_host *host) +{ + renesas_sdhi_reset_hs400_mode(host, host_to_priv(host)); +} + #define SH_MOBILE_SDHI_MAX_TAP 3 static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) { struct renesas_sdhi *priv = host_to_priv(host); unsigned long tap_cnt; /* counter of tuning success */ - unsigned long tap_set; /* tap position */ unsigned long tap_start;/* start position of tuning success */ unsigned long tap_end; /* end position of tuning success */ unsigned long ntap; /* temporary counter of tuning success */ @@ -302,6 +385,18 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); /* + * When tuning CMD19 is issued twice for each tap, merge the + * result requiring the tap to be good in both runs before + * considering it for tuning selection. + */ + for (i = 0; i < host->tap_num * 2; i++) { + int offset = host->tap_num * (i < host->tap_num ? 1 : -1); + + if (!test_bit(i, host->taps)) + clear_bit(i + offset, host->taps); + } + + /* * Find the longest consecutive run of successful probes. If that * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the * center index as the tap. @@ -330,12 +425,12 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) } if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP) - tap_set = (tap_start + tap_end) / 2 % host->tap_num; + host->tap_set = (tap_start + tap_end) / 2 % host->tap_num; else return -EIO; /* Set SCC */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap_set); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, host->tap_set); /* Enable auto re-tuning */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, @@ -368,13 +463,8 @@ static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) priv = host_to_priv(host); - /* Reset SCC */ - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, - ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL & - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); + renesas_sdhi_reset_scc(host, priv); + renesas_sdhi_reset_hs400_mode(host, priv); sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); @@ -592,7 +682,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, /* Enable tuning iff we have an SCC and a supported mode */ if (of_data && of_data->scc_offset && (host->mmc->caps & MMC_CAP_UHS_SDR104 || - host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR)) { + host->mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | + MMC_CAP2_HS400_1_8V))) { const struct renesas_sdhi_scc *taps = of_data->taps; bool hit = false; @@ -616,6 +707,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->select_tuning = renesas_sdhi_select_tuning; host->check_scc_error = renesas_sdhi_check_scc_error; host->hw_reset = renesas_sdhi_hw_reset; + host->prepare_hs400_tuning = + renesas_sdhi_prepare_hs400_tuning; + host->hs400_downgrade = renesas_sdhi_disable_scc; + host->hs400_complete = renesas_sdhi_hs400_complete; } i = 0; diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index d032bd63444d..35cc0de6be67 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -82,6 +82,22 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; +static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_HAVE_4TAP_HS400, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, + .bus_shift = 2, + .scc_offset = 0x1000, + .taps = rcar_gen3_scc_taps, + .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), + /* DMAC can handle 0xffffffff blk count but only 1 segment */ + .max_blk_count = 0xffffffff, + .max_segs = 1, +}; + static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, @@ -98,8 +114,8 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { }; static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { - { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, - { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, {}, }; diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 4bb46c489d71..890f192dedbd 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -78,6 +78,19 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; +static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_HAVE_4TAP_HS400, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, + .bus_shift = 2, + .scc_offset = 0x1000, + .taps = rcar_gen3_scc_taps, + .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), +}; + static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, @@ -104,8 +117,8 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, - { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, - { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 4eb3d29ecde1..f44e49014a44 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Freescale eSDHC i.MX controller driver for the platform bus. * @@ -5,10 +6,6 @@ * * Copyright (c) 2010 Pengutronix e.K. * Author: Wolfram Sang <kernel@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. */ #include <linux/io.h> @@ -708,14 +705,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, int div = 1; u32 temp, val; + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } + if (clock == 0) { host->mmc->actual_clock = 0; - - if (esdhc_is_usdhc(imx_data)) { - val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); - writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, - host->ioaddr + ESDHC_VENDOR_SPEC); - } return; } @@ -761,7 +758,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, if (esdhc_is_usdhc(imx_data)) { val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, - host->ioaddr + ESDHC_VENDOR_SPEC); + host->ioaddr + ESDHC_VENDOR_SPEC); } mdelay(1); @@ -1151,18 +1148,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, &boarddata->tuning_start_tap); if (of_find_property(np, "no-1-8-v", NULL)) - boarddata->support_vsel = false; - else - boarddata->support_vsel = true; + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; mmc_of_parse_voltage(np, &host->ocr_mask); - /* sdr50 and sdr104 need work on 1.8v signal voltage */ - if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) && - !IS_ERR(imx_data->pins_default)) { + if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pins_default)) { imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, ESDHC_PINCTRL_STATE_100MHZ); imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, @@ -1318,7 +1311,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (esdhc_is_usdhc(imx_data)) { host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; - host->mmc->caps |= MMC_CAP_1_8V_DDR; + host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR; if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 646bf377ba77..3cc8bfee6c18 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -33,16 +33,11 @@ #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) -#define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 #define CORE_SW_RST BIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL 0xe8 #define CORE_PWRCTL_BUS_OFF BIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_EN BIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG 0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS 0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_EN BIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +102,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SEL BIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG 0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK 400000 @@ -137,6 +125,117 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +#define msm_host_readl(msm_host, host, offset) \ + msm_host->var_ops->msm_readl_relaxed(host, offset) + +#define msm_host_writel(msm_host, val, host, offset) \ + msm_host->var_ops->msm_writel_relaxed(val, host, offset) + +struct sdhci_msm_offset { + u32 core_hc_mode; + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +static const struct sdhci_msm_offset sdhci_msm_v5_offset = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +static const struct sdhci_msm_offset sdhci_msm_mci_offset = { + .core_hc_mode = 0x78, + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0cc, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xdc, + .core_pwrctl_mask = 0xe0, + .core_pwrctl_clear = 0xe4, + .core_pwrctl_ctl = 0xe8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10c, + .core_vendor_spec_adma_err_addr0 = 0x114, + .core_vendor_spec_adma_err_addr1 = 0x118, + .core_vendor_spec_func2 = 0x110, + .core_vendor_spec_capabilities0 = 0x11c, + .core_ddr_200_cfg = 0x184, + .core_vendor_spec3 = 0x1b0, + .core_dll_config_2 = 0x1b4, + .core_ddr_config = 0x1b8, + .core_ddr_config_2 = 0x1bc, +}; + +struct sdhci_msm_variant_ops { + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -156,8 +255,53 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return msm_host->offset; +} + +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + writel_relaxed(val, host->ioaddr + offset); +} + static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, unsigned int clock) { @@ -205,10 +349,12 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) u32 wait_cnt = 50; u8 ck_out_en; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Poll for CK_OUT_EN bit. max. poll time = 50us */ - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); while (ck_out_en != poll) { if (--wait_cnt == 0) { @@ -218,8 +364,8 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) } udelay(1); - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); } return 0; @@ -235,16 +381,18 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) unsigned long flags; u32 config; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); if (phase > 0xf) return -EINVAL; spin_lock_irqsave(&host->lock, flags); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ rc = msm_dll_poll_ck_out_en(host, 0); @@ -255,24 +403,24 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) * Write the selected DLL clock output phase (0 ... 15) * to CDR_SELEXT bit field of DLL_CONFIG register. */ - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~CDR_SELEXT_MASK; config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ rc = msm_dll_poll_ck_out_en(host, 1); if (rc) goto err_out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CDR_EN; config &= ~CORE_CDR_EXT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); goto out; err_out: @@ -398,6 +546,8 @@ static int msm_find_most_appropriate_phase(struct sdhci_host *host, static inline void msm_cm_dll_set_freq(struct sdhci_host *host) { u32 mclk_freq = 0, config; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Program the MCLK value to MCLK_FREQ bit field */ if (host->clock <= 112000000) @@ -417,10 +567,10 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host) else if (host->clock <= 200000000) mclk_freq = 7; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~CMUX_SHIFT_PHASE_MASK; config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); } /* Initialize the DLL (Programmable Delay Line) */ @@ -432,6 +582,8 @@ static int msm_init_cm_dll(struct sdhci_host *host) int wait_cnt = 50; unsigned long flags; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; spin_lock_irqsave(&host->lock, flags); @@ -440,34 +592,43 @@ static int msm_init_cm_dll(struct sdhci_host *host) * tuning is in progress. Keeping PWRSAVE ON may * turn off the clock. */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_CLK_PWRSAVE; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); if (msm_host->use_14lpp_dll_reset) { - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config |= CORE_DLL_CLOCK_DISABLE; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); msm_cm_dll_set_freq(host); if (msm_host->use_14lpp_dll_reset && !IS_ERR_OR_NULL(msm_host->xo_clk)) { u32 mclk_freq = 0; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= CORE_FLL_CYCLE_CNT; if (config) mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 8), @@ -476,40 +637,52 @@ static int msm_init_cm_dll(struct sdhci_host *host) mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 4), clk_get_rate(msm_host->xo_clk)); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= ~(0xFF << 10); config |= mclk_freq << 10; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); /* wait for 5us before enabling DLL clock */ udelay(5); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); if (msm_host->use_14lpp_dll_reset) { msm_cm_dll_set_freq(host); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= ~CORE_DLL_CLOCK_DISABLE; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ - while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) & + while (!(readl_relaxed(host->ioaddr + msm_offset->core_dll_status) & CORE_DLL_LOCK)) { /* max. wait for 50us sec for LOCK bit to be set */ if (--wait_cnt == 0) { @@ -530,19 +703,21 @@ static void msm_hc_select_default(struct sdhci_host *host) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; if (!msm_host->use_cdclp533) { config = readl_relaxed(host->ioaddr + - CORE_VENDOR_SPEC3); + msm_offset->core_vendor_spec3); config &= ~CORE_PWRSAVE_DLL; writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC3); + msm_offset->core_vendor_spec3); } - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_MCLK_SEL_MASK; config |= CORE_HC_MCLK_SEL_DFLT; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Disable HC_SELECT_IN to be able to use the UHS mode select @@ -551,10 +726,10 @@ static void msm_hc_select_default(struct sdhci_host *host) * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field * in VENDOR_SPEC_FUNC */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_SELECT_IN_EN; config &= ~CORE_HC_SELECT_IN_MASK; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Make sure above writes impacting free running MCLK are completed @@ -570,32 +745,36 @@ static void msm_hc_select_hs400(struct sdhci_host *host) struct mmc_ios ios = host->mmc->ios; u32 config, dll_lock; int rc; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; /* Select the divided clock (free running MCLK/2) */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_MCLK_SEL_MASK; config |= CORE_HC_MCLK_SEL_HS400; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC * register */ if ((msm_host->tuning_done || ios.enhanced_strobe) && !msm_host->calibration_done) { - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); config |= CORE_HC_SELECT_IN_HS400; config |= CORE_HC_SELECT_IN_EN; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + + msm_offset->core_vendor_spec); } if (!msm_host->clk_rate && !msm_host->use_cdclp533) { /* * Poll on DLL_LOCK or DDR_DLL_LOCK bits in - * CORE_DLL_STATUS to be set. This should get set + * core_dll_status to be set. This should get set * within 15 us at 200 MHz. */ rc = readl_relaxed_poll_timeout(host->ioaddr + - CORE_DLL_STATUS, + msm_offset->core_dll_status, dll_lock, (dll_lock & (CORE_DLL_LOCK | @@ -647,6 +826,8 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 config, calib_done; int ret; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); @@ -663,13 +844,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) if (ret) goto out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config &= ~CORE_CDC_T4_DLY_SEL; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG); config &= ~CORE_CDC_SWITCH_BYPASS_OFF; @@ -679,9 +860,9 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) config |= CORE_CDC_SWITCH_RC_EN; writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG); - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config &= ~CORE_START_CDC_TRAFFIC; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); /* Perform CDC Register Initialization Sequence */ @@ -733,9 +914,9 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config |= CORE_START_CDC_TRAFFIC; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); out: pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc), __func__, ret); @@ -747,32 +928,38 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; u32 dll_status, config; int ret; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); /* - * Currently the CORE_DDR_CONFIG register defaults to desired + * Currently the core_ddr_config register defaults to desired * configuration on reset. Currently reprogramming the power on * reset (POR) value in case it might have been modified by * bootloaders. In the future, if this changes, then the desired * values will need to be programmed appropriately. */ - writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG); + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + + msm_offset->core_ddr_config); if (mmc->ios.enhanced_strobe) { - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_ddr_200_cfg); config |= CORE_CMDIN_RCLK_EN; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_ddr_200_cfg); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2); config |= CORE_DDR_CAL_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config_2); - ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_DLL_STATUS, - dll_status, - (dll_status & CORE_DDR_DLL_LOCK), - 10, 1000); + ret = readl_relaxed_poll_timeout(host->ioaddr + + msm_offset->core_dll_status, + dll_status, + (dll_status & CORE_DDR_DLL_LOCK), + 10, 1000); if (ret == -ETIMEDOUT) { pr_err("%s: %s: CM_DLL_SDC4 calibration was not completed\n", @@ -780,9 +967,9 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3); config |= CORE_PWRSAVE_DLL; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC3); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec3); /* * Drain writebuffer to ensure above DLL calibration @@ -802,6 +989,8 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; int ret; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); @@ -819,9 +1008,11 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) msm_host->saved_tuning_phase); if (ret) goto out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); } if (msm_host->use_cdclp533) @@ -951,6 +1142,8 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u16 ctrl_2; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */ @@ -991,13 +1184,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, * DLL is not required for clock <= 100MHz * Thus, make sure DLL it is disabled when not required */ - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); /* * The DLL needs to be restored and CDCLP533 recalibrated @@ -1039,7 +1236,9 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); bool done = false; - u32 val; + u32 val = SWITCHABLE_SIGNALING_VOLTAGE; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", mmc_hostname(host->mmc), __func__, req_type, @@ -1048,8 +1247,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) /* * The power interrupt will not be generated for signal voltage * switches if SWITCHABLE_SIGNALING_VOLTAGE in MCI_GENERICS is not set. + * Since sdhci-msm-v5, this bit has been removed and SW must consider + * it as always set. */ - val = readl(msm_host->core_mem + CORE_MCI_GENERICS); + if (!msm_host->mci_removed) + val = msm_host_readl(msm_host, host, + msm_offset->core_generics); if ((req_type & REQ_IO_HIGH || req_type & REQ_IO_LOW) && !(val & SWITCHABLE_SIGNALING_VOLTAGE)) { return; @@ -1097,12 +1300,14 @@ static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", - mmc_hostname(host->mmc), - readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), - readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), - readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); + mmc_hostname(host->mmc), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_mask), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_ctl)); } static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) @@ -1113,11 +1318,14 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) int retry = 10; u32 pwr_state = 0, io_level = 0; u32 config; + const struct sdhci_msm_offset *msm_offset = msm_host->offset; - irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + irq_status = msm_host_readl(msm_host, host, + msm_offset->core_pwrctl_status); irq_status &= INT_MASK; - writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + msm_host_writel(msm_host, irq_status, host, + msm_offset->core_pwrctl_clear); /* * There is a rare HW scenario where the first clear pulse could be @@ -1126,8 +1334,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * sure status register is cleared. Otherwise, this will result in * a spurious power IRQ resulting in system instability. */ - while (irq_status & readl_relaxed(msm_host->core_mem + - CORE_PWRCTL_STATUS)) { + while (irq_status & msm_host_readl(msm_host, host, + msm_offset->core_pwrctl_status)) { if (retry == 0) { pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", mmc_hostname(host->mmc), irq_status); @@ -1135,8 +1343,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) WARN_ON(1); break; } - writel_relaxed(irq_status, - msm_host->core_mem + CORE_PWRCTL_CLEAR); + msm_host_writel(msm_host, irq_status, host, + msm_offset->core_pwrctl_clear); retry--; udelay(10); } @@ -1167,7 +1375,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * report back if it succeded or not to this register. The voltage * switches are handled by the sdhci core, so just report success. */ - writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + msm_host_writel(msm_host, irq_ack, host, + msm_offset->core_pwrctl_ctl); /* * If we don't have info regarding the voltage levels supported by @@ -1186,7 +1395,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * controllers with only 1.8V, we will set the IO PAD bit * without waiting for a REQ_IO_LOW. */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); new_config = config; if ((io_level & REQ_IO_HIGH) && @@ -1197,8 +1407,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) new_config |= CORE_IO_PAD_PWR_SWITCH; if (config ^ new_config) - writel_relaxed(new_config, - host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(new_config, host->ioaddr + + msm_offset->core_vendor_spec); } if (pwr_state) @@ -1359,6 +1569,7 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) struct regulator *supply = mmc->supply.vqmmc; u32 caps = 0, config; struct sdhci_host *host = mmc_priv(mmc); + const struct sdhci_msm_offset *msm_offset = msm_host->offset; if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 1700000, 1950000)) @@ -1378,7 +1589,8 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) */ u32 io_level = msm_host->curr_io_level; - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); config |= CORE_IO_PAD_PWR_SWITCH_EN; if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) @@ -1386,14 +1598,38 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT)) config |= CORE_IO_PAD_PWR_SWITCH; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, + host->ioaddr + msm_offset->core_vendor_spec); } msm_host->caps_0 |= caps; pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static const struct sdhci_msm_variant_ops mci_var_ops = { + .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_ops v5_var_ops = { + .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_mci_var = { + .mci_removed = false, + .var_ops = &mci_var_ops, + .offset = &sdhci_msm_mci_offset, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { + .mci_removed = true, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; + static const struct of_device_id sdhci_msm_dt_match[] = { - { .compatible = "qcom,sdhci-msm-v4" }, + {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, + {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, {}, }; @@ -1429,6 +1665,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) u16 host_version, core_minor; u32 core_version, config; u8 core_major; + const struct sdhci_msm_offset *msm_offset; + const struct sdhci_msm_variant_info *var_info; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) @@ -1444,6 +1682,18 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto pltfm_free; + /* + * Based on the compatible string, load the required msm host info from + * the data associated with the version info. + */ + var_info = of_device_get_match_data(&pdev->dev); + + msm_host->mci_removed = var_info->mci_removed; + msm_host->var_ops = var_info->var_ops; + msm_host->offset = var_info->offset; + + msm_offset = msm_host->offset; + sdhci_get_of_property(pdev); msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; @@ -1508,32 +1758,39 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret); } - core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); - msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); + if (!msm_host->mci_removed) { + core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + msm_host->core_mem = devm_ioremap_resource(&pdev->dev, + core_memres); - if (IS_ERR(msm_host->core_mem)) { - dev_err(&pdev->dev, "Failed to remap registers\n"); - ret = PTR_ERR(msm_host->core_mem); - goto clk_disable; + if (IS_ERR(msm_host->core_mem)) { + ret = PTR_ERR(msm_host->core_mem); + goto clk_disable; + } } /* Reset the vendor spec register to power on reset state */ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL, - host->ioaddr + CORE_VENDOR_SPEC); - - /* Set HC_MODE_EN bit in HC_MODE register */ - writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); - - config = readl_relaxed(msm_host->core_mem + CORE_HC_MODE); - config |= FF_CLK_SW_RST_DIS; - writel_relaxed(config, msm_host->core_mem + CORE_HC_MODE); + host->ioaddr + msm_offset->core_vendor_spec); + + if (!msm_host->mci_removed) { + /* Set HC_MODE_EN bit in HC_MODE register */ + msm_host_writel(msm_host, HC_MODE_EN, host, + msm_offset->core_hc_mode); + config = msm_host_readl(msm_host, host, + msm_offset->core_hc_mode); + config |= FF_CLK_SW_RST_DIS; + msm_host_writel(msm_host, config, host, + msm_offset->core_hc_mode); + } host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT)); - core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION); + core_version = msm_host_readl(msm_host, host, + msm_offset->core_mci_version); core_major = (core_version & CORE_VERSION_MAJOR_MASK) >> CORE_VERSION_MAJOR_SHIFT; core_minor = core_version & CORE_VERSION_MINOR_MASK; @@ -1558,7 +1815,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC_CAPABILITIES0); + msm_offset->core_vendor_spec_capabilities0); } /* @@ -1587,7 +1844,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) sdhci_msm_init_pwr_irq_wait(msm_host); /* Enable pwr irq interrupts */ - writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + msm_host_writel(msm_host, INT_MASK, host, + msm_offset->core_pwrctl_mask); ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index e3332a522a5d..a40bcc27f187 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -102,6 +102,9 @@ struct sdhci_arasan_data { /* Controller does not have CD wired and will not function normally without */ #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) +/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the + * internal clock even when the clock isn't stable */ +#define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1) }; static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { @@ -207,6 +210,16 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_set_clock(host, clock); + if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE) + /* + * Some controllers immediately report SDHCI_CLOCK_INT_STABLE + * after enabling the clock even though the clock is not + * stable. Trying to use a clock without waiting here results + * in EILSEQ while detecting some older/slower cards. The + * chosen delay is the maximum delay from sdhci_set_clock. + */ + msleep(20); + if (ctrl_phy) { phy_power_on(sdhci_arasan->phy); sdhci_arasan->is_phy_on = true; @@ -758,6 +771,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev) if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; + if (of_property_read_bool(np, "xlnx,int-clock-stable-broken")) + sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE; + pltfm_host->clk = clk_xin; if (of_device_is_compatible(pdev->dev.of_node, diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c new file mode 100644 index 000000000000..1b7cd144fb01 --- /dev/null +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller + * + * Copyright (C) 2018 Synaptics Incorporated + * + * Author: Jisheng Zhang <jszhang@kernel.org> + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> + +#include "sdhci-pltfm.h" + +struct dwcmshc_priv { + struct clk *bus_clk; +}; + +static const struct sdhci_ops sdhci_dwcmshc_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = sdhci_reset, +}; + +static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { + .ops = &sdhci_dwcmshc_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, +}; + +static int dwcmshc_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct dwcmshc_priv *priv; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, + sizeof(struct dwcmshc_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pltfm_host->clk)) { + err = PTR_ERR(pltfm_host->clk); + dev_err(&pdev->dev, "failed to get core clk: %d\n", err); + goto free_pltfm; + } + err = clk_prepare_enable(pltfm_host->clk); + if (err) + goto free_pltfm; + + priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (!IS_ERR(priv->bus_clk)) + clk_prepare_enable(priv->bus_clk); + + err = mmc_of_parse(host->mmc); + if (err) + goto err_clk; + + sdhci_get_of_property(pdev); + + err = sdhci_add_host(host); + if (err) + goto err_clk; + + return 0; + +err_clk: + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); +free_pltfm: + sdhci_pltfm_free(pdev); + return err; +} + +static int dwcmshc_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + + sdhci_remove_host(host, 0); + + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); + + sdhci_pltfm_free(pdev); + + return 0; +} + +static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { + { .compatible = "snps,dwcmshc-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); + +static struct platform_driver sdhci_dwcmshc_driver = { + .driver = { + .name = "sdhci-dwcmshc", + .of_match_table = sdhci_dwcmshc_dt_ids, + }, + .probe = dwcmshc_probe, + .remove = dwcmshc_remove, +}; +module_platform_driver(sdhci_dwcmshc_driver); + +MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); +MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 4ffa6b173a21..9cb7554a463d 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -22,6 +22,7 @@ #include <linux/sys_soc.h> #include <linux/clk.h> #include <linux/ktime.h> +#include <linux/dma-mapping.h> #include <linux/mmc/host.h> #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" @@ -29,11 +30,56 @@ #define VENDOR_V_22 0x12 #define VENDOR_V_23 0x13 +#define MMC_TIMING_NUM (MMC_TIMING_MMC_HS400 + 1) + +struct esdhc_clk_fixup { + const unsigned int sd_dflt_max_clk; + const unsigned int max_clk[MMC_TIMING_NUM]; +}; + +static const struct esdhc_clk_fixup ls1021a_esdhc_clk = { + .sd_dflt_max_clk = 25000000, + .max_clk[MMC_TIMING_MMC_HS] = 46500000, + .max_clk[MMC_TIMING_SD_HS] = 46500000, +}; + +static const struct esdhc_clk_fixup ls1046a_esdhc_clk = { + .sd_dflt_max_clk = 25000000, + .max_clk[MMC_TIMING_UHS_SDR104] = 167000000, + .max_clk[MMC_TIMING_MMC_HS200] = 167000000, +}; + +static const struct esdhc_clk_fixup ls1012a_esdhc_clk = { + .sd_dflt_max_clk = 25000000, + .max_clk[MMC_TIMING_UHS_SDR104] = 125000000, + .max_clk[MMC_TIMING_MMC_HS200] = 125000000, +}; + +static const struct esdhc_clk_fixup p1010_esdhc_clk = { + .sd_dflt_max_clk = 20000000, + .max_clk[MMC_TIMING_LEGACY] = 20000000, + .max_clk[MMC_TIMING_MMC_HS] = 42000000, + .max_clk[MMC_TIMING_SD_HS] = 40000000, +}; + +static const struct of_device_id sdhci_esdhc_of_match[] = { + { .compatible = "fsl,ls1021a-esdhc", .data = &ls1021a_esdhc_clk}, + { .compatible = "fsl,ls1046a-esdhc", .data = &ls1046a_esdhc_clk}, + { .compatible = "fsl,ls1012a-esdhc", .data = &ls1012a_esdhc_clk}, + { .compatible = "fsl,p1010-esdhc", .data = &p1010_esdhc_clk}, + { .compatible = "fsl,mpc8379-esdhc" }, + { .compatible = "fsl,mpc8536-esdhc" }, + { .compatible = "fsl,esdhc" }, + { } +}; +MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); + struct sdhci_esdhc { u8 vendor_ver; u8 spec_ver; bool quirk_incorrect_hostver; unsigned int peripheral_clock; + const struct esdhc_clk_fixup *clk_fixup; }; /** @@ -427,6 +473,11 @@ static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask) static int esdhc_of_enable_dma(struct sdhci_host *host) { u32 value; + struct device *dev = mmc_dev(host->mmc); + + if (of_device_is_compatible(dev->of_node, "fsl,ls1043a-esdhc") || + of_device_is_compatible(dev->of_node, "fsl,ls1046a-esdhc")) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); value = sdhci_readl(host, ESDHC_DMA_SYSCTL); value |= ESDHC_DMA_SNOOP; @@ -492,6 +543,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) int pre_div = 1; int div = 1; ktime_t timeout; + long fixup = 0; u32 temp; host->mmc->actual_clock = 0; @@ -505,27 +557,14 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) if (esdhc->vendor_ver < VENDOR_V_23) pre_div = 2; - /* - * Limit SD clock to 167MHz for ls1046a according to its datasheet - */ - if (clock > 167000000 && - of_find_compatible_node(NULL, NULL, "fsl,ls1046a-esdhc")) - clock = 167000000; + if (host->mmc->card && mmc_card_sd(host->mmc->card) && + esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY) + fixup = esdhc->clk_fixup->sd_dflt_max_clk; + else if (esdhc->clk_fixup) + fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing]; - /* - * Limit SD clock to 125MHz for ls1012a according to its datasheet - */ - if (clock > 125000000 && - of_find_compatible_node(NULL, NULL, "fsl,ls1012a-esdhc")) - clock = 125000000; - - /* Workaround to reduce the clock frequency for p1010 esdhc */ - if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { - if (clock > 20000000) - clock -= 5000000; - if (clock > 40000000) - clock -= 5000000; - } + if (fixup && clock > fixup) + clock = fixup; temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | @@ -783,6 +822,7 @@ static struct soc_device_attribute soc_incorrect_hostver[] = { static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) { + const struct of_device_id *match; struct sdhci_pltfm_host *pltfm_host; struct sdhci_esdhc *esdhc; struct device_node *np; @@ -802,6 +842,9 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) else esdhc->quirk_incorrect_hostver = false; + match = of_match_node(sdhci_esdhc_of_match, pdev->dev.of_node); + if (match) + esdhc->clk_fixup = match->data; np = pdev->dev.of_node; clk = of_clk_get(np, 0); if (!IS_ERR(clk)) { @@ -901,14 +944,6 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) return ret; } -static const struct of_device_id sdhci_esdhc_of_match[] = { - { .compatible = "fsl,mpc8379-esdhc" }, - { .compatible = "fsl,mpc8536-esdhc" }, - { .compatible = "fsl,esdhc" }, - { } -}; -MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); - static struct platform_driver sdhci_esdhc_driver = { .driver = { .name = "sdhci-esdhc", diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 77dd3521daae..7bfd366d970d 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1500,6 +1500,8 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(INTEL, CNP_EMMC, intel_glk_emmc), SDHCI_PCI_DEVICE(INTEL, CNP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, CNPH_SD, intel_byt_sd), + SDHCI_PCI_DEVICE(INTEL, ICP_EMMC, intel_glk_emmc), + SDHCI_PCI_DEVICE(INTEL, ICP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(O2, 8120, o2), SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8221, o2), @@ -1511,6 +1513,7 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(O2, SEABIRD0, o2), SDHCI_PCI_DEVICE(O2, SEABIRD1, o2), SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan), + SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps), SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd), /* Generic SD host controller */ {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)}, diff --git a/drivers/mmc/host/sdhci-pci-dwc-mshc.c b/drivers/mmc/host/sdhci-pci-dwc-mshc.c new file mode 100644 index 000000000000..f78d65448d17 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-dwc-mshc.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SDHCI driver for Synopsys DWC_MSHC controller + * + * Copyright (C) 2018 Synopsys, Inc. (www.synopsys.com) + * + * Authors: + * Prabu Thangamuthu <prabu.t@synopsys.com> + * Manjunath M B <manjumb@synopsys.com> + */ + +#include "sdhci.h" +#include "sdhci-pci.h" + +#define SDHCI_VENDOR_PTR_R 0xE8 + +/* Synopsys vendor specific registers */ +#define SDHC_GPIO_OUT 0x34 +#define SDHC_AT_CTRL_R 0x40 +#define SDHC_SW_TUNE_EN 0x00000010 + +/* MMCM DRP */ +#define SDHC_MMCM_DIV_REG 0x1020 +#define DIV_REG_100_MHZ 0x1145 +#define DIV_REG_200_MHZ 0x1083 +#define SDHC_MMCM_CLKFBOUT 0x1024 +#define CLKFBOUT_100_MHZ 0x0000 +#define CLKFBOUT_200_MHZ 0x0080 +#define SDHC_CCLK_MMCM_RST 0x00000001 + +static void sdhci_snps_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + u32 reg, vendor_ptr; + + vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R); + + /* Disable software managed rx tuning */ + reg = sdhci_readl(host, (SDHC_AT_CTRL_R + vendor_ptr)); + reg &= ~SDHC_SW_TUNE_EN; + sdhci_writel(host, reg, (SDHC_AT_CTRL_R + vendor_ptr)); + + if (clock <= 52000000) { + sdhci_set_clock(host, clock); + } else { + /* Assert reset to MMCM */ + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr)); + reg |= SDHC_CCLK_MMCM_RST; + sdhci_writel(host, reg, (SDHC_GPIO_OUT + vendor_ptr)); + + /* Configure MMCM */ + if (clock == 100000000) { + sdhci_writel(host, DIV_REG_100_MHZ, SDHC_MMCM_DIV_REG); + sdhci_writel(host, CLKFBOUT_100_MHZ, + SDHC_MMCM_CLKFBOUT); + } else { + sdhci_writel(host, DIV_REG_200_MHZ, SDHC_MMCM_DIV_REG); + sdhci_writel(host, CLKFBOUT_200_MHZ, + SDHC_MMCM_CLKFBOUT); + } + + /* De-assert reset to MMCM */ + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr)); + reg &= ~SDHC_CCLK_MMCM_RST; + sdhci_writel(host, reg, (SDHC_GPIO_OUT + vendor_ptr)); + + /* Enable clock */ + clk = SDHCI_PROG_CLOCK_MODE | SDHCI_CLOCK_INT_EN | + SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + } +} + +static const struct sdhci_ops sdhci_snps_ops = { + .set_clock = sdhci_snps_set_clock, + .enable_dma = sdhci_pci_enable_dma, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +const struct sdhci_pci_fixes sdhci_snps = { + .ops = &sdhci_snps_ops, +}; diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 555970a29c94..77e9bc4aaee9 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -3,6 +3,7 @@ * * Authors: Peter Guo <peter.guo@bayhubtech.com> * Adam Lee <adam.lee@canonical.com> + * Ernest Zhang <ernest.zhang@bayhubtech.com> * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,6 +17,9 @@ */ #include <linux/pci.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/delay.h> #include "sdhci.h" #include "sdhci-pci.h" @@ -39,6 +43,7 @@ #define O2_SD_MISC_CTRL4 0xFC #define O2_SD_TUNING_CTRL 0x300 #define O2_SD_PLL_SETTING 0x304 +#define O2_SD_MISC_SETTING 0x308 #define O2_SD_CLK_SETTING 0x328 #define O2_SD_CAP_REG2 0x330 #define O2_SD_CAP_REG0 0x334 @@ -53,6 +58,82 @@ #define O2_SD_VENDOR_SETTING 0x110 #define O2_SD_VENDOR_SETTING2 0x1C8 +#define O2_SD_HW_TUNING_DISABLE BIT(4) + +static void sdhci_o2_set_tuning_mode(struct sdhci_host *host) +{ + u16 reg; + + /* enable hardware tuning */ + reg = sdhci_readw(host, O2_SD_VENDOR_SETTING); + reg &= ~O2_SD_HW_TUNING_DISABLE; + sdhci_writew(host, reg, O2_SD_VENDOR_SETTING); +} + +static void __sdhci_o2_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int i; + + sdhci_send_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); + + for (i = 0; i < 150; i++) { + u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { + if (ctrl & SDHCI_CTRL_TUNED_CLK) { + host->tuning_done = true; + return; + } + pr_warn("%s: HW tuning failed !\n", + mmc_hostname(host->mmc)); + break; + } + + mdelay(1); + } + + pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", + mmc_hostname(host->mmc)); + sdhci_reset_tuning(host); +} + +static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + int current_bus_width = 0; + + /* + * This handler only implements the eMMC tuning that is specific to + * this controller. Fall back to the standard method for other TIMING. + */ + if (host->timing != MMC_TIMING_MMC_HS200) + return sdhci_execute_tuning(mmc, opcode); + + if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200)) + return -EINVAL; + + /* + * o2 sdhci host didn't support 8bit emmc tuning + */ + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + current_bus_width = mmc->ios.bus_width; + sdhci_set_bus_width(host, MMC_BUS_WIDTH_4); + } + + sdhci_o2_set_tuning_mode(host); + + sdhci_start_tuning(host); + + __sdhci_o2_execute_tuning(host, opcode); + + sdhci_end_tuning(host); + + if (current_bus_width == MMC_BUS_WIDTH_8) + sdhci_set_bus_width(host, current_bus_width); + + host->flags &= ~SDHCI_HS400_TUNING; + return 0; +} static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) { @@ -179,11 +260,35 @@ static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); } +static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip, + struct sdhci_host *host) +{ + int ret; + + ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI); + if (!ret) { + pr_info("%s: unsupport msi, use INTx irq\n", + mmc_hostname(host->mmc)); + return; + } + + ret = pci_alloc_irq_vectors(chip->pdev, 1, 1, + PCI_IRQ_MSI | PCI_IRQ_MSIX); + if (ret < 0) { + pr_err("%s: enable PCI MSI failed, err=%d\n", + mmc_hostname(host->mmc), ret); + return; + } + + host->irq = pci_irq_vector(chip->pdev, 0); +} + int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) { struct sdhci_pci_chip *chip; struct sdhci_host *host; u32 reg; + int ret; chip = slot->chip; host = slot->host; @@ -197,6 +302,25 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) if (reg & 0x1) host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + sdhci_pci_o2_enable_msi(chip, host); + + if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) { + ret = pci_read_config_dword(chip->pdev, + O2_SD_MISC_SETTING, ®); + if (ret) + return -EIO; + if (reg & (1 << 4)) { + pr_info("%s: emmc 1.8v flag is set, force 1.8v signaling voltage\n", + mmc_hostname(host->mmc)); + host->flags &= ~SDHCI_SIGNALING_330; + host->flags |= SDHCI_SIGNALING_180; + host->mmc->caps2 |= MMC_CAP2_NO_SD; + host->mmc->caps2 |= MMC_CAP2_NO_SDIO; + } + } + + host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning; + if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) break; /* set dll watch dog timer */ @@ -293,9 +417,8 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) /* Check Whether subId is 0x11 or 0x12 */ if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { - scratch_32 = 0x2c280000; + scratch_32 = 0x25100000; - /* Set Base Clock to 208MZ */ o2_pci_set_baseclk(chip, scratch_32); ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG4, @@ -388,7 +511,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) O2_SD_PLL_SETTING, scratch_32); } else { scratch_32 &= 0x0000FFFF; - scratch_32 |= 0x2c280000; + scratch_32 |= 0x25100000; pci_write_config_dword(chip->pdev, O2_SD_PLL_SETTING, scratch_32); diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index db9cb54ef700..2ef0bdca9197 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -48,6 +48,8 @@ #define PCI_DEVICE_ID_INTEL_CNP_EMMC 0x9dc4 #define PCI_DEVICE_ID_INTEL_CNP_SD 0x9df5 #define PCI_DEVICE_ID_INTEL_CNPH_SD 0xa375 +#define PCI_DEVICE_ID_INTEL_ICP_EMMC 0x34c4 +#define PCI_DEVICE_ID_INTEL_ICP_SD 0x34f8 #define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000 #define PCI_DEVICE_ID_VIA_95D0 0x95d0 @@ -59,6 +61,8 @@ #define PCI_VENDOR_ID_ARASAN 0x16e6 #define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670 +#define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202 + /* * PCI device class and mask */ @@ -182,5 +186,6 @@ int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip); #endif extern const struct sdhci_pci_fixes sdhci_arasan; +extern const struct sdhci_pci_fixes sdhci_snps; #endif /* __SDHCI_PCI_H */ diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 970d38f68939..908b23e6a03c 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -210,9 +210,24 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (!clock) return sdhci_set_clock(host, clock); + /* + * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI + * divider to be configured to divided the host clock by two. The SDHCI + * clock divider is calculated as part of sdhci_set_clock() by + * sdhci_calc_clk(). The divider is calculated from host->max_clk and + * the requested clock rate. + * + * By setting the host->max_clk to clock * 2 the divider calculation + * will always result in the correct value for DDR50/52 modes, + * regardless of clock rate rounding, which may happen if the value + * from clk_get_rate() is used. + */ host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; clk_set_rate(pltfm_host->clk, host_clk); - host->max_clk = clk_get_rate(pltfm_host->clk); + if (tegra_host->ddr_signaling) + host->max_clk = host_clk; + else + host->max_clk = clk_get_rate(pltfm_host->clk); sdhci_set_clock(host, clock); @@ -228,7 +243,8 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); - if (timing == MMC_TIMING_UHS_DDR50) + if (timing == MMC_TIMING_UHS_DDR50 || + timing == MMC_TIMING_MMC_DDR52) tegra_host->ddr_signaling = true; sdhci_set_uhs_signaling(host, timing); @@ -238,11 +254,7 @@ static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - /* - * DDR modes require the host to run at double the card frequency, so - * the maximum rate we can support is half of the module input clock. - */ - return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; + return clk_round_rate(pltfm_host->clk, UINT_MAX); } static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) @@ -334,7 +346,16 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_BROKEN_HS200 | + /* + * Auto-CMD23 leads to "Got command interrupt 0x00010000 even + * though no command operation was in progress." + * + * The exact reason is unknown, as the same hardware seems + * to support Auto CMD23 on a downstream 3.1 kernel. + */ + SDHCI_QUIRK2_ACMD23_BROKEN, .ops = &tegra_sdhci_ops, }; diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c index a35804b203a7..c335052d0c02 100644 --- a/drivers/mmc/host/sdhci-xenon-phy.c +++ b/drivers/mmc/host/sdhci-xenon-phy.c @@ -526,6 +526,7 @@ static bool xenon_emmc_phy_slow_mode(struct sdhci_host *host, ret = true; break; } + /* else: fall through */ default: reg &= ~XENON_TIMING_ADJUST_SLOW_MODE; ret = false; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0e9905..1b3fbd9bd5c5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1029,7 +1029,9 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, if (data == NULL) { if (host->quirks2 & SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) { - sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); + /* must not clear SDHCI_TRANSFER_MODE when tuning */ + if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) + sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); } else { /* clear Auto CMD settings for no data CMDs */ mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); @@ -2103,7 +2105,7 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) return 0; } -static void sdhci_start_tuning(struct sdhci_host *host) +void sdhci_start_tuning(struct sdhci_host *host) { u16 ctrl; @@ -2126,14 +2128,16 @@ static void sdhci_start_tuning(struct sdhci_host *host) sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); } +EXPORT_SYMBOL_GPL(sdhci_start_tuning); -static void sdhci_end_tuning(struct sdhci_host *host) +void sdhci_end_tuning(struct sdhci_host *host) { sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } +EXPORT_SYMBOL_GPL(sdhci_end_tuning); -static void sdhci_reset_tuning(struct sdhci_host *host) +void sdhci_reset_tuning(struct sdhci_host *host) { u16 ctrl; @@ -2142,6 +2146,7 @@ static void sdhci_reset_tuning(struct sdhci_host *host) ctrl &= ~SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); } +EXPORT_SYMBOL_GPL(sdhci_reset_tuning); static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode) { @@ -2162,7 +2167,7 @@ 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) +void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) { struct mmc_host *mmc = host->mmc; struct mmc_command cmd = {}; @@ -2212,6 +2217,7 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) msecs_to_jiffies(50)); } +EXPORT_SYMBOL_GPL(sdhci_send_tuning); static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) { @@ -3734,14 +3740,21 @@ int sdhci_setup_host(struct sdhci_host *host) mmc_gpio_get_cd(host->mmc) < 0) mmc->caps |= MMC_CAP_NEEDS_POLL; - /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_enable(mmc->supply.vqmmc); + + /* If vqmmc provides no 1.8V signalling, then there's no UHS */ if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, 1950000)) host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + + /* In eMMC case vqmmc might be a fixed 1.8V regulator */ + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 2700000, + 3600000)) + host->flags &= ~SDHCI_SIGNALING_330; + if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 23966f887da6..f0bd36ce3817 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -748,4 +748,9 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error, void sdhci_dumpregs(struct sdhci_host *host); +void sdhci_start_tuning(struct sdhci_host *host); +void sdhci_end_tuning(struct sdhci_host *host); +void sdhci_reset_tuning(struct sdhci_host *host); +void sdhci_send_tuning(struct sdhci_host *host, u32 opcode); + #endif /* __SDHCI_HW_H */ diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 8e7f3e35ee3d..568349e1fbc2 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1388,7 +1388,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; if (host->cfg->clk_delays || host->use_new_timings) - mmc->caps |= MMC_CAP_1_8V_DDR; + mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR; ret = mmc_of_parse(mmc); if (ret) @@ -1407,7 +1407,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) goto error_free_dma; - dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); + dev_info(&pdev->dev, "initialized, max. request size: %u KB%s\n", + mmc->max_req_size >> 10, + host->use_new_timings ? ", uses new timings mode" : ""); + return 0; error_free_dma: diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index e7d651352dc9..5d141f79e175 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -46,6 +46,7 @@ #define CTL_DMA_ENABLE 0xd8 #define CTL_RESET_SD 0xe0 #define CTL_VERSION 0xe2 +#define CTL_SDIF_MODE 0xe6 #define CTL_SDIO_REGS 0x100 #define CTL_CLK_AND_WAIT_CTL 0x138 #define CTL_RESET_SDIO 0x1e0 @@ -191,6 +192,11 @@ struct tmio_mmc_host { /* Tuning values: 1 for success, 0 for failure */ DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long)); unsigned int tap_num; + unsigned long tap_set; + + void (*prepare_hs400_tuning)(struct tmio_mmc_host *host); + void (*hs400_downgrade)(struct tmio_mmc_host *host); + void (*hs400_complete)(struct tmio_mmc_host *host); const struct tmio_mmc_dma_ops *dma_ops; }; diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 308029930304..261b4d62d2b1 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -199,6 +199,14 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, tmio_mmc_clk_stop(host); return; } + /* + * Both HS400 and HS200/SD104 set 200MHz, but some devices need to + * set 400MHz to distinguish the CPG settings in HS400. + */ + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && + host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400 && + new_clock == 200000000) + new_clock = 400000000; if (host->clk_update) clock = host->clk_update(host, new_clock) / 512; @@ -209,8 +217,13 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, clock <<= 1; /* 1/1 clock is option */ - if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && ((clk >> 22) & 0x1)) - clk |= 0xff; + if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && + ((clk >> 22) & 0x1)) { + if (!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400)) + clk |= 0xff; + else + clk &= ~0xff; + } if (host->set_clk_div) host->set_clk_div(host->pdev, (clk >> 22) & 1); @@ -309,7 +322,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, { struct mmc_data *data = host->data; int c = cmd->opcode; - u32 irq_mask = TMIO_MASK_CMD; switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: c |= RESP_NONE; break; @@ -349,7 +361,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, c |= TRANSFER_READ; } - tmio_mmc_enable_mmc_irqs(host, irq_mask); + tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_CMD); /* Fire off the command */ sd_ctrl_write32_as_16_and_16(host, CTL_ARG_REG, cmd->arg); @@ -805,8 +817,6 @@ static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) host->prepare_tuning(host, i % host->tap_num); ret = mmc_send_tuning(mmc, opcode, NULL); - if (ret && ret != -EILSEQ) - goto out; if (ret == 0) set_bit(i, host->taps); @@ -1087,6 +1097,33 @@ static int tmio_multi_io_quirk(struct mmc_card *card, return blk_size; } +static int tmio_mmc_prepare_hs400_tuning(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->prepare_hs400_tuning) + host->prepare_hs400_tuning(host); + + return 0; +} + +static void tmio_mmc_hs400_downgrade(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->hs400_downgrade) + host->hs400_downgrade(host); +} + +static void tmio_mmc_hs400_complete(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->hs400_complete) + host->hs400_complete(host); +} + static const struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, @@ -1096,6 +1133,9 @@ static const struct mmc_host_ops tmio_mmc_ops = { .multi_io_quirk = tmio_multi_io_quirk, .hw_reset = tmio_mmc_hw_reset, .execute_tuning = tmio_mmc_execute_tuning, + .prepare_hs400_tuning = tmio_mmc_prepare_hs400_tuning, + .hs400_downgrade = tmio_mmc_hs400_downgrade, + .hs400_complete = tmio_mmc_hs400_complete, }; static int tmio_mmc_init_ocr(struct tmio_mmc_host *host) |