diff options
| author | Shawn Lin <shawn.lin@rock-chips.com> | 2016-03-07 10:39:19 -0500 |
|---|---|---|
| committer | Ulf Hansson <ulf.hansson@linaro.org> | 2016-03-17 09:54:40 -0400 |
| commit | 91aa366109e801ef4755096248d7c66136f6e380 (patch) | |
| tree | e0f871838bee698db0c050aeacf60312a9850134 /drivers/mmc | |
| parent | 278d09624eda94d82ff385b1c40ecc3db99c73d3 (diff) | |
mmc: sdhci-of-arasan: add phy support for sdhci-of-arasan
This patch adds Generic PHY access for sdhci-of-arasan. Driver
can get PHY handler from dt-binding, and power-on/init the PHY.
Currently, it's just mandatory for arasan,sdhci-5.1.
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/host/sdhci-of-arasan.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 91b7c62324b5..2e482b13d25e 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
| 24 | #include <linux/phy/phy.h> | ||
| 24 | #include "sdhci-pltfm.h" | 25 | #include "sdhci-pltfm.h" |
| 25 | 26 | ||
| 26 | #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c | 27 | #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c |
| @@ -32,9 +33,11 @@ | |||
| 32 | /** | 33 | /** |
| 33 | * struct sdhci_arasan_data | 34 | * struct sdhci_arasan_data |
| 34 | * @clk_ahb: Pointer to the AHB clock | 35 | * @clk_ahb: Pointer to the AHB clock |
| 36 | * @phy: Pointer to the generic phy | ||
| 35 | */ | 37 | */ |
| 36 | struct sdhci_arasan_data { | 38 | struct sdhci_arasan_data { |
| 37 | struct clk *clk_ahb; | 39 | struct clk *clk_ahb; |
| 40 | struct phy *phy; | ||
| 38 | }; | 41 | }; |
| 39 | 42 | ||
| 40 | static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) | 43 | static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) |
| @@ -88,6 +91,15 @@ static int sdhci_arasan_suspend(struct device *dev) | |||
| 88 | if (ret) | 91 | if (ret) |
| 89 | return ret; | 92 | return ret; |
| 90 | 93 | ||
| 94 | if (!IS_ERR(sdhci_arasan->phy)) { | ||
| 95 | ret = phy_power_off(sdhci_arasan->phy); | ||
| 96 | if (ret) { | ||
| 97 | dev_err(dev, "Cannot power off phy.\n"); | ||
| 98 | sdhci_resume_host(host); | ||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 91 | clk_disable(pltfm_host->clk); | 103 | clk_disable(pltfm_host->clk); |
| 92 | clk_disable(sdhci_arasan->clk_ahb); | 104 | clk_disable(sdhci_arasan->clk_ahb); |
| 93 | 105 | ||
| @@ -121,6 +133,14 @@ static int sdhci_arasan_resume(struct device *dev) | |||
| 121 | return ret; | 133 | return ret; |
| 122 | } | 134 | } |
| 123 | 135 | ||
| 136 | if (!IS_ERR(sdhci_arasan->phy)) { | ||
| 137 | ret = phy_power_on(sdhci_arasan->phy); | ||
| 138 | if (ret) { | ||
| 139 | dev_err(dev, "Cannot power on phy.\n"); | ||
| 140 | return ret; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 124 | return sdhci_resume_host(host); | 144 | return sdhci_resume_host(host); |
| 125 | } | 145 | } |
| 126 | #endif /* ! CONFIG_PM_SLEEP */ | 146 | #endif /* ! CONFIG_PM_SLEEP */ |
| @@ -179,12 +199,42 @@ static int sdhci_arasan_probe(struct platform_device *pdev) | |||
| 179 | goto clk_disable_all; | 199 | goto clk_disable_all; |
| 180 | } | 200 | } |
| 181 | 201 | ||
| 202 | sdhci_arasan->phy = ERR_PTR(-ENODEV); | ||
| 203 | if (of_device_is_compatible(pdev->dev.of_node, | ||
| 204 | "arasan,sdhci-5.1")) { | ||
| 205 | sdhci_arasan->phy = devm_phy_get(&pdev->dev, | ||
| 206 | "phy_arasan"); | ||
| 207 | if (IS_ERR(sdhci_arasan->phy)) { | ||
| 208 | ret = PTR_ERR(sdhci_arasan->phy); | ||
| 209 | dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n"); | ||
| 210 | goto clk_disable_all; | ||
| 211 | } | ||
| 212 | |||
| 213 | ret = phy_init(sdhci_arasan->phy); | ||
| 214 | if (ret < 0) { | ||
| 215 | dev_err(&pdev->dev, "phy_init err.\n"); | ||
| 216 | goto clk_disable_all; | ||
| 217 | } | ||
| 218 | |||
| 219 | ret = phy_power_on(sdhci_arasan->phy); | ||
| 220 | if (ret < 0) { | ||
| 221 | dev_err(&pdev->dev, "phy_power_on err.\n"); | ||
| 222 | goto err_phy_power; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 182 | ret = sdhci_add_host(host); | 226 | ret = sdhci_add_host(host); |
| 183 | if (ret) | 227 | if (ret) |
| 184 | goto clk_disable_all; | 228 | goto err_add_host; |
| 185 | 229 | ||
| 186 | return 0; | 230 | return 0; |
| 187 | 231 | ||
| 232 | err_add_host: | ||
| 233 | if (!IS_ERR(sdhci_arasan->phy)) | ||
| 234 | phy_power_off(sdhci_arasan->phy); | ||
| 235 | err_phy_power: | ||
| 236 | if (!IS_ERR(sdhci_arasan->phy)) | ||
| 237 | phy_exit(sdhci_arasan->phy); | ||
| 188 | clk_disable_all: | 238 | clk_disable_all: |
| 189 | clk_disable_unprepare(clk_xin); | 239 | clk_disable_unprepare(clk_xin); |
| 190 | clk_dis_ahb: | 240 | clk_dis_ahb: |
| @@ -202,6 +252,11 @@ static int sdhci_arasan_remove(struct platform_device *pdev) | |||
| 202 | struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); | 252 | struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); |
| 203 | struct clk *clk_ahb = sdhci_arasan->clk_ahb; | 253 | struct clk *clk_ahb = sdhci_arasan->clk_ahb; |
| 204 | 254 | ||
| 255 | if (!IS_ERR(sdhci_arasan->phy)) { | ||
| 256 | phy_power_off(sdhci_arasan->phy); | ||
| 257 | phy_exit(sdhci_arasan->phy); | ||
| 258 | } | ||
| 259 | |||
| 205 | ret = sdhci_pltfm_unregister(pdev); | 260 | ret = sdhci_pltfm_unregister(pdev); |
| 206 | 261 | ||
| 207 | clk_disable_unprepare(clk_ahb); | 262 | clk_disable_unprepare(clk_ahb); |
