aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/ata/Kconfig7
-rw-r--r--drivers/ata/Makefile10
-rw-r--r--drivers/ata/ahci_platform.c515
-rw-r--r--drivers/ata/libahci_platform.c541
4 files changed, 549 insertions, 524 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 9f3e3269c09a..12767ad098e0 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -99,7 +99,6 @@ config SATA_AHCI_PLATFORM
99 99
100config AHCI_ST 100config AHCI_ST
101 tristate "ST AHCI SATA support" 101 tristate "ST AHCI SATA support"
102 depends on SATA_AHCI_PLATFORM
103 depends on ARCH_STI 102 depends on ARCH_STI
104 help 103 help
105 This option enables support for ST AHCI SATA controller. 104 This option enables support for ST AHCI SATA controller.
@@ -108,7 +107,7 @@ config AHCI_ST
108 107
109config AHCI_IMX 108config AHCI_IMX
110 tristate "Freescale i.MX AHCI SATA support" 109 tristate "Freescale i.MX AHCI SATA support"
111 depends on SATA_AHCI_PLATFORM && MFD_SYSCON 110 depends on MFD_SYSCON
112 help 111 help
113 This option enables support for the Freescale i.MX SoC's 112 This option enables support for the Freescale i.MX SoC's
114 onboard AHCI SATA. 113 onboard AHCI SATA.
@@ -117,7 +116,7 @@ config AHCI_IMX
117 116
118config AHCI_SUNXI 117config AHCI_SUNXI
119 tristate "Allwinner sunxi AHCI SATA support" 118 tristate "Allwinner sunxi AHCI SATA support"
120 depends on ARCH_SUNXI && SATA_AHCI_PLATFORM 119 depends on ARCH_SUNXI
121 help 120 help
122 This option enables support for the Allwinner sunxi SoC's 121 This option enables support for the Allwinner sunxi SoC's
123 onboard AHCI SATA. 122 onboard AHCI SATA.
@@ -126,7 +125,7 @@ config AHCI_SUNXI
126 125
127config AHCI_XGENE 126config AHCI_XGENE
128 tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support" 127 tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support"
129 depends on SATA_AHCI_PLATFORM && (ARM64 || COMPILE_TEST) 128 depends on ARM64 || COMPILE_TEST
130 select PHY_XGENE 129 select PHY_XGENE
131 help 130 help
132 This option enables support for APM X-Gene SoC SATA host controller. 131 This option enables support for APM X-Gene SoC SATA host controller.
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 095d4610d5c3..5fa79abc6a53 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -4,16 +4,16 @@ obj-$(CONFIG_ATA) += libata.o
4# non-SFF interface 4# non-SFF interface
5obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o 5obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o
6obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o 6obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o
7obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o 7obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o
8obj-$(CONFIG_SATA_FSL) += sata_fsl.o 8obj-$(CONFIG_SATA_FSL) += sata_fsl.o
9obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o 9obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
10obj-$(CONFIG_SATA_SIL24) += sata_sil24.o 10obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
11obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o 11obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
12obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o 12obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
13obj-$(CONFIG_AHCI_IMX) += ahci_imx.o 13obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
14obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o 14obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o
15obj-$(CONFIG_AHCI_ST) += ahci_st.o 15obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o
16obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o 16obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o
17 17
18# SFF w/ custom DMA 18# SFF w/ custom DMA
19obj-$(CONFIG_PDC_ADMA) += pdc_adma.o 19obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 7bd6adf54b3e..ef67e79944f9 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -12,28 +12,15 @@
12 * any later version. 12 * any later version.
13 */ 13 */
14 14
15#include <linux/clk.h>
16#include <linux/kernel.h> 15#include <linux/kernel.h>
17#include <linux/gfp.h>
18#include <linux/module.h> 16#include <linux/module.h>
19#include <linux/pm.h> 17#include <linux/pm.h>
20#include <linux/interrupt.h>
21#include <linux/device.h> 18#include <linux/device.h>
22#include <linux/platform_device.h> 19#include <linux/platform_device.h>
23#include <linux/libata.h> 20#include <linux/libata.h>
24#include <linux/ahci_platform.h> 21#include <linux/ahci_platform.h>
25#include <linux/phy/phy.h>
26#include <linux/pm_runtime.h>
27#include "ahci.h" 22#include "ahci.h"
28 23
29static void ahci_host_stop(struct ata_host *host);
30
31struct ata_port_operations ahci_platform_ops = {
32 .inherits = &ahci_ops,
33 .host_stop = ahci_host_stop,
34};
35EXPORT_SYMBOL_GPL(ahci_platform_ops);
36
37static const struct ata_port_info ahci_port_info = { 24static const struct ata_port_info ahci_port_info = {
38 .flags = AHCI_FLAG_COMMON, 25 .flags = AHCI_FLAG_COMMON,
39 .pio_mask = ATA_PIO4, 26 .pio_mask = ATA_PIO4,
@@ -41,345 +28,6 @@ static const struct ata_port_info ahci_port_info = {
41 .port_ops = &ahci_platform_ops, 28 .port_ops = &ahci_platform_ops,
42}; 29};
43 30
44static struct scsi_host_template ahci_platform_sht = {
45 AHCI_SHT("ahci_platform"),
46};
47
48/**
49 * ahci_platform_enable_clks - Enable platform clocks
50 * @hpriv: host private area to store config values
51 *
52 * This function enables all the clks found in hpriv->clks, starting at
53 * index 0. If any clk fails to enable it disables all the clks already
54 * enabled in reverse order, and then returns an error.
55 *
56 * RETURNS:
57 * 0 on success otherwise a negative error code
58 */
59int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
60{
61 int c, rc;
62
63 for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
64 rc = clk_prepare_enable(hpriv->clks[c]);
65 if (rc)
66 goto disable_unprepare_clk;
67 }
68 return 0;
69
70disable_unprepare_clk:
71 while (--c >= 0)
72 clk_disable_unprepare(hpriv->clks[c]);
73 return rc;
74}
75EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
76
77/**
78 * ahci_platform_disable_clks - Disable platform clocks
79 * @hpriv: host private area to store config values
80 *
81 * This function disables all the clks found in hpriv->clks, in reverse
82 * order of ahci_platform_enable_clks (starting at the end of the array).
83 */
84void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
85{
86 int c;
87
88 for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
89 if (hpriv->clks[c])
90 clk_disable_unprepare(hpriv->clks[c]);
91}
92EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
93
94/**
95 * ahci_platform_enable_resources - Enable platform resources
96 * @hpriv: host private area to store config values
97 *
98 * This function enables all ahci_platform managed resources in the
99 * following order:
100 * 1) Regulator
101 * 2) Clocks (through ahci_platform_enable_clks)
102 * 3) Phy
103 *
104 * If resource enabling fails at any point the previous enabled resources
105 * are disabled in reverse order.
106 *
107 * RETURNS:
108 * 0 on success otherwise a negative error code
109 */
110int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
111{
112 int rc;
113
114 if (hpriv->target_pwr) {
115 rc = regulator_enable(hpriv->target_pwr);
116 if (rc)
117 return rc;
118 }
119
120 rc = ahci_platform_enable_clks(hpriv);
121 if (rc)
122 goto disable_regulator;
123
124 if (hpriv->phy) {
125 rc = phy_init(hpriv->phy);
126 if (rc)
127 goto disable_clks;
128
129 rc = phy_power_on(hpriv->phy);
130 if (rc) {
131 phy_exit(hpriv->phy);
132 goto disable_clks;
133 }
134 }
135
136 return 0;
137
138disable_clks:
139 ahci_platform_disable_clks(hpriv);
140
141disable_regulator:
142 if (hpriv->target_pwr)
143 regulator_disable(hpriv->target_pwr);
144 return rc;
145}
146EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
147
148/**
149 * ahci_platform_disable_resources - Disable platform resources
150 * @hpriv: host private area to store config values
151 *
152 * This function disables all ahci_platform managed resources in the
153 * following order:
154 * 1) Phy
155 * 2) Clocks (through ahci_platform_disable_clks)
156 * 3) Regulator
157 */
158void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
159{
160 if (hpriv->phy) {
161 phy_power_off(hpriv->phy);
162 phy_exit(hpriv->phy);
163 }
164
165 ahci_platform_disable_clks(hpriv);
166
167 if (hpriv->target_pwr)
168 regulator_disable(hpriv->target_pwr);
169}
170EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
171
172static void ahci_platform_put_resources(struct device *dev, void *res)
173{
174 struct ahci_host_priv *hpriv = res;
175 int c;
176
177 if (hpriv->got_runtime_pm) {
178 pm_runtime_put_sync(dev);
179 pm_runtime_disable(dev);
180 }
181
182 for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
183 clk_put(hpriv->clks[c]);
184}
185
186/**
187 * ahci_platform_get_resources - Get platform resources
188 * @pdev: platform device to get resources for
189 *
190 * This function allocates an ahci_host_priv struct, and gets the following
191 * resources, storing a reference to them inside the returned struct:
192 *
193 * 1) mmio registers (IORESOURCE_MEM 0, mandatory)
194 * 2) regulator for controlling the targets power (optional)
195 * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
196 * or for non devicetree enabled platforms a single clock
197 * 4) phy (optional)
198 *
199 * RETURNS:
200 * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
201 */
202struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
203{
204 struct device *dev = &pdev->dev;
205 struct ahci_host_priv *hpriv;
206 struct clk *clk;
207 int i, rc = -ENOMEM;
208
209 if (!devres_open_group(dev, NULL, GFP_KERNEL))
210 return ERR_PTR(-ENOMEM);
211
212 hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
213 GFP_KERNEL);
214 if (!hpriv)
215 goto err_out;
216
217 devres_add(dev, hpriv);
218
219 hpriv->mmio = devm_ioremap_resource(dev,
220 platform_get_resource(pdev, IORESOURCE_MEM, 0));
221 if (IS_ERR(hpriv->mmio)) {
222 dev_err(dev, "no mmio space\n");
223 rc = PTR_ERR(hpriv->mmio);
224 goto err_out;
225 }
226
227 hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
228 if (IS_ERR(hpriv->target_pwr)) {
229 rc = PTR_ERR(hpriv->target_pwr);
230 if (rc == -EPROBE_DEFER)
231 goto err_out;
232 hpriv->target_pwr = NULL;
233 }
234
235 for (i = 0; i < AHCI_MAX_CLKS; i++) {
236 /*
237 * For now we must use clk_get(dev, NULL) for the first clock,
238 * because some platforms (da850, spear13xx) are not yet
239 * converted to use devicetree for clocks. For new platforms
240 * this is equivalent to of_clk_get(dev->of_node, 0).
241 */
242 if (i == 0)
243 clk = clk_get(dev, NULL);
244 else
245 clk = of_clk_get(dev->of_node, i);
246
247 if (IS_ERR(clk)) {
248 rc = PTR_ERR(clk);
249 if (rc == -EPROBE_DEFER)
250 goto err_out;
251 break;
252 }
253 hpriv->clks[i] = clk;
254 }
255
256 hpriv->phy = devm_phy_get(dev, "sata-phy");
257 if (IS_ERR(hpriv->phy)) {
258 rc = PTR_ERR(hpriv->phy);
259 switch (rc) {
260 case -ENODEV:
261 case -ENOSYS:
262 /* continue normally */
263 hpriv->phy = NULL;
264 break;
265
266 case -EPROBE_DEFER:
267 goto err_out;
268
269 default:
270 dev_err(dev, "couldn't get sata-phy\n");
271 goto err_out;
272 }
273 }
274
275 pm_runtime_enable(dev);
276 pm_runtime_get_sync(dev);
277 hpriv->got_runtime_pm = true;
278
279 devres_remove_group(dev, NULL);
280 return hpriv;
281
282err_out:
283 devres_release_group(dev, NULL);
284 return ERR_PTR(rc);
285}
286EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
287
288/**
289 * ahci_platform_init_host - Bring up an ahci-platform host
290 * @pdev: platform device pointer for the host
291 * @hpriv: ahci-host private data for the host
292 * @pi_template: template for the ata_port_info to use
293 * @force_port_map: param passed to ahci_save_initial_config
294 * @mask_port_map: param passed to ahci_save_initial_config
295 *
296 * This function does all the usual steps needed to bring up an
297 * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
298 * must be initialized / enabled before calling this.
299 *
300 * RETURNS:
301 * 0 on success otherwise a negative error code
302 */
303int ahci_platform_init_host(struct platform_device *pdev,
304 struct ahci_host_priv *hpriv,
305 const struct ata_port_info *pi_template,
306 unsigned int force_port_map,
307 unsigned int mask_port_map)
308{
309 struct device *dev = &pdev->dev;
310 struct ata_port_info pi = *pi_template;
311 const struct ata_port_info *ppi[] = { &pi, NULL };
312 struct ata_host *host;
313 int i, irq, n_ports, rc;
314
315 irq = platform_get_irq(pdev, 0);
316 if (irq <= 0) {
317 dev_err(dev, "no irq\n");
318 return -EINVAL;
319 }
320
321 /* prepare host */
322 hpriv->flags |= (unsigned long)pi.private_data;
323
324 ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map);
325
326 if (hpriv->cap & HOST_CAP_NCQ)
327 pi.flags |= ATA_FLAG_NCQ;
328
329 if (hpriv->cap & HOST_CAP_PMP)
330 pi.flags |= ATA_FLAG_PMP;
331
332 ahci_set_em_messages(hpriv, &pi);
333
334 /* CAP.NP sometimes indicate the index of the last enabled
335 * port, at other times, that of the last possible port, so
336 * determining the maximum port number requires looking at
337 * both CAP.NP and port_map.
338 */
339 n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
340
341 host = ata_host_alloc_pinfo(dev, ppi, n_ports);
342 if (!host)
343 return -ENOMEM;
344
345 host->private_data = hpriv;
346
347 if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
348 host->flags |= ATA_HOST_PARALLEL_SCAN;
349 else
350 dev_info(dev, "SSS flag set, parallel bus scan disabled\n");
351
352 if (pi.flags & ATA_FLAG_EM)
353 ahci_reset_em(host);
354
355 for (i = 0; i < host->n_ports; i++) {
356 struct ata_port *ap = host->ports[i];
357
358 ata_port_desc(ap, "mmio %pR",
359 platform_get_resource(pdev, IORESOURCE_MEM, 0));
360 ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
361
362 /* set enclosure management message type */
363 if (ap->flags & ATA_FLAG_EM)
364 ap->em_message_type = hpriv->em_msg_type;
365
366 /* disabled/not-implemented port */
367 if (!(hpriv->port_map & (1 << i)))
368 ap->ops = &ata_dummy_port_ops;
369 }
370
371 rc = ahci_reset_controller(host);
372 if (rc)
373 return rc;
374
375 ahci_init_controller(host);
376 ahci_print_info(host, "platform");
377
378 return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
379 &ahci_platform_sht);
380}
381EXPORT_SYMBOL_GPL(ahci_platform_init_host);
382
383static int ahci_probe(struct platform_device *pdev) 31static int ahci_probe(struct platform_device *pdev)
384{ 32{
385 struct device *dev = &pdev->dev; 33 struct device *dev = &pdev->dev;
@@ -420,169 +68,6 @@ disable_resources:
420 return rc; 68 return rc;
421} 69}
422 70
423static void ahci_host_stop(struct ata_host *host)
424{
425 struct device *dev = host->dev;
426 struct ahci_platform_data *pdata = dev_get_platdata(dev);
427 struct ahci_host_priv *hpriv = host->private_data;
428
429 if (pdata && pdata->exit)
430 pdata->exit(dev);
431
432 ahci_platform_disable_resources(hpriv);
433}
434
435#ifdef CONFIG_PM_SLEEP
436/**
437 * ahci_platform_suspend_host - Suspend an ahci-platform host
438 * @dev: device pointer for the host
439 *
440 * This function does all the usual steps needed to suspend an
441 * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
442 * must be disabled after calling this.
443 *
444 * RETURNS:
445 * 0 on success otherwise a negative error code
446 */
447int ahci_platform_suspend_host(struct device *dev)
448{
449 struct ata_host *host = dev_get_drvdata(dev);
450 struct ahci_host_priv *hpriv = host->private_data;
451 void __iomem *mmio = hpriv->mmio;
452 u32 ctl;
453
454 if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
455 dev_err(dev, "firmware update required for suspend/resume\n");
456 return -EIO;
457 }
458
459 /*
460 * AHCI spec rev1.1 section 8.3.3:
461 * Software must disable interrupts prior to requesting a
462 * transition of the HBA to D3 state.
463 */
464 ctl = readl(mmio + HOST_CTL);
465 ctl &= ~HOST_IRQ_EN;
466 writel(ctl, mmio + HOST_CTL);
467 readl(mmio + HOST_CTL); /* flush */
468
469 return ata_host_suspend(host, PMSG_SUSPEND);
470}
471EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
472
473/**
474 * ahci_platform_resume_host - Resume an ahci-platform host
475 * @dev: device pointer for the host
476 *
477 * This function does all the usual steps needed to resume an ahci-platform
478 * host, note any necessary resources (ie clks, phy, etc.) must be
479 * initialized / enabled before calling this.
480 *
481 * RETURNS:
482 * 0 on success otherwise a negative error code
483 */
484int ahci_platform_resume_host(struct device *dev)
485{
486 struct ata_host *host = dev_get_drvdata(dev);
487 int rc;
488
489 if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
490 rc = ahci_reset_controller(host);
491 if (rc)
492 return rc;
493
494 ahci_init_controller(host);
495 }
496
497 ata_host_resume(host);
498
499 return 0;
500}
501EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
502
503/**
504 * ahci_platform_suspend - Suspend an ahci-platform device
505 * @dev: the platform device to suspend
506 *
507 * This function suspends the host associated with the device, followed by
508 * disabling all the resources of the device.
509 *
510 * RETURNS:
511 * 0 on success otherwise a negative error code
512 */
513int ahci_platform_suspend(struct device *dev)
514{
515 struct ahci_platform_data *pdata = dev_get_platdata(dev);
516 struct ata_host *host = dev_get_drvdata(dev);
517 struct ahci_host_priv *hpriv = host->private_data;
518 int rc;
519
520 rc = ahci_platform_suspend_host(dev);
521 if (rc)
522 return rc;
523
524 if (pdata && pdata->suspend) {
525 rc = pdata->suspend(dev);
526 if (rc)
527 goto resume_host;
528 }
529
530 ahci_platform_disable_resources(hpriv);
531
532 return 0;
533
534resume_host:
535 ahci_platform_resume_host(dev);
536 return rc;
537}
538EXPORT_SYMBOL_GPL(ahci_platform_suspend);
539
540/**
541 * ahci_platform_resume - Resume an ahci-platform device
542 * @dev: the platform device to resume
543 *
544 * This function enables all the resources of the device followed by
545 * resuming the host associated with the device.
546 *
547 * RETURNS:
548 * 0 on success otherwise a negative error code
549 */
550int ahci_platform_resume(struct device *dev)
551{
552 struct ahci_platform_data *pdata = dev_get_platdata(dev);
553 struct ata_host *host = dev_get_drvdata(dev);
554 struct ahci_host_priv *hpriv = host->private_data;
555 int rc;
556
557 rc = ahci_platform_enable_resources(hpriv);
558 if (rc)
559 return rc;
560
561 if (pdata && pdata->resume) {
562 rc = pdata->resume(dev);
563 if (rc)
564 goto disable_resources;
565 }
566
567 rc = ahci_platform_resume_host(dev);
568 if (rc)
569 goto disable_resources;
570
571 /* We resumed so update PM runtime state */
572 pm_runtime_disable(dev);
573 pm_runtime_set_active(dev);
574 pm_runtime_enable(dev);
575
576 return 0;
577
578disable_resources:
579 ahci_platform_disable_resources(hpriv);
580
581 return rc;
582}
583EXPORT_SYMBOL_GPL(ahci_platform_resume);
584#endif
585
586static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, 71static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
587 ahci_platform_resume); 72 ahci_platform_resume);
588 73
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
new file mode 100644
index 000000000000..7cb3a85719c0
--- /dev/null
+++ b/drivers/ata/libahci_platform.c
@@ -0,0 +1,541 @@
1/*
2 * AHCI SATA platform library
3 *
4 * Copyright 2004-2005 Red Hat, Inc.
5 * Jeff Garzik <jgarzik@pobox.com>
6 * Copyright 2010 MontaVista Software, LLC.
7 * Anton Vorontsov <avorontsov@ru.mvista.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 */
14
15#include <linux/clk.h>
16#include <linux/kernel.h>
17#include <linux/gfp.h>
18#include <linux/module.h>
19#include <linux/pm.h>
20#include <linux/interrupt.h>
21#include <linux/device.h>
22#include <linux/platform_device.h>
23#include <linux/libata.h>
24#include <linux/ahci_platform.h>
25#include <linux/phy/phy.h>
26#include <linux/pm_runtime.h>
27#include "ahci.h"
28
29static void ahci_host_stop(struct ata_host *host);
30
31struct ata_port_operations ahci_platform_ops = {
32 .inherits = &ahci_ops,
33 .host_stop = ahci_host_stop,
34};
35EXPORT_SYMBOL_GPL(ahci_platform_ops);
36
37static struct scsi_host_template ahci_platform_sht = {
38 AHCI_SHT("ahci_platform"),
39};
40
41/**
42 * ahci_platform_enable_clks - Enable platform clocks
43 * @hpriv: host private area to store config values
44 *
45 * This function enables all the clks found in hpriv->clks, starting at
46 * index 0. If any clk fails to enable it disables all the clks already
47 * enabled in reverse order, and then returns an error.
48 *
49 * RETURNS:
50 * 0 on success otherwise a negative error code
51 */
52int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
53{
54 int c, rc;
55
56 for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
57 rc = clk_prepare_enable(hpriv->clks[c]);
58 if (rc)
59 goto disable_unprepare_clk;
60 }
61 return 0;
62
63disable_unprepare_clk:
64 while (--c >= 0)
65 clk_disable_unprepare(hpriv->clks[c]);
66 return rc;
67}
68EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
69
70/**
71 * ahci_platform_disable_clks - Disable platform clocks
72 * @hpriv: host private area to store config values
73 *
74 * This function disables all the clks found in hpriv->clks, in reverse
75 * order of ahci_platform_enable_clks (starting at the end of the array).
76 */
77void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
78{
79 int c;
80
81 for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
82 if (hpriv->clks[c])
83 clk_disable_unprepare(hpriv->clks[c]);
84}
85EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
86
87/**
88 * ahci_platform_enable_resources - Enable platform resources
89 * @hpriv: host private area to store config values
90 *
91 * This function enables all ahci_platform managed resources in the
92 * following order:
93 * 1) Regulator
94 * 2) Clocks (through ahci_platform_enable_clks)
95 * 3) Phy
96 *
97 * If resource enabling fails at any point the previous enabled resources
98 * are disabled in reverse order.
99 *
100 * RETURNS:
101 * 0 on success otherwise a negative error code
102 */
103int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
104{
105 int rc;
106
107 if (hpriv->target_pwr) {
108 rc = regulator_enable(hpriv->target_pwr);
109 if (rc)
110 return rc;
111 }
112
113 rc = ahci_platform_enable_clks(hpriv);
114 if (rc)
115 goto disable_regulator;
116
117 if (hpriv->phy) {
118 rc = phy_init(hpriv->phy);
119 if (rc)
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
129 return 0;
130
131disable_clks:
132 ahci_platform_disable_clks(hpriv);
133
134disable_regulator:
135 if (hpriv->target_pwr)
136 regulator_disable(hpriv->target_pwr);
137 return rc;
138}
139EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
140
141/**
142 * ahci_platform_disable_resources - Disable platform resources
143 * @hpriv: host private area to store config values
144 *
145 * This function disables all ahci_platform managed resources in the
146 * following order:
147 * 1) Phy
148 * 2) Clocks (through ahci_platform_disable_clks)
149 * 3) Regulator
150 */
151void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
152{
153 if (hpriv->phy) {
154 phy_power_off(hpriv->phy);
155 phy_exit(hpriv->phy);
156 }
157
158 ahci_platform_disable_clks(hpriv);
159
160 if (hpriv->target_pwr)
161 regulator_disable(hpriv->target_pwr);
162}
163EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
164
165static void ahci_platform_put_resources(struct device *dev, void *res)
166{
167 struct ahci_host_priv *hpriv = res;
168 int c;
169
170 if (hpriv->got_runtime_pm) {
171 pm_runtime_put_sync(dev);
172 pm_runtime_disable(dev);
173 }
174
175 for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
176 clk_put(hpriv->clks[c]);
177}
178
179/**
180 * ahci_platform_get_resources - Get platform resources
181 * @pdev: platform device to get resources for
182 *
183 * This function allocates an ahci_host_priv struct, and gets the following
184 * resources, storing a reference to them inside the returned struct:
185 *
186 * 1) mmio registers (IORESOURCE_MEM 0, mandatory)
187 * 2) regulator for controlling the targets power (optional)
188 * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
189 * or for non devicetree enabled platforms a single clock
190 * 4) phy (optional)
191 *
192 * RETURNS:
193 * The allocated ahci_host_priv on success, otherwise an ERR_PTR value
194 */
195struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
196{
197 struct device *dev = &pdev->dev;
198 struct ahci_host_priv *hpriv;
199 struct clk *clk;
200 int i, rc = -ENOMEM;
201
202 if (!devres_open_group(dev, NULL, GFP_KERNEL))
203 return ERR_PTR(-ENOMEM);
204
205 hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
206 GFP_KERNEL);
207 if (!hpriv)
208 goto err_out;
209
210 devres_add(dev, hpriv);
211
212 hpriv->mmio = devm_ioremap_resource(dev,
213 platform_get_resource(pdev, IORESOURCE_MEM, 0));
214 if (IS_ERR(hpriv->mmio)) {
215 dev_err(dev, "no mmio space\n");
216 rc = PTR_ERR(hpriv->mmio);
217 goto err_out;
218 }
219
220 hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
221 if (IS_ERR(hpriv->target_pwr)) {
222 rc = PTR_ERR(hpriv->target_pwr);
223 if (rc == -EPROBE_DEFER)
224 goto err_out;
225 hpriv->target_pwr = NULL;
226 }
227
228 for (i = 0; i < AHCI_MAX_CLKS; i++) {
229 /*
230 * For now we must use clk_get(dev, NULL) for the first clock,
231 * because some platforms (da850, spear13xx) are not yet
232 * converted to use devicetree for clocks. For new platforms
233 * this is equivalent to of_clk_get(dev->of_node, 0).
234 */
235 if (i == 0)
236 clk = clk_get(dev, NULL);
237 else
238 clk = of_clk_get(dev->of_node, i);
239
240 if (IS_ERR(clk)) {
241 rc = PTR_ERR(clk);
242 if (rc == -EPROBE_DEFER)
243 goto err_out;
244 break;
245 }
246 hpriv->clks[i] = clk;
247 }
248
249 hpriv->phy = devm_phy_get(dev, "sata-phy");
250 if (IS_ERR(hpriv->phy)) {
251 rc = PTR_ERR(hpriv->phy);
252 switch (rc) {
253 case -ENODEV:
254 case -ENOSYS:
255 /* continue normally */
256 hpriv->phy = NULL;
257 break;
258
259 case -EPROBE_DEFER:
260 goto err_out;
261
262 default:
263 dev_err(dev, "couldn't get sata-phy\n");
264 goto err_out;
265 }
266 }
267
268 pm_runtime_enable(dev);
269 pm_runtime_get_sync(dev);
270 hpriv->got_runtime_pm = true;
271
272 devres_remove_group(dev, NULL);
273 return hpriv;
274
275err_out:
276 devres_release_group(dev, NULL);
277 return ERR_PTR(rc);
278}
279EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
280
281/**
282 * ahci_platform_init_host - Bring up an ahci-platform host
283 * @pdev: platform device pointer for the host
284 * @hpriv: ahci-host private data for the host
285 * @pi_template: template for the ata_port_info to use
286 * @force_port_map: param passed to ahci_save_initial_config
287 * @mask_port_map: param passed to ahci_save_initial_config
288 *
289 * This function does all the usual steps needed to bring up an
290 * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
291 * must be initialized / enabled before calling this.
292 *
293 * RETURNS:
294 * 0 on success otherwise a negative error code
295 */
296int ahci_platform_init_host(struct platform_device *pdev,
297 struct ahci_host_priv *hpriv,
298 const struct ata_port_info *pi_template,
299 unsigned int force_port_map,
300 unsigned int mask_port_map)
301{
302 struct device *dev = &pdev->dev;
303 struct ata_port_info pi = *pi_template;
304 const struct ata_port_info *ppi[] = { &pi, NULL };
305 struct ata_host *host;
306 int i, irq, n_ports, rc;
307
308 irq = platform_get_irq(pdev, 0);
309 if (irq <= 0) {
310 dev_err(dev, "no irq\n");
311 return -EINVAL;
312 }
313
314 /* prepare host */
315 hpriv->flags |= (unsigned long)pi.private_data;
316
317 ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map);
318
319 if (hpriv->cap & HOST_CAP_NCQ)
320 pi.flags |= ATA_FLAG_NCQ;
321
322 if (hpriv->cap & HOST_CAP_PMP)
323 pi.flags |= ATA_FLAG_PMP;
324
325 ahci_set_em_messages(hpriv, &pi);
326
327 /* CAP.NP sometimes indicate the index of the last enabled
328 * port, at other times, that of the last possible port, so
329 * determining the maximum port number requires looking at
330 * both CAP.NP and port_map.
331 */
332 n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
333
334 host = ata_host_alloc_pinfo(dev, ppi, n_ports);
335 if (!host)
336 return -ENOMEM;
337
338 host->private_data = hpriv;
339
340 if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
341 host->flags |= ATA_HOST_PARALLEL_SCAN;
342 else
343 dev_info(dev, "SSS flag set, parallel bus scan disabled\n");
344
345 if (pi.flags & ATA_FLAG_EM)
346 ahci_reset_em(host);
347
348 for (i = 0; i < host->n_ports; i++) {
349 struct ata_port *ap = host->ports[i];
350
351 ata_port_desc(ap, "mmio %pR",
352 platform_get_resource(pdev, IORESOURCE_MEM, 0));
353 ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
354
355 /* set enclosure management message type */
356 if (ap->flags & ATA_FLAG_EM)
357 ap->em_message_type = hpriv->em_msg_type;
358
359 /* disabled/not-implemented port */
360 if (!(hpriv->port_map & (1 << i)))
361 ap->ops = &ata_dummy_port_ops;
362 }
363
364 rc = ahci_reset_controller(host);
365 if (rc)
366 return rc;
367
368 ahci_init_controller(host);
369 ahci_print_info(host, "platform");
370
371 return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
372 &ahci_platform_sht);
373}
374EXPORT_SYMBOL_GPL(ahci_platform_init_host);
375
376static void ahci_host_stop(struct ata_host *host)
377{
378 struct device *dev = host->dev;
379 struct ahci_platform_data *pdata = dev_get_platdata(dev);
380 struct ahci_host_priv *hpriv = host->private_data;
381
382 if (pdata && pdata->exit)
383 pdata->exit(dev);
384
385 ahci_platform_disable_resources(hpriv);
386}
387
388#ifdef CONFIG_PM_SLEEP
389/**
390 * ahci_platform_suspend_host - Suspend an ahci-platform host
391 * @dev: device pointer for the host
392 *
393 * This function does all the usual steps needed to suspend an
394 * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
395 * must be disabled after calling this.
396 *
397 * RETURNS:
398 * 0 on success otherwise a negative error code
399 */
400int ahci_platform_suspend_host(struct device *dev)
401{
402 struct ata_host *host = dev_get_drvdata(dev);
403 struct ahci_host_priv *hpriv = host->private_data;
404 void __iomem *mmio = hpriv->mmio;
405 u32 ctl;
406
407 if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
408 dev_err(dev, "firmware update required for suspend/resume\n");
409 return -EIO;
410 }
411
412 /*
413 * AHCI spec rev1.1 section 8.3.3:
414 * Software must disable interrupts prior to requesting a
415 * transition of the HBA to D3 state.
416 */
417 ctl = readl(mmio + HOST_CTL);
418 ctl &= ~HOST_IRQ_EN;
419 writel(ctl, mmio + HOST_CTL);
420 readl(mmio + HOST_CTL); /* flush */
421
422 return ata_host_suspend(host, PMSG_SUSPEND);
423}
424EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
425
426/**
427 * ahci_platform_resume_host - Resume an ahci-platform host
428 * @dev: device pointer for the host
429 *
430 * This function does all the usual steps needed to resume an ahci-platform
431 * host, note any necessary resources (ie clks, phy, etc.) must be
432 * initialized / enabled before calling this.
433 *
434 * RETURNS:
435 * 0 on success otherwise a negative error code
436 */
437int ahci_platform_resume_host(struct device *dev)
438{
439 struct ata_host *host = dev_get_drvdata(dev);
440 int rc;
441
442 if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
443 rc = ahci_reset_controller(host);
444 if (rc)
445 return rc;
446
447 ahci_init_controller(host);
448 }
449
450 ata_host_resume(host);
451
452 return 0;
453}
454EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
455
456/**
457 * ahci_platform_suspend - Suspend an ahci-platform device
458 * @dev: the platform device to suspend
459 *
460 * This function suspends the host associated with the device, followed by
461 * disabling all the resources of the device.
462 *
463 * RETURNS:
464 * 0 on success otherwise a negative error code
465 */
466int ahci_platform_suspend(struct device *dev)
467{
468 struct ahci_platform_data *pdata = dev_get_platdata(dev);
469 struct ata_host *host = dev_get_drvdata(dev);
470 struct ahci_host_priv *hpriv = host->private_data;
471 int rc;
472
473 rc = ahci_platform_suspend_host(dev);
474 if (rc)
475 return rc;
476
477 if (pdata && pdata->suspend) {
478 rc = pdata->suspend(dev);
479 if (rc)
480 goto resume_host;
481 }
482
483 ahci_platform_disable_resources(hpriv);
484
485 return 0;
486
487resume_host:
488 ahci_platform_resume_host(dev);
489 return rc;
490}
491EXPORT_SYMBOL_GPL(ahci_platform_suspend);
492
493/**
494 * ahci_platform_resume - Resume an ahci-platform device
495 * @dev: the platform device to resume
496 *
497 * This function enables all the resources of the device followed by
498 * resuming the host associated with the device.
499 *
500 * RETURNS:
501 * 0 on success otherwise a negative error code
502 */
503int ahci_platform_resume(struct device *dev)
504{
505 struct ahci_platform_data *pdata = dev_get_platdata(dev);
506 struct ata_host *host = dev_get_drvdata(dev);
507 struct ahci_host_priv *hpriv = host->private_data;
508 int rc;
509
510 rc = ahci_platform_enable_resources(hpriv);
511 if (rc)
512 return rc;
513
514 if (pdata && pdata->resume) {
515 rc = pdata->resume(dev);
516 if (rc)
517 goto disable_resources;
518 }
519
520 rc = ahci_platform_resume_host(dev);
521 if (rc)
522 goto disable_resources;
523
524 /* We resumed so update PM runtime state */
525 pm_runtime_disable(dev);
526 pm_runtime_set_active(dev);
527 pm_runtime_enable(dev);
528
529 return 0;
530
531disable_resources:
532 ahci_platform_disable_resources(hpriv);
533
534 return rc;
535}
536EXPORT_SYMBOL_GPL(ahci_platform_resume);
537#endif
538
539MODULE_DESCRIPTION("AHCI SATA platform library");
540MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
541MODULE_LICENSE("GPL");