Commit bbd60bff authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 30779715 7f38abf2
...@@ -37,6 +37,8 @@ Optional Properties: ...@@ -37,6 +37,8 @@ Optional Properties:
- xlnx,fails-without-test-cd: when present, the controller doesn't work when - xlnx,fails-without-test-cd: when present, the controller doesn't work when
the CD line is not connected properly, and the line is not connected the CD line is not connected properly, and the line is not connected
properly. Test mode can be used to force the controller to function. properly. Test mode can be used to force the controller to function.
- xlnx,int-clock-stable-broken: when present, the controller always reports
that the internal clock is stable even when it is not.
Example: Example:
sdhci@e0100000 { sdhci@e0100000 {
......
...@@ -8,10 +8,9 @@ Required properties: ...@@ -8,10 +8,9 @@ Required properties:
Optional properties: Optional properties:
- marvell,detect-delay-ms: sets the detection delay timeout in ms. - marvell,detect-delay-ms: sets the detection delay timeout in ms.
- marvell,gpio-power: GPIO spec for the card power enable pin
This file documents differences between the core properties in mmc.txt In addition to the properties described in this docuent, the details
and the properties used by the pxa-mmc driver. described in mmc.txt are supported.
Examples: Examples:
...@@ -19,6 +18,7 @@ mmc0: mmc@41100000 { ...@@ -19,6 +18,7 @@ mmc0: mmc@41100000 {
compatible = "marvell,pxa-mmc"; compatible = "marvell,pxa-mmc";
reg = <0x41100000 0x1000>; reg = <0x41100000 0x1000>;
interrupts = <23>; interrupts = <23>;
vmmc-supply = <&mmc_regulator>;
cd-gpios = <&gpio 23 0>; cd-gpios = <&gpio 23 0>;
wp-gpios = <&gpio 24 0>; wp-gpios = <&gpio 24 0>;
}; };
......
...@@ -14,6 +14,7 @@ Required Properties: ...@@ -14,6 +14,7 @@ Required Properties:
before RK3288 before RK3288
- "rockchip,rk3288-dw-mshc": for Rockchip RK3288 - "rockchip,rk3288-dw-mshc": for Rockchip RK3288
- "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108 - "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108
- "rockchip,px30-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip PX30
- "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036 - "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036
- "rockchip,rk3228-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK322x - "rockchip,rk3228-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK322x
- "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3328 - "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3328
......
...@@ -4,7 +4,12 @@ This file documents differences between the core properties in mmc.txt ...@@ -4,7 +4,12 @@ This file documents differences between the core properties in mmc.txt
and the properties used by the sdhci-msm driver. and the properties used by the sdhci-msm driver.
Required properties: Required properties:
- compatible: Should contain "qcom,sdhci-msm-v4". - compatible: Should contain:
"qcom,sdhci-msm-v4" for sdcc versions less than 5.0
"qcom,sdhci-msm-v5" for sdcc versions >= 5.0
For SDCC version 5.0.0, MCI registers are removed from SDCC
interface and some registers are moved to HC. New compatible
string is added to support this change - "qcom,sdhci-msm-v5".
- reg: Base address and length of the register in the following order: - reg: Base address and length of the register in the following order:
- Host controller register map (required) - Host controller register map (required)
- SD Core register map (required) - SD Core register map (required)
......
* Synopsys DesignWare Cores Mobile Storage Host Controller
Required properties:
- compatible: should be one of the following:
"snps,dwcmshc-sdhci"
- reg: offset and length of the register set for the device.
- interrupts: a single interrupt specifier.
- clocks: Array of clocks required for SDHCI; requires at least one for
core clock.
- clock-names: Array of names corresponding to clocks property; shall be
"core" for core clock and "bus" for optional bus clock.
Example:
sdhci2: sdhci@aa0000 {
compatible = "snps,dwcmshc-sdhci";
reg = <0xaa0000 0x1000>;
interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&emmcclk>;
bus-width = <8>;
}
...@@ -28,6 +28,7 @@ Required properties: ...@@ -28,6 +28,7 @@ Required properties:
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
"renesas,sdhi-r8a77965" - SDHI IP on R8A77965 SoC "renesas,sdhi-r8a77965" - SDHI IP on R8A77965 SoC
"renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC "renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC
"renesas,sdhi-r8a77990" - SDHI IP on R8A77990 SoC
"renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC "renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC
"renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller "renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller
"renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller "renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller
......
...@@ -12916,6 +12916,13 @@ S: Maintained ...@@ -12916,6 +12916,13 @@ S: Maintained
F: drivers/mmc/host/sdhci* F: drivers/mmc/host/sdhci*
F: include/linux/mmc/sdhci* F: include/linux/mmc/sdhci*
SYNOPSYS SDHCI COMPLIANT DWC MSHC DRIVER
M: Prabu Thangamuthu <prabu.t@synopsys.com>
M: Manjunath M B <manjumb@synopsys.com>
L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/sdhci-pci-dwc-mshc.c
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
M: Ben Dooks <ben-linux@fluff.org> M: Ben Dooks <ben-linux@fluff.org>
M: Jaehoon Chung <jh80.chung@samsung.com> M: Jaehoon Chung <jh80.chung@samsung.com>
......
...@@ -2078,7 +2078,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, ...@@ -2078,7 +2078,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
/* Do not retry else we can't see errors */ /* Do not retry else we can't see errors */
err = mmc_wait_for_cmd(card->host, &cmd, 0); 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", pr_err("error %d requesting status %#x\n",
err, cmd.resp[0]); err, cmd.resp[0]);
err = -EIO; err = -EIO;
...@@ -2716,52 +2716,6 @@ void mmc_stop_host(struct mmc_host *host) ...@@ -2716,52 +2716,6 @@ void mmc_stop_host(struct mmc_host *host)
mmc_release_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 #ifdef CONFIG_PM_SLEEP
/* Do the card removal on suspend if card is assumed removeable /* 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 * Do that in pm notifier while userspace isn't yet frozen, so we will be able
......
...@@ -28,8 +28,6 @@ struct mmc_bus_ops { ...@@ -28,8 +28,6 @@ struct mmc_bus_ops {
int (*resume)(struct mmc_host *); int (*resume)(struct mmc_host *);
int (*runtime_suspend)(struct mmc_host *); int (*runtime_suspend)(struct mmc_host *);
int (*runtime_resume)(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 (*alive)(struct mmc_host *);
int (*shutdown)(struct mmc_host *); int (*shutdown)(struct mmc_host *);
int (*hw_reset)(struct mmc_host *); int (*hw_reset)(struct mmc_host *);
......
...@@ -1169,6 +1169,10 @@ static int mmc_select_hs400(struct mmc_card *card) ...@@ -1169,6 +1169,10 @@ static int mmc_select_hs400(struct mmc_card *card)
/* Set host controller to HS timing */ /* Set host controller to HS timing */
mmc_set_timing(card->host, MMC_TIMING_MMC_HS); 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 */ /* Reduce frequency to HS frequency */
max_dtr = card->ext_csd.hs_max_dtr; max_dtr = card->ext_csd.hs_max_dtr;
mmc_set_clock(host, max_dtr); mmc_set_clock(host, max_dtr);
...@@ -1209,6 +1213,9 @@ static int mmc_select_hs400(struct mmc_card *card) ...@@ -1209,6 +1213,9 @@ static int mmc_select_hs400(struct mmc_card *card)
if (err) if (err)
goto out_err; goto out_err;
if (host->ops->hs400_complete)
host->ops->hs400_complete(host);
return 0; return 0;
out_err: out_err:
...@@ -1256,6 +1263,9 @@ int mmc_hs400_to_hs200(struct mmc_card *card) ...@@ -1256,6 +1263,9 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS); mmc_set_timing(host, MMC_TIMING_MMC_HS);
if (host->ops->hs400_downgrade)
host->ops->hs400_downgrade(host);
err = mmc_switch_status(card); err = mmc_switch_status(card);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1338,8 +1348,12 @@ static int mmc_select_hs400es(struct mmc_card *card) ...@@ -1338,8 +1348,12 @@ static int mmc_select_hs400es(struct mmc_card *card)
goto out_err; goto out_err;
err = mmc_select_bus_width(card); 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; goto out_err;
}
/* Switch card to HS mode */ /* Switch card to HS mode */
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
......
...@@ -417,7 +417,7 @@ static int mmc_switch_status_error(struct mmc_host *host, u32 status) ...@@ -417,7 +417,7 @@ static int mmc_switch_status_error(struct mmc_host *host, u32 status)
if (status & R1_SPI_ILLEGAL_COMMAND) if (status & R1_SPI_ILLEGAL_COMMAND)
return -EBADMSG; return -EBADMSG;
} else { } else {
if (status & 0xFDFFA000) if (R1_STATUS(status))
pr_warn("%s: unexpected status %#x after switch\n", pr_warn("%s: unexpected status %#x after switch\n",
mmc_hostname(host), status); mmc_hostname(host), status);
if (status & R1_SWITCH_ERROR) if (status & R1_SWITCH_ERROR)
......
...@@ -1076,7 +1076,6 @@ static const struct mmc_bus_ops mmc_sdio_ops = { ...@@ -1076,7 +1076,6 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
.resume = mmc_sdio_resume, .resume = mmc_sdio_resume,
.runtime_suspend = mmc_sdio_runtime_suspend, .runtime_suspend = mmc_sdio_runtime_suspend,
.runtime_resume = mmc_sdio_runtime_resume, .runtime_resume = mmc_sdio_runtime_resume,
.power_restore = mmc_sdio_power_restore,
.alive = mmc_sdio_alive, .alive = mmc_sdio_alive,
.hw_reset = mmc_sdio_hw_reset, .hw_reset = mmc_sdio_hw_reset,
.sw_reset = mmc_sdio_sw_reset, .sw_reset = mmc_sdio_sw_reset,
......
...@@ -176,6 +176,17 @@ config MMC_SDHCI_OF_HLWD ...@@ -176,6 +176,17 @@ config MMC_SDHCI_OF_HLWD
If unsure, say N. 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 config MMC_SDHCI_CADENCE
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
depends on MMC_SDHCI_PLTFM depends on MMC_SDHCI_PLTFM
......
...@@ -11,7 +11,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o ...@@ -11,7 +11,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.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-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
...@@ -82,6 +83,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.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_AT91) += sdhci-of-at91.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.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_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_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
......
...@@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) ...@@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev)
return ret; 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 * dw_mci_exynos_resume_noirq - Exynos-specific resume code
...@@ -186,12 +200,16 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) ...@@ -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. * We run this code on all exynos variants because it doesn't hurt.
*/ */
static int dw_mci_exynos_resume_noirq(struct device *dev) static int dw_mci_exynos_resume_noirq(struct device *dev)
{ {
struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci *host = dev_get_drvdata(dev);
struct dw_mci_exynos_priv_data *priv = host->priv; struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel; u32 clksel;
int ret;
ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
...@@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) ...@@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
mci_writel(host, CLKSEL, clksel); mci_writel(host, CLKSEL, clksel);
} }
pm_runtime_put(dev);
return 0; return 0;
} }
#else #endif /* CONFIG_PM_SLEEP */
#define dw_mci_exynos_resume_noirq NULL
#endif /* CONFIG_PM */
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) 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) ...@@ -553,14 +571,11 @@ static int dw_mci_exynos_remove(struct platform_device *pdev)
} }
static const struct dev_pm_ops dw_mci_exynos_pmops = { static const struct dev_pm_ops dw_mci_exynos_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
pm_runtime_force_resume) dw_mci_exynos_resume_noirq)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_exynos_runtime_resume, dw_mci_exynos_runtime_resume,
NULL) 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 = { static struct platform_driver dw_mci_exynos_pltfm_driver = {
......
...@@ -48,78 +48,6 @@ ...@@ -48,78 +48,6 @@
static unsigned int fmax = 515633; 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 = { static struct variant_data variant_arm = {
.fifosize = 16 * 4, .fifosize = 16 * 4,
.fifohalfsize = 8 * 4, .fifohalfsize = 8 * 4,
...@@ -280,6 +208,7 @@ static struct variant_data variant_qcom = { ...@@ -280,6 +208,7 @@ static struct variant_data variant_qcom = {
.mmcimask1 = true, .mmcimask1 = true,
.start_err = MCI_STARTBITERR, .start_err = MCI_STARTBITERR,
.opendrain = MCI_ROD, .opendrain = MCI_ROD,
.init = qcom_variant_init,
}; };
/* Busy detection for the ST Micro variant */ /* Busy detection for the ST Micro variant */
...@@ -489,7 +418,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) ...@@ -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) static void mmci_dma_setup(struct mmci_host *host)
{ {
const char *rxname, *txname; 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_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); 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) ...@@ -537,9 +465,8 @@ static void mmci_dma_setup(struct mmci_host *host)
host->mmc->max_seg_size = max_seg_size; host->mmc->max_seg_size = max_seg_size;
} }
if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel) if (host->ops && host->ops->dma_setup)
if (dml_hw_init(host, host->mmc->parent->of_node)) host->ops->dma_setup(host);
variant->qcom_dml = false;
} }
/* /*
...@@ -1706,6 +1633,9 @@ static int mmci_probe(struct amba_device *dev, ...@@ -1706,6 +1633,9 @@ static int mmci_probe(struct amba_device *dev,
goto clk_disable; goto clk_disable;
} }
if (variant->init)
variant->init(host);
/* /*
* The ARM and ST versions of the block have slightly different * The ARM and ST versions of the block have slightly different
* clock divider equations which means that the minimum divider * clock divider equations which means that the minimum divider
......
...@@ -195,8 +195,86 @@ ...@@ -195,8 +195,86 @@
#define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain" #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
struct clk; struct clk;
struct variant_data;
struct dma_chan; 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 mmci_host_next {
struct dma_async_tx_descriptor *dma_desc; struct dma_async_tx_descriptor *dma_desc;
...@@ -228,6 +306,7 @@ struct mmci_host { ...@@ -228,6 +306,7 @@ struct mmci_host {
u32 mask1_reg; u32 mask1_reg;
bool vqmmc_enabled; bool vqmmc_enabled;
struct mmci_platform_data *plat; struct mmci_platform_data *plat;
struct mmci_host_ops *ops;
struct variant_data *variant; struct variant_data *variant;
struct pinctrl *pinctrl; struct pinctrl *pinctrl;
struct pinctrl_state *pins_default; struct pinctrl_state *pins_default;
......
...@@ -119,17 +119,20 @@ static int of_get_dml_pipe_index(struct device_node *np, const char *name) ...@@ -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 */ /* 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; u32 config;
void __iomem *base; void __iomem *base;
int consumer_id, producer_id; int consumer_id, producer_id;
struct device_node *np = host->mmc->parent->of_node;
consumer_id = of_get_dml_pipe_index(np, "tx"); consumer_id = of_get_dml_pipe_index(np, "tx");
producer_id = of_get_dml_pipe_index(np, "rx"); producer_id = of_get_dml_pipe_index(np, "rx");
if (producer_id < 0 || consumer_id < 0) if (producer_id < 0 || consumer_id < 0) {
return -ENODEV; host->variant->qcom_dml = false;
return;
}
base = host->base + DML_OFFSET; base = host->base + DML_OFFSET;
...@@ -172,6 +175,13 @@ int dml_hw_init(struct mmci_host *host, struct device_node *np) ...@@ -172,6 +175,13 @@ int dml_hw_init(struct mmci_host *host, struct device_node *np)
/* Make sure dml initialization is finished */ /* Make sure dml initialization is finished */
mb(); 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;
} }
...@@ -16,12 +16,11 @@ ...@@ -16,12 +16,11 @@
#define __MMC_QCOM_DML_H__ #define __MMC_QCOM_DML_H__
#ifdef CONFIG_MMC_QCOM_DML #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); void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
#else #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) static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
{ {
......
...@@ -58,11 +58,11 @@ struct pxamci_host { ...@@ -58,11 +58,11 @@ struct pxamci_host {
void __iomem *base; void __iomem *base;
struct clk *clk; struct clk *clk;
unsigned long clkrate; unsigned long clkrate;
int irq;
unsigned int clkrt; unsigned int clkrt;
unsigned int cmdat; unsigned int cmdat;
unsigned int imask; unsigned int imask;
unsigned int power_mode; unsigned int power_mode;
unsigned long detect_delay_ms;
struct pxamci_platform_data *pdata; struct pxamci_platform_data *pdata;
struct mmc_request *mrq; struct mmc_request *mrq;
...@@ -72,64 +72,48 @@ struct pxamci_host { ...@@ -72,64 +72,48 @@ struct pxamci_host {
struct dma_chan *dma_chan_rx; struct dma_chan *dma_chan_rx;
struct dma_chan *dma_chan_tx; struct dma_chan *dma_chan_tx;
dma_cookie_t dma_cookie; dma_cookie_t dma_cookie;
dma_addr_t sg_dma;
unsigned int dma_len; unsigned int dma_len;
unsigned int dma_dir; 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 struct mmc_host *mmc = host->mmc;
host->vcc = devm_regulator_get_optional(mmc_dev(host->mmc), "vmmc"); int ret;
if (IS_ERR(host->vcc)) ret = mmc_regulator_get_supply(mmc);
host->vcc = NULL; if (ret < 0)
else { return ret;
host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
if (host->pdata && host->pdata->ocr_mask) if (IS_ERR(mmc->supply.vmmc)) {
dev_warn(mmc_dev(host->mmc),
"ocr_mask/setpower will not be used\n");
}
#endif
if (host->vcc == NULL) {
/* fall-back to platform data */ /* fall-back to platform data */
host->mmc->ocr_avail = host->pdata ? mmc->ocr_avail = host->pdata ?
host->pdata->ocr_mask : host->pdata->ocr_mask :
MMC_VDD_32_33 | MMC_VDD_33_34; MMC_VDD_32_33 | MMC_VDD_33_34;
} }
return 0;
} }
static inline int pxamci_set_power(struct pxamci_host *host, static inline int pxamci_set_power(struct pxamci_host *host,
unsigned char power_mode, unsigned char power_mode,
unsigned int vdd) unsigned int vdd)
{ {
struct mmc_host *mmc = host->mmc;
struct regulator *supply = mmc->supply.vmmc;
int on; int on;
if (host->vcc) { if (!IS_ERR(supply))
int ret; return mmc_regulator_set_ocr(mmc, supply, vdd);
if (power_mode == MMC_POWER_UP) { if (host->pdata &&
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 &&
gpio_is_valid(host->pdata->gpio_power)) { gpio_is_valid(host->pdata->gpio_power)) {
on = ((1 << vdd) & host->pdata->ocr_mask); on = ((1 << vdd) & host->pdata->ocr_mask);
gpio_set_value(host->pdata->gpio_power, gpio_set_value(host->pdata->gpio_power,
!!on ^ host->pdata->gpio_power_invert); !!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 host->pdata->setpower(mmc_dev(host->mmc), vdd);
return 0; return 0;
...@@ -584,7 +568,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) ...@@ -584,7 +568,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid)
{ {
struct pxamci_host *host = mmc_priv(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; return IRQ_HANDLED;
} }
...@@ -596,37 +580,30 @@ static const struct of_device_id pxa_mmc_dt_ids[] = { ...@@ -596,37 +580,30 @@ static const struct of_device_id pxa_mmc_dt_ids[] = {
MODULE_DEVICE_TABLE(of, 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 device_node *np = pdev->dev.of_node;
struct pxamci_platform_data *pdata; struct pxamci_host *host = mmc_priv(mmc);
u32 tmp; u32 tmp;
int ret;
if (!np)
return 0;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->gpio_card_detect = if (!np)
of_get_named_gpio(np, "cd-gpios", 0); return 0;
pdata->gpio_card_ro =
of_get_named_gpio(np, "wp-gpios", 0);
/* pxa-mmc specific */ /* 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) 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 #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; return 0;
} }
...@@ -636,19 +613,16 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -636,19 +613,16 @@ static int pxamci_probe(struct platform_device *pdev)
{ {
struct mmc_host *mmc; struct mmc_host *mmc;
struct pxamci_host *host = NULL; struct pxamci_host *host = NULL;
struct device *dev = &pdev->dev;
struct resource *r; struct resource *r;
int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; int ret, irq;
ret = pxamci_of_init(pdev);
if (ret)
return ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0)
return irq; return irq;
mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev);
if (!mmc) { if (!mmc) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
...@@ -677,12 +651,16 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -677,12 +651,16 @@ static int pxamci_probe(struct platform_device *pdev)
*/ */
mmc->max_blk_count = 65535; mmc->max_blk_count = 65535;
ret = pxamci_of_init(pdev, mmc);
if (ret)
return ret;
host = mmc_priv(mmc); host = mmc_priv(mmc);
host->mmc = mmc; host->mmc = mmc;
host->pdata = pdev->dev.platform_data; host->pdata = pdev->dev.platform_data;
host->clkrt = CLKRT_OFF; host->clkrt = CLKRT_OFF;
host->clk = devm_clk_get(&pdev->dev, NULL); host->clk = devm_clk_get(dev, NULL);
if (IS_ERR(host->clk)) { if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk); ret = PTR_ERR(host->clk);
host->clk = NULL; host->clk = NULL;
...@@ -697,7 +675,9 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -697,7 +675,9 @@ static int pxamci_probe(struct platform_device *pdev)
mmc->f_min = (host->clkrate + 63) / 64; mmc->f_min = (host->clkrate + 63) / 64;
mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate; 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; mmc->caps = 0;
host->cmdat = 0; host->cmdat = 0;
...@@ -711,10 +691,9 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -711,10 +691,9 @@ static int pxamci_probe(struct platform_device *pdev)
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
host->res = r; host->res = r;
host->irq = irq;
host->imask = MMC_I_MASK_ALL; 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)) { if (IS_ERR(host->base)) {
ret = PTR_ERR(host->base); ret = PTR_ERR(host->base);
goto out; goto out;
...@@ -729,69 +708,76 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -729,69 +708,76 @@ static int pxamci_probe(struct platform_device *pdev)
writel(64, host->base + MMC_RESTO); writel(64, host->base + MMC_RESTO);
writel(host->imask, host->base + MMC_I_MASK); 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); DRIVER_NAME, host);
if (ret) if (ret)
goto out; goto out;
platform_set_drvdata(pdev, mmc); 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) { 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; ret = -ENODEV;
goto out; 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) { 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; ret = -ENODEV;
goto out; goto out;
} }
if (host->pdata) { if (host->pdata) {
gpio_cd = host->pdata->gpio_card_detect; int gpio_cd = host->pdata->gpio_card_detect;
gpio_ro = host->pdata->gpio_card_ro; int gpio_ro = host->pdata->gpio_card_ro;
gpio_power = host->pdata->gpio_power; int gpio_power = host->pdata->gpio_power;
}
if (gpio_is_valid(gpio_power)) { host->detect_delay_ms = host->pdata->detect_delay_ms;
ret = devm_gpio_request(&pdev->dev, gpio_power,
"mmc card power"); if (gpio_is_valid(gpio_power)) {
if (ret) { ret = devm_gpio_request(dev, gpio_power,
dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", "mmc card power");
gpio_power); if (ret) {
goto out; 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)) { if (ret) {
ret = mmc_gpio_request_ro(mmc, gpio_ro); 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) { if (ret) {
dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", dev_err(dev, "Failed requesting gpio_cd %d\n",
gpio_ro); gpio_cd);
goto out; goto out;
} else {
mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
0 : MMC_CAP2_RO_ACTIVE_HIGH;
} }
}
if (gpio_is_valid(gpio_cd)) if (host->pdata->init)
ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); host->pdata->init(dev, pxamci_detect_irq, mmc);
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 (gpio_is_valid(gpio_power) && host->pdata->setpower) if (gpio_is_valid(gpio_power) && host->pdata->setpower)
dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); dev_warn(dev, "gpio_power and setpower() both defined\n");
if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) if (gpio_is_valid(gpio_ro) && host->pdata->get_ro)
dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); dev_warn(dev, "gpio_ro and get_ro() both defined\n");
}
mmc_add_host(mmc); mmc_add_host(mmc);
...@@ -812,18 +798,12 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -812,18 +798,12 @@ static int pxamci_probe(struct platform_device *pdev)
static int pxamci_remove(struct platform_device *pdev) static int pxamci_remove(struct platform_device *pdev)
{ {
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
int gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
if (mmc) { if (mmc) {
struct pxamci_host *host = mmc_priv(mmc); struct pxamci_host *host = mmc_priv(mmc);
mmc_remove_host(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) if (host->pdata && host->pdata->exit)
host->pdata->exit(&pdev->dev, mmc); host->pdata->exit(&pdev->dev, mmc);
...@@ -839,6 +819,7 @@ static int pxamci_remove(struct platform_device *pdev) ...@@ -839,6 +819,7 @@ static int pxamci_remove(struct platform_device *pdev)
mmc_free_host(mmc); mmc_free_host(mmc);
} }
return 0; return 0;
} }
......
...@@ -212,6 +212,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, ...@@ -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_CKSEL 0x006
#define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008 #define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008
#define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A #define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A
#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E
/* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */ /* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */
#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0) #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, ...@@ -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) #define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN BIT(0)
/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */ /* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */
#define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) #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, static inline u32 sd_scc_read32(struct tmio_mmc_host *host,
struct renesas_sdhi *priv, int addr) struct renesas_sdhi *priv, int addr)
...@@ -244,33 +248,30 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host) ...@@ -244,33 +248,30 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host)
priv = host_to_priv(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 */ /* Initialize SCC */
sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x0); 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_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); 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, sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); 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, sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos); 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 */ /* Read TAPNUM */
return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >> return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >>
SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) &
...@@ -286,13 +287,95 @@ static void renesas_sdhi_prepare_tuning(struct tmio_mmc_host *host, ...@@ -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); 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 #define SH_MOBILE_SDHI_MAX_TAP 3
static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
{ {
struct renesas_sdhi *priv = host_to_priv(host); struct renesas_sdhi *priv = host_to_priv(host);
unsigned long tap_cnt; /* counter of tuning success */ 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_start;/* start position of tuning success */
unsigned long tap_end; /* end position of tuning success */ unsigned long tap_end; /* end position of tuning success */
unsigned long ntap; /* temporary counter of tuning success */ unsigned long ntap; /* temporary counter of tuning success */
...@@ -301,6 +384,18 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) ...@@ -301,6 +384,18 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
/* Clear SCC_RVSREQ */ /* Clear SCC_RVSREQ */
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); 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 * Find the longest consecutive run of successful probes. If that
* is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
...@@ -330,12 +425,12 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) ...@@ -330,12 +425,12 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
} }
if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP) 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 else
return -EIO; return -EIO;
/* Set SCC */ /* 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 */ /* Enable auto re-tuning */
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, 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) ...@@ -368,13 +463,8 @@ static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host)
priv = host_to_priv(host); priv = host_to_priv(host);
/* Reset SCC */ renesas_sdhi_reset_scc(host, priv);
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & renesas_sdhi_reset_hs400_mode(host, priv);
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));
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
...@@ -592,7 +682,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, ...@@ -592,7 +682,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
/* Enable tuning iff we have an SCC and a supported mode */ /* Enable tuning iff we have an SCC and a supported mode */
if (of_data && of_data->scc_offset && if (of_data && of_data->scc_offset &&
(host->mmc->caps & MMC_CAP_UHS_SDR104 || (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; const struct renesas_sdhi_scc *taps = of_data->taps;
bool hit = false; bool hit = false;
...@@ -616,6 +707,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, ...@@ -616,6 +707,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
host->select_tuning = renesas_sdhi_select_tuning; host->select_tuning = renesas_sdhi_select_tuning;
host->check_scc_error = renesas_sdhi_check_scc_error; host->check_scc_error = renesas_sdhi_check_scc_error;
host->hw_reset = renesas_sdhi_hw_reset; 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; i = 0;
......
...@@ -82,6 +82,22 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { ...@@ -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 = { static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
...@@ -98,8 +114,8 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { ...@@ -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[] = { static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, },
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
{}, {},
}; };
......
...@@ -78,6 +78,19 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { ...@@ -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 = { static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
...@@ -104,8 +117,8 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { ...@@ -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-r8a7792", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7793", .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-r8a7794", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, },
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, },
{ .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, },
{ .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Freescale eSDHC i.MX controller driver for the platform bus. * Freescale eSDHC i.MX controller driver for the platform bus.
* *
...@@ -5,10 +6,6 @@ ...@@ -5,10 +6,6 @@
* *
* Copyright (c) 2010 Pengutronix e.K. * Copyright (c) 2010 Pengutronix e.K.
* Author: Wolfram Sang <kernel@pengutronix.de> * 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> #include <linux/io.h>
...@@ -708,14 +705,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, ...@@ -708,14 +705,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
int div = 1; int div = 1;
u32 temp, val; 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) { if (clock == 0) {
host->mmc->actual_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; return;
} }
...@@ -761,7 +758,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, ...@@ -761,7 +758,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
if (esdhc_is_usdhc(imx_data)) { if (esdhc_is_usdhc(imx_data)) {
val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
host->ioaddr + ESDHC_VENDOR_SPEC); host->ioaddr + ESDHC_VENDOR_SPEC);
} }
mdelay(1); mdelay(1);
...@@ -1151,18 +1148,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, ...@@ -1151,18 +1148,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
&boarddata->tuning_start_tap); &boarddata->tuning_start_tap);
if (of_find_property(np, "no-1-8-v", NULL)) if (of_find_property(np, "no-1-8-v", NULL))
boarddata->support_vsel = false; host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
else
boarddata->support_vsel = true;
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
boarddata->delay_line = 0; boarddata->delay_line = 0;
mmc_of_parse_voltage(np, &host->ocr_mask); mmc_of_parse_voltage(np, &host->ocr_mask);
/* sdr50 and sdr104 need work on 1.8v signal voltage */ if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pins_default)) {
if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) &&
!IS_ERR(imx_data->pins_default)) {
imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
ESDHC_PINCTRL_STATE_100MHZ); ESDHC_PINCTRL_STATE_100MHZ);
imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
...@@ -1318,7 +1311,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) ...@@ -1318,7 +1311,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (esdhc_is_usdhc(imx_data)) { if (esdhc_is_usdhc(imx_data)) {
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 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)) if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
......
This diff is collapsed.
...@@ -102,6 +102,9 @@ struct sdhci_arasan_data { ...@@ -102,6 +102,9 @@ struct sdhci_arasan_data {
/* Controller does not have CD wired and will not function normally without */ /* Controller does not have CD wired and will not function normally without */
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) #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 = { 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) ...@@ -207,6 +210,16 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_set_clock(host, 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) { if (ctrl_phy) {
phy_power_on(sdhci_arasan->phy); phy_power_on(sdhci_arasan->phy);
sdhci_arasan->is_phy_on = true; sdhci_arasan->is_phy_on = true;
...@@ -758,6 +771,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev) ...@@ -758,6 +771,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) if (of_property_read_bool(np, "xlnx,fails-without-test-cd"))
sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; 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; pltfm_host->clk = clk_xin;
if (of_device_is_compatible(pdev->dev.of_node, if (of_device_is_compatible(pdev->dev.of_node,
......
// 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");
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/sys_soc.h> #include <linux/sys_soc.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "sdhci-esdhc.h" #include "sdhci-esdhc.h"
...@@ -29,11 +30,56 @@ ...@@ -29,11 +30,56 @@
#define VENDOR_V_22 0x12 #define VENDOR_V_22 0x12
#define VENDOR_V_23 0x13 #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 { struct sdhci_esdhc {
u8 vendor_ver; u8 vendor_ver;
u8 spec_ver; u8 spec_ver;
bool quirk_incorrect_hostver; bool quirk_incorrect_hostver;
unsigned int peripheral_clock; 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) ...@@ -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) static int esdhc_of_enable_dma(struct sdhci_host *host)
{ {
u32 value; 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 = sdhci_readl(host, ESDHC_DMA_SYSCTL);
value |= ESDHC_DMA_SNOOP; value |= ESDHC_DMA_SNOOP;
...@@ -492,6 +543,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -492,6 +543,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
int pre_div = 1; int pre_div = 1;
int div = 1; int div = 1;
ktime_t timeout; ktime_t timeout;
long fixup = 0;
u32 temp; u32 temp;
host->mmc->actual_clock = 0; host->mmc->actual_clock = 0;
...@@ -505,27 +557,14 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -505,27 +557,14 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
if (esdhc->vendor_ver < VENDOR_V_23) if (esdhc->vendor_ver < VENDOR_V_23)
pre_div = 2; pre_div = 2;
/* if (host->mmc->card && mmc_card_sd(host->mmc->card) &&
* Limit SD clock to 167MHz for ls1046a according to its datasheet esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
*/ fixup = esdhc->clk_fixup->sd_dflt_max_clk;
if (clock > 167000000 && else if (esdhc->clk_fixup)
of_find_compatible_node(NULL, NULL, "fsl,ls1046a-esdhc")) fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
clock = 167000000;
/* if (fixup && clock > fixup)
* Limit SD clock to 125MHz for ls1012a according to its datasheet clock = fixup;
*/
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;
}
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
...@@ -783,6 +822,7 @@ static struct soc_device_attribute soc_incorrect_hostver[] = { ...@@ -783,6 +822,7 @@ static struct soc_device_attribute soc_incorrect_hostver[] = {
static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) 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_pltfm_host *pltfm_host;
struct sdhci_esdhc *esdhc; struct sdhci_esdhc *esdhc;
struct device_node *np; struct device_node *np;
...@@ -802,6 +842,9 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) ...@@ -802,6 +842,9 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
else else
esdhc->quirk_incorrect_hostver = false; 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; np = pdev->dev.of_node;
clk = of_clk_get(np, 0); clk = of_clk_get(np, 0);
if (!IS_ERR(clk)) { if (!IS_ERR(clk)) {
...@@ -901,14 +944,6 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) ...@@ -901,14 +944,6 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
return ret; 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 = { static struct platform_driver sdhci_esdhc_driver = {
.driver = { .driver = {
.name = "sdhci-esdhc", .name = "sdhci-esdhc",
......
...@@ -1500,6 +1500,8 @@ static const struct pci_device_id pci_ids[] = { ...@@ -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_EMMC, intel_glk_emmc),
SDHCI_PCI_DEVICE(INTEL, CNP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, CNP_SD, intel_byt_sd),
SDHCI_PCI_DEVICE(INTEL, CNPH_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, 8120, o2),
SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8220, o2),
SDHCI_PCI_DEVICE(O2, 8221, o2), SDHCI_PCI_DEVICE(O2, 8221, o2),
...@@ -1511,6 +1513,7 @@ static const struct pci_device_id pci_ids[] = { ...@@ -1511,6 +1513,7 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(O2, SEABIRD0, o2), SDHCI_PCI_DEVICE(O2, SEABIRD0, o2),
SDHCI_PCI_DEVICE(O2, SEABIRD1, o2), SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan), 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), SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
/* Generic SD host controller */ /* Generic SD host controller */
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)}, {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
......
// 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,
};
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Authors: Peter Guo <peter.guo@bayhubtech.com> * Authors: Peter Guo <peter.guo@bayhubtech.com>
* Adam Lee <adam.lee@canonical.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 * This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and * License version 2, as published by the Free Software Foundation, and
...@@ -16,6 +17,9 @@ ...@@ -16,6 +17,9 @@
*/ */
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/delay.h>
#include "sdhci.h" #include "sdhci.h"
#include "sdhci-pci.h" #include "sdhci-pci.h"
...@@ -39,6 +43,7 @@ ...@@ -39,6 +43,7 @@
#define O2_SD_MISC_CTRL4 0xFC #define O2_SD_MISC_CTRL4 0xFC
#define O2_SD_TUNING_CTRL 0x300 #define O2_SD_TUNING_CTRL 0x300
#define O2_SD_PLL_SETTING 0x304 #define O2_SD_PLL_SETTING 0x304
#define O2_SD_MISC_SETTING 0x308
#define O2_SD_CLK_SETTING 0x328 #define O2_SD_CLK_SETTING 0x328
#define O2_SD_CAP_REG2 0x330 #define O2_SD_CAP_REG2 0x330
#define O2_SD_CAP_REG0 0x334 #define O2_SD_CAP_REG0 0x334
...@@ -53,6 +58,82 @@ ...@@ -53,6 +58,82 @@
#define O2_SD_VENDOR_SETTING 0x110 #define O2_SD_VENDOR_SETTING 0x110
#define O2_SD_VENDOR_SETTING2 0x1C8 #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) 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) ...@@ -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); 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) int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
{ {
struct sdhci_pci_chip *chip; struct sdhci_pci_chip *chip;
struct sdhci_host *host; struct sdhci_host *host;
u32 reg; u32 reg;
int ret;
chip = slot->chip; chip = slot->chip;
host = slot->host; host = slot->host;
...@@ -197,6 +302,25 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) ...@@ -197,6 +302,25 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
if (reg & 0x1) if (reg & 0x1)
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; 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, &reg);
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) if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2)
break; break;
/* set dll watch dog timer */ /* set dll watch dog timer */
...@@ -293,9 +417,8 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) ...@@ -293,9 +417,8 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
/* Check Whether subId is 0x11 or 0x12 */ /* Check Whether subId is 0x11 or 0x12 */
if ((scratch_32 == 0x11) || (scratch_32 == 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); o2_pci_set_baseclk(chip, scratch_32);
ret = pci_read_config_dword(chip->pdev, ret = pci_read_config_dword(chip->pdev,
O2_SD_FUNC_REG4, O2_SD_FUNC_REG4,
...@@ -388,7 +511,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) ...@@ -388,7 +511,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
O2_SD_PLL_SETTING, scratch_32); O2_SD_PLL_SETTING, scratch_32);
} else { } else {
scratch_32 &= 0x0000FFFF; scratch_32 &= 0x0000FFFF;
scratch_32 |= 0x2c280000; scratch_32 |= 0x25100000;
pci_write_config_dword(chip->pdev, pci_write_config_dword(chip->pdev,
O2_SD_PLL_SETTING, scratch_32); O2_SD_PLL_SETTING, scratch_32);
......
...@@ -48,6 +48,8 @@ ...@@ -48,6 +48,8 @@
#define PCI_DEVICE_ID_INTEL_CNP_EMMC 0x9dc4 #define PCI_DEVICE_ID_INTEL_CNP_EMMC 0x9dc4
#define PCI_DEVICE_ID_INTEL_CNP_SD 0x9df5 #define PCI_DEVICE_ID_INTEL_CNP_SD 0x9df5
#define PCI_DEVICE_ID_INTEL_CNPH_SD 0xa375 #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_SYSKONNECT_8000 0x8000
#define PCI_DEVICE_ID_VIA_95D0 0x95d0 #define PCI_DEVICE_ID_VIA_95D0 0x95d0
...@@ -59,6 +61,8 @@ ...@@ -59,6 +61,8 @@
#define PCI_VENDOR_ID_ARASAN 0x16e6 #define PCI_VENDOR_ID_ARASAN 0x16e6
#define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670 #define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670
#define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202
/* /*
* PCI device class and mask * PCI device class and mask
*/ */
...@@ -182,5 +186,6 @@ int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip); ...@@ -182,5 +186,6 @@ int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
#endif #endif
extern const struct sdhci_pci_fixes sdhci_arasan; extern const struct sdhci_pci_fixes sdhci_arasan;
extern const struct sdhci_pci_fixes sdhci_snps;
#endif /* __SDHCI_PCI_H */ #endif /* __SDHCI_PCI_H */
...@@ -210,9 +210,24 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -210,9 +210,24 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (!clock) if (!clock)
return sdhci_set_clock(host, 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; host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
clk_set_rate(pltfm_host->clk, host_clk); 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); sdhci_set_clock(host, clock);
...@@ -228,7 +243,8 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, ...@@ -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_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_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; tegra_host->ddr_signaling = true;
sdhci_set_uhs_signaling(host, timing); sdhci_set_uhs_signaling(host, timing);
...@@ -238,11 +254,7 @@ static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) ...@@ -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); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
/* return clk_round_rate(pltfm_host->clk, UINT_MAX);
* 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;
} }
static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 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 = { ...@@ -334,7 +346,16 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 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, .ops = &tegra_sdhci_ops,
}; };
......
...@@ -526,6 +526,7 @@ static bool xenon_emmc_phy_slow_mode(struct sdhci_host *host, ...@@ -526,6 +526,7 @@ static bool xenon_emmc_phy_slow_mode(struct sdhci_host *host,
ret = true; ret = true;
break; break;
} }
/* else: fall through */
default: default:
reg &= ~XENON_TIMING_ADJUST_SLOW_MODE; reg &= ~XENON_TIMING_ADJUST_SLOW_MODE;
ret = false; ret = false;
......
...@@ -1029,7 +1029,9 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, ...@@ -1029,7 +1029,9 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
if (data == NULL) { if (data == NULL) {
if (host->quirks2 & if (host->quirks2 &
SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) { 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 { } else {
/* clear Auto CMD settings for no data CMDs */ /* clear Auto CMD settings for no data CMDs */
mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); 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) ...@@ -2103,7 +2105,7 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
return 0; return 0;
} }
static void sdhci_start_tuning(struct sdhci_host *host) void sdhci_start_tuning(struct sdhci_host *host)
{ {
u16 ctrl; u16 ctrl;
...@@ -2126,14 +2128,16 @@ static void sdhci_start_tuning(struct sdhci_host *host) ...@@ -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_INT_ENABLE);
sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_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_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_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; u16 ctrl;
...@@ -2142,6 +2146,7 @@ static void sdhci_reset_tuning(struct sdhci_host *host) ...@@ -2142,6 +2146,7 @@ static void sdhci_reset_tuning(struct sdhci_host *host)
ctrl &= ~SDHCI_CTRL_EXEC_TUNING; ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
} }
EXPORT_SYMBOL_GPL(sdhci_reset_tuning);
static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode) 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) ...@@ -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 setup is different to other commands and there is no timeout
* interrupt so special handling is needed. * 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_host *mmc = host->mmc;
struct mmc_command cmd = {}; struct mmc_command cmd = {};
...@@ -2212,6 +2217,7 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) ...@@ -2212,6 +2217,7 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode)
msecs_to_jiffies(50)); msecs_to_jiffies(50));
} }
EXPORT_SYMBOL_GPL(sdhci_send_tuning);
static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
{ {
...@@ -3734,14 +3740,21 @@ int sdhci_setup_host(struct sdhci_host *host) ...@@ -3734,14 +3740,21 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc_gpio_get_cd(host->mmc) < 0) mmc_gpio_get_cd(host->mmc) < 0)
mmc->caps |= MMC_CAP_NEEDS_POLL; 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)) { if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_enable(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, if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
1950000)) 1950000))
host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | host->caps1 &= ~(SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50); 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) { if (ret) {
pr_warn("%s: Failed to enable vqmmc regulator: %d\n", pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
mmc_hostname(mmc), ret); mmc_hostname(mmc), ret);
......
...@@ -748,4 +748,9 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error, ...@@ -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_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 */ #endif /* __SDHCI_HW_H */
...@@ -1388,7 +1388,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) ...@@ -1388,7 +1388,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
if (host->cfg->clk_delays || host->use_new_timings) 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); ret = mmc_of_parse(mmc);
if (ret) if (ret)
...@@ -1407,7 +1407,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) ...@@ -1407,7 +1407,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
if (ret) if (ret)
goto error_free_dma; 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; return 0;
error_free_dma: error_free_dma:
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#define CTL_DMA_ENABLE 0xd8 #define CTL_DMA_ENABLE 0xd8
#define CTL_RESET_SD 0xe0 #define CTL_RESET_SD 0xe0
#define CTL_VERSION 0xe2 #define CTL_VERSION 0xe2
#define CTL_SDIF_MODE 0xe6
#define CTL_SDIO_REGS 0x100 #define CTL_SDIO_REGS 0x100
#define CTL_CLK_AND_WAIT_CTL 0x138 #define CTL_CLK_AND_WAIT_CTL 0x138
#define CTL_RESET_SDIO 0x1e0 #define CTL_RESET_SDIO 0x1e0
...@@ -191,6 +192,11 @@ struct tmio_mmc_host { ...@@ -191,6 +192,11 @@ struct tmio_mmc_host {
/* Tuning values: 1 for success, 0 for failure */ /* Tuning values: 1 for success, 0 for failure */
DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long)); DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long));
unsigned int tap_num; 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; const struct tmio_mmc_dma_ops *dma_ops;
}; };
......
...@@ -199,6 +199,14 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, ...@@ -199,6 +199,14 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
tmio_mmc_clk_stop(host); tmio_mmc_clk_stop(host);
return; 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) if (host->clk_update)
clock = host->clk_update(host, new_clock) / 512; clock = host->clk_update(host, new_clock) / 512;
...@@ -209,8 +217,13 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, ...@@ -209,8 +217,13 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
clock <<= 1; clock <<= 1;
/* 1/1 clock is option */ /* 1/1 clock is option */
if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && ((clk >> 22) & 0x1)) if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) &&
clk |= 0xff; ((clk >> 22) & 0x1)) {
if (!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400))
clk |= 0xff;
else
clk &= ~0xff;
}
if (host->set_clk_div) if (host->set_clk_div)
host->set_clk_div(host->pdev, (clk >> 22) & 1); host->set_clk_div(host->pdev, (clk >> 22) & 1);
...@@ -309,7 +322,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, ...@@ -309,7 +322,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
{ {
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
int c = cmd->opcode; int c = cmd->opcode;
u32 irq_mask = TMIO_MASK_CMD;
switch (mmc_resp_type(cmd)) { switch (mmc_resp_type(cmd)) {
case MMC_RSP_NONE: c |= RESP_NONE; break; case MMC_RSP_NONE: c |= RESP_NONE; break;
...@@ -349,7 +361,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, ...@@ -349,7 +361,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
c |= TRANSFER_READ; c |= TRANSFER_READ;
} }
tmio_mmc_enable_mmc_irqs(host, irq_mask); tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_CMD);
/* Fire off the command */ /* Fire off the command */
sd_ctrl_write32_as_16_and_16(host, CTL_ARG_REG, cmd->arg); 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) ...@@ -805,8 +817,6 @@ static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
host->prepare_tuning(host, i % host->tap_num); host->prepare_tuning(host, i % host->tap_num);
ret = mmc_send_tuning(mmc, opcode, NULL); ret = mmc_send_tuning(mmc, opcode, NULL);
if (ret && ret != -EILSEQ)
goto out;
if (ret == 0) if (ret == 0)
set_bit(i, host->taps); set_bit(i, host->taps);
...@@ -1087,6 +1097,33 @@ static int tmio_multi_io_quirk(struct mmc_card *card, ...@@ -1087,6 +1097,33 @@ static int tmio_multi_io_quirk(struct mmc_card *card,
return blk_size; 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 = { static const struct mmc_host_ops tmio_mmc_ops = {
.request = tmio_mmc_request, .request = tmio_mmc_request,
.set_ios = tmio_mmc_set_ios, .set_ios = tmio_mmc_set_ios,
...@@ -1096,6 +1133,9 @@ static const struct mmc_host_ops tmio_mmc_ops = { ...@@ -1096,6 +1133,9 @@ static const struct mmc_host_ops tmio_mmc_ops = {
.multi_io_quirk = tmio_multi_io_quirk, .multi_io_quirk = tmio_multi_io_quirk,
.hw_reset = tmio_mmc_hw_reset, .hw_reset = tmio_mmc_hw_reset,
.execute_tuning = tmio_mmc_execute_tuning, .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) static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
......
...@@ -90,6 +90,9 @@ ...@@ -90,6 +90,9 @@
/* Some controllers have a CBSY bit */ /* Some controllers have a CBSY bit */
#define TMIO_MMC_HAVE_CBSY BIT(11) #define TMIO_MMC_HAVE_CBSY BIT(11)
/* Some controllers that support HS400 use use 4 taps while others use 8. */
#define TMIO_MMC_HAVE_4TAP_HS400 BIT(13)
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state); void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
......
...@@ -146,6 +146,13 @@ struct mmc_host_ops { ...@@ -146,6 +146,13 @@ struct mmc_host_ops {
/* Prepare HS400 target operating frequency depending host driver */ /* Prepare HS400 target operating frequency depending host driver */
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
/* Prepare for switching from HS400 to HS200 */
void (*hs400_downgrade)(struct mmc_host *host);
/* Complete selection of HS400 */
void (*hs400_complete)(struct mmc_host *host);
/* Prepare enhanced strobe depending host driver */ /* Prepare enhanced strobe depending host driver */
void (*hs400_enhanced_strobe)(struct mmc_host *host, void (*hs400_enhanced_strobe)(struct mmc_host *host,
struct mmc_ios *ios); struct mmc_ios *ios);
...@@ -474,9 +481,6 @@ static inline void *mmc_priv(struct mmc_host *host) ...@@ -474,9 +481,6 @@ static inline void *mmc_priv(struct mmc_host *host)
#define mmc_classdev(x) (&(x)->class_dev) #define mmc_classdev(x) (&(x)->class_dev)
#define mmc_hostname(x) (dev_name(&(x)->class_dev)) #define mmc_hostname(x) (dev_name(&(x)->class_dev))
int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);
void mmc_detect_change(struct mmc_host *, unsigned long delay); void mmc_detect_change(struct mmc_host *, unsigned long delay);
void mmc_request_done(struct mmc_host *, struct mmc_request *); void mmc_request_done(struct mmc_host *, struct mmc_request *);
void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq); void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq);
......
...@@ -144,7 +144,7 @@ static inline bool mmc_op_multi(u32 opcode) ...@@ -144,7 +144,7 @@ static inline bool mmc_op_multi(u32 opcode)
#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ #define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */
#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ #define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
#define R1_ERASE_RESET (1 << 13) /* sr, c */ #define R1_ERASE_RESET (1 << 13) /* sr, c */
#define R1_STATUS(x) (x & 0xFFFFE000) #define R1_STATUS(x) (x & 0xFFF9A000)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ #define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ #define R1_SWITCH_ERROR (1 << 7) /* sx, c */
......
...@@ -34,7 +34,6 @@ enum cd_types { ...@@ -34,7 +34,6 @@ enum cd_types {
* @cd_gpio: gpio for card_detect interrupt * @cd_gpio: gpio for card_detect interrupt
* @wp_type: type of write_protect method (see wp_types enum above) * @wp_type: type of write_protect method (see wp_types enum above)
* @cd_type: type of card_detect method (see cd_types enum above) * @cd_type: type of card_detect method (see cd_types enum above)
* @support_vsel: indicate it supports 1.8v switching
*/ */
struct esdhc_platform_data { struct esdhc_platform_data {
...@@ -43,7 +42,6 @@ struct esdhc_platform_data { ...@@ -43,7 +42,6 @@ struct esdhc_platform_data {
enum wp_types wp_type; enum wp_types wp_type;
enum cd_types cd_type; enum cd_types cd_type;
int max_bus_width; int max_bus_width;
bool support_vsel;
unsigned int delay_line; unsigned int delay_line;
unsigned int tuning_step; /* The delay cell steps in tuning procedure */ unsigned int tuning_step; /* The delay cell steps in tuning procedure */
unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */ unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment