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