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 | |
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>
-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); |