diff options
Diffstat (limited to 'drivers/ata/ahci_platform.c')
-rw-r--r-- | drivers/ata/ahci_platform.c | 515 |
1 files changed, 0 insertions, 515 deletions
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 | ||
29 | static void ahci_host_stop(struct ata_host *host); | ||
30 | |||
31 | struct ata_port_operations ahci_platform_ops = { | ||
32 | .inherits = &ahci_ops, | ||
33 | .host_stop = ahci_host_stop, | ||
34 | }; | ||
35 | EXPORT_SYMBOL_GPL(ahci_platform_ops); | ||
36 | |||
37 | static const struct ata_port_info ahci_port_info = { | 24 | static 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 | ||
44 | static 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 | */ | ||
59 | int 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 | |||
70 | disable_unprepare_clk: | ||
71 | while (--c >= 0) | ||
72 | clk_disable_unprepare(hpriv->clks[c]); | ||
73 | return rc; | ||
74 | } | ||
75 | EXPORT_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 | */ | ||
84 | void 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 | } | ||
92 | EXPORT_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 | */ | ||
110 | int 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 | |||
138 | disable_clks: | ||
139 | ahci_platform_disable_clks(hpriv); | ||
140 | |||
141 | disable_regulator: | ||
142 | if (hpriv->target_pwr) | ||
143 | regulator_disable(hpriv->target_pwr); | ||
144 | return rc; | ||
145 | } | ||
146 | EXPORT_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 | */ | ||
158 | void 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 | } | ||
170 | EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); | ||
171 | |||
172 | static 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 | */ | ||
202 | struct 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 | |||
282 | err_out: | ||
283 | devres_release_group(dev, NULL); | ||
284 | return ERR_PTR(rc); | ||
285 | } | ||
286 | EXPORT_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 | */ | ||
303 | int 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 | } | ||
381 | EXPORT_SYMBOL_GPL(ahci_platform_init_host); | ||
382 | |||
383 | static int ahci_probe(struct platform_device *pdev) | 31 | static 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 | ||
423 | static 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 | */ | ||
447 | int 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 | } | ||
471 | EXPORT_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 | */ | ||
484 | int 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 | } | ||
501 | EXPORT_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 | */ | ||
513 | int 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 | |||
534 | resume_host: | ||
535 | ahci_platform_resume_host(dev); | ||
536 | return rc; | ||
537 | } | ||
538 | EXPORT_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 | */ | ||
550 | int 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 | |||
578 | disable_resources: | ||
579 | ahci_platform_disable_resources(hpriv); | ||
580 | |||
581 | return rc; | ||
582 | } | ||
583 | EXPORT_SYMBOL_GPL(ahci_platform_resume); | ||
584 | #endif | ||
585 | |||
586 | static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, | 71 | static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, |
587 | ahci_platform_resume); | 72 | ahci_platform_resume); |
588 | 73 | ||