diff options
author | yangbo lu <yangbo.lu@nxp.com> | 2017-04-20 04:14:41 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2017-04-24 15:42:25 -0400 |
commit | ea35645a3c66a74af92d3bbb4eb131220fc3e58a (patch) | |
tree | 34a5caeec1bb499cd1b6da46864a4a2706cf4204 | |
parent | 19c3a0ef65adf090cb592dfe3e575f40322c88d8 (diff) |
mmc: sdhci-of-esdhc: add support for signal voltage switch
eSDHC supports signal voltage switch from 3.3v to 1.8v by
eSDHC_PROCTL[VOLT_SEL] bit. This bit changes the value of output
signal SDHC_VS, and there must be a control circuit out of eSDHC
to change the signal voltage according to SDHC_VS output signal.
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc.h | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-of-esdhc.c | 74 |
2 files changed, 75 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 5343fc062e22..68695679fed5 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | /* Protocol Control Register */ | 38 | /* Protocol Control Register */ |
39 | #define ESDHC_PROCTL 0x28 | 39 | #define ESDHC_PROCTL 0x28 |
40 | #define ESDHC_VOLT_SEL 0x00000400 | ||
40 | #define ESDHC_CTRL_4BITBUS (0x1 << 1) | 41 | #define ESDHC_CTRL_4BITBUS (0x1 << 1) |
41 | #define ESDHC_CTRL_8BITBUS (0x2 << 1) | 42 | #define ESDHC_CTRL_8BITBUS (0x2 << 1) |
42 | #define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1) | 43 | #define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1) |
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0b3f05ae8328..f2d7002e37c1 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/err.h> | 16 | #include <linux/err.h> |
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/of.h> | 18 | #include <linux/of.h> |
19 | #include <linux/of_address.h> | ||
19 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
20 | #include <linux/module.h> | 21 | #include <linux/module.h> |
21 | #include <linux/sys_soc.h> | 22 | #include <linux/sys_soc.h> |
@@ -560,6 +561,76 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) | |||
560 | sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); | 561 | sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); |
561 | } | 562 | } |
562 | 563 | ||
564 | /* The SCFG, Supplemental Configuration Unit, provides SoC specific | ||
565 | * configuration and status registers for the device. There is a | ||
566 | * SDHC IO VSEL control register on SCFG for some platforms. It's | ||
567 | * used to support SDHC IO voltage switching. | ||
568 | */ | ||
569 | static const struct of_device_id scfg_device_ids[] = { | ||
570 | { .compatible = "fsl,t1040-scfg", }, | ||
571 | { .compatible = "fsl,ls1012a-scfg", }, | ||
572 | { .compatible = "fsl,ls1046a-scfg", }, | ||
573 | {} | ||
574 | }; | ||
575 | |||
576 | /* SDHC IO VSEL control register definition */ | ||
577 | #define SCFG_SDHCIOVSELCR 0x408 | ||
578 | #define SDHCIOVSELCR_TGLEN 0x80000000 | ||
579 | #define SDHCIOVSELCR_VSELVAL 0x60000000 | ||
580 | #define SDHCIOVSELCR_SDHC_VS 0x00000001 | ||
581 | |||
582 | static int esdhc_signal_voltage_switch(struct mmc_host *mmc, | ||
583 | struct mmc_ios *ios) | ||
584 | { | ||
585 | struct sdhci_host *host = mmc_priv(mmc); | ||
586 | struct device_node *scfg_node; | ||
587 | void __iomem *scfg_base = NULL; | ||
588 | u32 sdhciovselcr; | ||
589 | u32 val; | ||
590 | |||
591 | /* | ||
592 | * Signal Voltage Switching is only applicable for Host Controllers | ||
593 | * v3.00 and above. | ||
594 | */ | ||
595 | if (host->version < SDHCI_SPEC_300) | ||
596 | return 0; | ||
597 | |||
598 | val = sdhci_readl(host, ESDHC_PROCTL); | ||
599 | |||
600 | switch (ios->signal_voltage) { | ||
601 | case MMC_SIGNAL_VOLTAGE_330: | ||
602 | val &= ~ESDHC_VOLT_SEL; | ||
603 | sdhci_writel(host, val, ESDHC_PROCTL); | ||
604 | return 0; | ||
605 | case MMC_SIGNAL_VOLTAGE_180: | ||
606 | scfg_node = of_find_matching_node(NULL, scfg_device_ids); | ||
607 | if (scfg_node) | ||
608 | scfg_base = of_iomap(scfg_node, 0); | ||
609 | if (scfg_base) { | ||
610 | sdhciovselcr = SDHCIOVSELCR_TGLEN | | ||
611 | SDHCIOVSELCR_VSELVAL; | ||
612 | iowrite32be(sdhciovselcr, | ||
613 | scfg_base + SCFG_SDHCIOVSELCR); | ||
614 | |||
615 | val |= ESDHC_VOLT_SEL; | ||
616 | sdhci_writel(host, val, ESDHC_PROCTL); | ||
617 | mdelay(5); | ||
618 | |||
619 | sdhciovselcr = SDHCIOVSELCR_TGLEN | | ||
620 | SDHCIOVSELCR_SDHC_VS; | ||
621 | iowrite32be(sdhciovselcr, | ||
622 | scfg_base + SCFG_SDHCIOVSELCR); | ||
623 | iounmap(scfg_base); | ||
624 | } else { | ||
625 | val |= ESDHC_VOLT_SEL; | ||
626 | sdhci_writel(host, val, ESDHC_PROCTL); | ||
627 | } | ||
628 | return 0; | ||
629 | default: | ||
630 | return 0; | ||
631 | } | ||
632 | } | ||
633 | |||
563 | #ifdef CONFIG_PM_SLEEP | 634 | #ifdef CONFIG_PM_SLEEP |
564 | static u32 esdhc_proctl; | 635 | static u32 esdhc_proctl; |
565 | static int esdhc_of_suspend(struct device *dev) | 636 | static int esdhc_of_suspend(struct device *dev) |
@@ -717,6 +788,9 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) | |||
717 | if (IS_ERR(host)) | 788 | if (IS_ERR(host)) |
718 | return PTR_ERR(host); | 789 | return PTR_ERR(host); |
719 | 790 | ||
791 | host->mmc_host_ops.start_signal_voltage_switch = | ||
792 | esdhc_signal_voltage_switch; | ||
793 | |||
720 | esdhc_init(pdev, host); | 794 | esdhc_init(pdev, host); |
721 | 795 | ||
722 | sdhci_get_of_property(pdev); | 796 | sdhci_get_of_property(pdev); |