aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libahci_platform.c
diff options
context:
space:
mode:
authorAntoine Ténart <antoine.tenart@free-electrons.com>2014-07-30 14:13:57 -0400
committerTejun Heo <tj@kernel.org>2014-07-30 15:39:46 -0400
commitb1a9edbda040a43583ff14d63ebeb91abe5848b9 (patch)
tree6ff80c6cafbd903fbf3bdea1af7ef4c696ee9f90 /drivers/ata/libahci_platform.c
parent725c7b570fda4207e465ff8856c2c12c2645a685 (diff)
ata: libahci: allow to use multiple PHYs
The current implementation of the libahci does not allow to use multiple PHYs. This patch adds the support of multiple PHYs by the libahci while keeping the old bindings valid for device tree compatibility. This introduce a new way of defining SATA ports in the device tree, with one port per sub-node. This as the advantage of allowing a per port configuration. Because some ports may be accessible but disabled in the device tree, the port_map mask is computed automatically when using this. Signed-off-by: Antoine Ténart <antoine.tenart@free-electrons.com> Acked-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata/libahci_platform.c')
-rw-r--r--drivers/ata/libahci_platform.c189
1 files changed, 151 insertions, 38 deletions
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index 00582d3a46a4..a60b8cd40198 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -39,6 +39,67 @@ static struct scsi_host_template ahci_platform_sht = {
39}; 39};
40 40
41/** 41/**
42 * ahci_platform_enable_phys - Enable PHYs
43 * @hpriv: host private area to store config values
44 *
45 * This function enables all the PHYs found in hpriv->phys, if any.
46 * If a PHY fails to be enabled, it disables all the PHYs already
47 * enabled in reverse order and returns an error.
48 *
49 * RETURNS:
50 * 0 on success otherwise a negative error code
51 */
52int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
53{
54 int rc, i;
55
56 for (i = 0; i < hpriv->nports; i++) {
57 if (!hpriv->phys[i])
58 continue;
59
60 rc = phy_init(hpriv->phys[i]);
61 if (rc)
62 goto disable_phys;
63
64 rc = phy_power_on(hpriv->phys[i]);
65 if (rc) {
66 phy_exit(hpriv->phys[i]);
67 goto disable_phys;
68 }
69 }
70
71 return 0;
72
73disable_phys:
74 while (--i >= 0) {
75 phy_power_off(hpriv->phys[i]);
76 phy_exit(hpriv->phys[i]);
77 }
78 return rc;
79}
80EXPORT_SYMBOL_GPL(ahci_platform_enable_phys);
81
82/**
83 * ahci_platform_disable_phys - Disable PHYs
84 * @hpriv: host private area to store config values
85 *
86 * This function disables all PHYs found in hpriv->phys.
87 */
88void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
89{
90 int i;
91
92 for (i = 0; i < hpriv->nports; i++) {
93 if (!hpriv->phys[i])
94 continue;
95
96 phy_power_off(hpriv->phys[i]);
97 phy_exit(hpriv->phys[i]);
98 }
99}
100EXPORT_SYMBOL_GPL(ahci_platform_disable_phys);
101
102/**
42 * ahci_platform_enable_clks - Enable platform clocks 103 * ahci_platform_enable_clks - Enable platform clocks
43 * @hpriv: host private area to store config values 104 * @hpriv: host private area to store config values
44 * 105 *
@@ -92,7 +153,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
92 * following order: 153 * following order:
93 * 1) Regulator 154 * 1) Regulator
94 * 2) Clocks (through ahci_platform_enable_clks) 155 * 2) Clocks (through ahci_platform_enable_clks)
95 * 3) Phy 156 * 3) Phys
96 * 157 *
97 * If resource enabling fails at any point the previous enabled resources 158 * If resource enabling fails at any point the previous enabled resources
98 * are disabled in reverse order. 159 * are disabled in reverse order.
@@ -114,17 +175,9 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
114 if (rc) 175 if (rc)
115 goto disable_regulator; 176 goto disable_regulator;
116 177
117 if (hpriv->phy) { 178 rc = ahci_platform_enable_phys(hpriv);
118 rc = phy_init(hpriv->phy); 179 if (rc)
119 if (rc) 180 goto disable_clks;
120 goto disable_clks;
121
122 rc = phy_power_on(hpriv->phy);
123 if (rc) {
124 phy_exit(hpriv->phy);
125 goto disable_clks;
126 }
127 }
128 181
129 return 0; 182 return 0;
130 183
@@ -144,16 +197,13 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
144 * 197 *
145 * This function disables all ahci_platform managed resources in the 198 * This function disables all ahci_platform managed resources in the
146 * following order: 199 * following order:
147 * 1) Phy 200 * 1) Phys
148 * 2) Clocks (through ahci_platform_disable_clks) 201 * 2) Clocks (through ahci_platform_disable_clks)
149 * 3) Regulator 202 * 3) Regulator
150 */ 203 */
151void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) 204void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
152{ 205{
153 if (hpriv->phy) { 206 ahci_platform_disable_phys(hpriv);
154 phy_power_off(hpriv->phy);
155 phy_exit(hpriv->phy);
156 }
157 207
158 ahci_platform_disable_clks(hpriv); 208 ahci_platform_disable_clks(hpriv);
159 209
@@ -187,7 +237,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
187 * 2) regulator for controlling the targets power (optional) 237 * 2) regulator for controlling the targets power (optional)
188 * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, 238 * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
189 * or for non devicetree enabled platforms a single clock 239 * or for non devicetree enabled platforms a single clock
190 * 4) phy (optional) 240 * 4) phys (optional)
191 * 241 *
192 * RETURNS: 242 * RETURNS:
193 * The allocated ahci_host_priv on success, otherwise an ERR_PTR value 243 * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
@@ -197,7 +247,9 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
197 struct device *dev = &pdev->dev; 247 struct device *dev = &pdev->dev;
198 struct ahci_host_priv *hpriv; 248 struct ahci_host_priv *hpriv;
199 struct clk *clk; 249 struct clk *clk;
200 int i, rc = -ENOMEM; 250 struct device_node *child;
251 int i, enabled_ports = 0, rc = -ENOMEM;
252 u32 mask_port_map = 0;
201 253
202 if (!devres_open_group(dev, NULL, GFP_KERNEL)) 254 if (!devres_open_group(dev, NULL, GFP_KERNEL))
203 return ERR_PTR(-ENOMEM); 255 return ERR_PTR(-ENOMEM);
@@ -246,28 +298,89 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
246 hpriv->clks[i] = clk; 298 hpriv->clks[i] = clk;
247 } 299 }
248 300
249 hpriv->phy = devm_phy_get(dev, "sata-phy"); 301 hpriv->nports = of_get_child_count(dev->of_node);
250 if (IS_ERR(hpriv->phy)) { 302
251 rc = PTR_ERR(hpriv->phy); 303 if (hpriv->nports) {
252 switch (rc) { 304 hpriv->phys = devm_kzalloc(dev,
253 case -ENOSYS: 305 hpriv->nports * sizeof(*hpriv->phys),
254 /* No PHY support. Check if PHY is required. */ 306 GFP_KERNEL);
255 if (of_find_property(dev->of_node, "phys", NULL)) { 307 if (!hpriv->phys) {
256 dev_err(dev, "couldn't get sata-phy: ENOSYS\n"); 308 rc = -ENOMEM;
309 goto err_out;
310 }
311
312 for_each_child_of_node(dev->of_node, child) {
313 u32 port;
314
315 if (!of_device_is_available(child))
316 continue;
317
318 if (of_property_read_u32(child, "reg", &port)) {
319 rc = -EINVAL;
257 goto err_out; 320 goto err_out;
258 } 321 }
259 case -ENODEV:
260 /* continue normally */
261 hpriv->phy = NULL;
262 break;
263 322
264 case -EPROBE_DEFER: 323 if (port >= hpriv->nports) {
265 goto err_out; 324 dev_warn(dev, "invalid port number %d\n", port);
325 continue;
326 }
327
328 mask_port_map |= BIT(port);
329
330 hpriv->phys[port] = devm_of_phy_get(dev, child, NULL);
331 if (IS_ERR(hpriv->phys[port])) {
332 rc = PTR_ERR(hpriv->phys[port]);
333 dev_err(dev,
334 "couldn't get PHY in node %s: %d\n",
335 child->name, rc);
336 goto err_out;
337 }
266 338
267 default: 339 enabled_ports++;
268 dev_err(dev, "couldn't get sata-phy\n"); 340 }
341 if (!enabled_ports) {
342 dev_warn(dev, "No port enabled\n");
343 rc = -ENODEV;
269 goto err_out; 344 goto err_out;
270 } 345 }
346
347 if (!hpriv->mask_port_map)
348 hpriv->mask_port_map = mask_port_map;
349 } else {
350 /*
351 * If no sub-node was found, keep this for device tree
352 * compatibility
353 */
354 struct phy *phy = devm_phy_get(dev, "sata-phy");
355 if (!IS_ERR(phy)) {
356 hpriv->phys = devm_kzalloc(dev, sizeof(*hpriv->phys),
357 GFP_KERNEL);
358 if (!hpriv->phys) {
359 rc = -ENOMEM;
360 goto err_out;
361 }
362
363 hpriv->phys[0] = phy;
364 hpriv->nports = 1;
365 } else {
366 rc = PTR_ERR(phy);
367 switch (rc) {
368 case -ENOSYS:
369 /* No PHY support. Check if PHY is required. */
370 if (of_find_property(dev->of_node, "phys", NULL)) {
371 dev_err(dev, "couldn't get sata-phy: ENOSYS\n");
372 goto err_out;
373 }
374 case -ENODEV:
375 /* continue normally */
376 hpriv->phys = NULL;
377 break;
378
379 default:
380 goto err_out;
381
382 }
383 }
271 } 384 }
272 385
273 pm_runtime_enable(dev); 386 pm_runtime_enable(dev);
@@ -290,7 +403,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
290 * @pi_template: template for the ata_port_info to use 403 * @pi_template: template for the ata_port_info to use
291 * 404 *
292 * This function does all the usual steps needed to bring up an 405 * This function does all the usual steps needed to bring up an
293 * ahci-platform host, note any necessary resources (ie clks, phy, etc.) 406 * ahci-platform host, note any necessary resources (ie clks, phys, etc.)
294 * must be initialized / enabled before calling this. 407 * must be initialized / enabled before calling this.
295 * 408 *
296 * RETURNS: 409 * RETURNS:
@@ -405,7 +518,7 @@ static void ahci_host_stop(struct ata_host *host)
405 * @dev: device pointer for the host 518 * @dev: device pointer for the host
406 * 519 *
407 * This function does all the usual steps needed to suspend an 520 * This function does all the usual steps needed to suspend an
408 * ahci-platform host, note any necessary resources (ie clks, phy, etc.) 521 * ahci-platform host, note any necessary resources (ie clks, phys, etc.)
409 * must be disabled after calling this. 522 * must be disabled after calling this.
410 * 523 *
411 * RETURNS: 524 * RETURNS:
@@ -442,7 +555,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
442 * @dev: device pointer for the host 555 * @dev: device pointer for the host
443 * 556 *
444 * This function does all the usual steps needed to resume an ahci-platform 557 * This function does all the usual steps needed to resume an ahci-platform
445 * host, note any necessary resources (ie clks, phy, etc.) must be 558 * host, note any necessary resources (ie clks, phys, etc.) must be
446 * initialized / enabled before calling this. 559 * initialized / enabled before calling this.
447 * 560 *
448 * RETURNS: 561 * RETURNS: