mmc: sdhci-xenon: Add SoC PHY PAD voltage control
authorHu Ziji <huziji@marvell.com>
Thu, 30 Mar 2017 15:23:01 +0000 (17:23 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 24 Apr 2017 19:42:17 +0000 (21:42 +0200)
Some SoCs have PHY PAD outside Xenon IP.
PHY PAD voltage should match signalling voltage in use.

Add generic SoC PHY PAD voltage control interface.
Implement Aramda-3700 SoC PHY PAD voltage control.

Signed-off-by: Hu Ziji <huziji@marvell.com>
Tested-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-xenon-phy.c
drivers/mmc/host/sdhci-xenon.c
drivers/mmc/host/sdhci-xenon.h

index b14544e91b65e1b3dfda33dfb52daf1aa8084baa..4bdbcd3f2645d69f1bc2a50505f1f7688100089a 100644 (file)
@@ -143,6 +143,21 @@ enum xenon_phy_type_enum {
        NR_PHY_TYPES
 };
 
+enum soc_pad_ctrl_type {
+       SOC_PAD_SD,
+       SOC_PAD_FIXED_1_8V,
+};
+
+struct soc_pad_ctrl {
+       /* Register address of SoC PHY PAD ctrl */
+       void __iomem    *reg;
+       /* SoC PHY PAD ctrl type */
+       enum soc_pad_ctrl_type pad_type;
+       /* SoC specific operation to set SoC PHY PAD */
+       void (*set_soc_pad)(struct sdhci_host *host,
+                           unsigned char signal_voltage);
+};
+
 static struct xenon_emmc_phy_regs xenon_emmc_5_0_phy_regs = {
        .timing_adj     = XENON_EMMC_5_0_PHY_TIMING_ADJUST,
        .func_ctrl      = XENON_EMMC_5_0_PHY_FUNC_CONTROL,
@@ -176,6 +191,8 @@ struct xenon_emmc_phy_params {
        u8      nr_tun_times;
        /* Divider for calculating Tuning Step */
        u8      tun_step_divider;
+
+       struct soc_pad_ctrl pad_ctrl;
 };
 
 static int xenon_alloc_emmc_phy(struct sdhci_host *host)
@@ -254,6 +271,45 @@ static int xenon_emmc_phy_init(struct sdhci_host *host)
        return 0;
 }
 
+#define ARMADA_3700_SOC_PAD_1_8V       0x1
+#define ARMADA_3700_SOC_PAD_3_3V       0x0
+
+static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host,
+                                           unsigned char signal_voltage)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+       struct xenon_emmc_phy_params *params = priv->phy_params;
+
+       if (params->pad_ctrl.pad_type == SOC_PAD_FIXED_1_8V) {
+               writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
+       } else if (params->pad_ctrl.pad_type == SOC_PAD_SD) {
+               if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+                       writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
+               else if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+                       writel(ARMADA_3700_SOC_PAD_3_3V, params->pad_ctrl.reg);
+       }
+}
+
+/*
+ * Set SoC PHY voltage PAD control register,
+ * according to the operation voltage on PAD.
+ * The detailed operation depends on SoC implementation.
+ */
+static void xenon_emmc_phy_set_soc_pad(struct sdhci_host *host,
+                                      unsigned char signal_voltage)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+       struct xenon_emmc_phy_params *params = priv->phy_params;
+
+       if (!params->pad_ctrl.reg)
+               return;
+
+       if (params->pad_ctrl.set_soc_pad)
+               params->pad_ctrl.set_soc_pad(host, signal_voltage);
+}
+
 /*
  * Enable eMMC PHY HW DLL
  * DLL should be enabled and stable before HS200/SDR104 tuning,
@@ -562,6 +618,51 @@ phy_init:
        dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting completes\n");
 }
 
+static int get_dt_pad_ctrl_data(struct sdhci_host *host,
+                               struct device_node *np,
+                               struct xenon_emmc_phy_params *params)
+{
+       int ret = 0;
+       const char *name;
+       struct resource iomem;
+
+       if (of_device_is_compatible(np, "marvell,armada-3700-sdhci"))
+               params->pad_ctrl.set_soc_pad = armada_3700_soc_pad_voltage_set;
+       else
+               return 0;
+
+       if (of_address_to_resource(np, 1, &iomem)) {
+               dev_err(mmc_dev(host->mmc), "Unable to find SoC PAD ctrl register address for %s\n",
+                       np->name);
+               return -EINVAL;
+       }
+
+       params->pad_ctrl.reg = devm_ioremap_resource(mmc_dev(host->mmc),
+                                                    &iomem);
+       if (IS_ERR(params->pad_ctrl.reg)) {
+               dev_err(mmc_dev(host->mmc), "Unable to get SoC PHY PAD ctrl register for %s\n",
+                       np->name);
+               return PTR_ERR(params->pad_ctrl.reg);
+       }
+
+       ret = of_property_read_string(np, "marvell,pad-type", &name);
+       if (ret) {
+               dev_err(mmc_dev(host->mmc), "Unable to determine SoC PHY PAD ctrl type\n");
+               return ret;
+       }
+       if (!strcmp(name, "sd")) {
+               params->pad_ctrl.pad_type = SOC_PAD_SD;
+       } else if (!strcmp(name, "fixed-1-8v")) {
+               params->pad_ctrl.pad_type = SOC_PAD_FIXED_1_8V;
+       } else {
+               dev_err(mmc_dev(host->mmc), "Unsupported SoC PHY PAD ctrl type %s\n",
+                       name);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
 static int xenon_emmc_phy_parse_param_dt(struct sdhci_host *host,
                                         struct device_node *np,
                                         struct xenon_emmc_phy_params *params)
@@ -590,7 +691,14 @@ static int xenon_emmc_phy_parse_param_dt(struct sdhci_host *host,
                                  &value))
                params->tun_step_divider = value & 0xFF;
 
-       return 0;
+       return get_dt_pad_ctrl_data(host, np, params);
+}
+
+/* Set SoC PHY Voltage PAD */
+void xenon_soc_pad_ctrl(struct sdhci_host *host,
+                       unsigned char signal_voltage)
+{
+       xenon_emmc_phy_set_soc_pad(host, signal_voltage);
 }
 
 /*
index 36e22bd2b8cc6e6caa2c1b8c1cb8b8a65137de7b..8e56b9ccfb399325852c890ced5c517840fb24c2 100644 (file)
@@ -280,6 +280,8 @@ static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
         */
        xenon_enable_internal_clk(host);
 
+       xenon_soc_pad_ctrl(host, ios->signal_voltage);
+
        /*
         * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable.
         * Thus SDHCI_CTRL_VDD_180 bit might not work then.
index b29d45358de8674d3f687f031d9b091b5ca180ba..6e6523ea01ce50389f3b77f52b467efb11305831 100644 (file)
@@ -96,4 +96,6 @@ int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios);
 void xenon_clean_phy(struct sdhci_host *host);
 int xenon_phy_parse_dt(struct device_node *np,
                       struct sdhci_host *host);
+void xenon_soc_pad_ctrl(struct sdhci_host *host,
+                       unsigned char signal_voltage);
 #endif