Commit | Line | Data |
---|---|---|
8d7c56d0 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
fd990556 BZ |
2 | /* |
3 | * AHCI SATA platform library | |
4 | * | |
5 | * Copyright 2004-2005 Red Hat, Inc. | |
6 | * Jeff Garzik <jgarzik@pobox.com> | |
7 | * Copyright 2010 MontaVista Software, LLC. | |
8 | * Anton Vorontsov <avorontsov@ru.mvista.com> | |
fd990556 BZ |
9 | */ |
10 | ||
11 | #include <linux/clk.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/gfp.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/pm.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/device.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/libata.h> | |
20 | #include <linux/ahci_platform.h> | |
21 | #include <linux/phy/phy.h> | |
22 | #include <linux/pm_runtime.h> | |
61e6ae71 | 23 | #include <linux/of.h> |
c7d7ddee | 24 | #include <linux/of_platform.h> |
9d2ab995 | 25 | #include <linux/reset.h> |
fd990556 BZ |
26 | #include "ahci.h" |
27 | ||
28 | static void ahci_host_stop(struct ata_host *host); | |
29 | ||
30 | struct ata_port_operations ahci_platform_ops = { | |
31 | .inherits = &ahci_ops, | |
32 | .host_stop = ahci_host_stop, | |
33 | }; | |
34 | EXPORT_SYMBOL_GPL(ahci_platform_ops); | |
35 | ||
b1a9edbd AT |
36 | /** |
37 | * ahci_platform_enable_phys - Enable PHYs | |
38 | * @hpriv: host private area to store config values | |
39 | * | |
40 | * This function enables all the PHYs found in hpriv->phys, if any. | |
41 | * If a PHY fails to be enabled, it disables all the PHYs already | |
42 | * enabled in reverse order and returns an error. | |
43 | * | |
44 | * RETURNS: | |
45 | * 0 on success otherwise a negative error code | |
46 | */ | |
84b032db | 47 | int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) |
b1a9edbd AT |
48 | { |
49 | int rc, i; | |
50 | ||
51 | for (i = 0; i < hpriv->nports; i++) { | |
b1a9edbd AT |
52 | rc = phy_init(hpriv->phys[i]); |
53 | if (rc) | |
54 | goto disable_phys; | |
55 | ||
49e54187 MR |
56 | rc = phy_set_mode(hpriv->phys[i], PHY_MODE_SATA); |
57 | if (rc) { | |
58 | phy_exit(hpriv->phys[i]); | |
59 | goto disable_phys; | |
60 | } | |
61 | ||
b1a9edbd | 62 | rc = phy_power_on(hpriv->phys[i]); |
ee995101 | 63 | if (rc) { |
b1a9edbd AT |
64 | phy_exit(hpriv->phys[i]); |
65 | goto disable_phys; | |
66 | } | |
67 | } | |
68 | ||
69 | return 0; | |
70 | ||
71 | disable_phys: | |
72 | while (--i >= 0) { | |
73 | phy_power_off(hpriv->phys[i]); | |
74 | phy_exit(hpriv->phys[i]); | |
75 | } | |
76 | return rc; | |
77 | } | |
84b032db | 78 | EXPORT_SYMBOL_GPL(ahci_platform_enable_phys); |
b1a9edbd AT |
79 | |
80 | /** | |
81 | * ahci_platform_disable_phys - Disable PHYs | |
82 | * @hpriv: host private area to store config values | |
83 | * | |
84 | * This function disables all PHYs found in hpriv->phys. | |
85 | */ | |
84b032db | 86 | void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) |
b1a9edbd AT |
87 | { |
88 | int i; | |
89 | ||
90 | for (i = 0; i < hpriv->nports; i++) { | |
b1a9edbd AT |
91 | phy_power_off(hpriv->phys[i]); |
92 | phy_exit(hpriv->phys[i]); | |
93 | } | |
94 | } | |
84b032db | 95 | EXPORT_SYMBOL_GPL(ahci_platform_disable_phys); |
b1a9edbd | 96 | |
6ce73f3a SS |
97 | /** |
98 | * ahci_platform_find_clk - Find platform clock | |
99 | * @hpriv: host private area to store config values | |
100 | * @con_id: clock connection ID | |
101 | * | |
102 | * This function returns a pointer to the clock descriptor of the clock with | |
103 | * the passed ID. | |
104 | * | |
105 | * RETURNS: | |
106 | * Pointer to the clock descriptor on success otherwise NULL | |
107 | */ | |
108 | struct clk *ahci_platform_find_clk(struct ahci_host_priv *hpriv, const char *con_id) | |
109 | { | |
110 | int i; | |
111 | ||
112 | for (i = 0; i < hpriv->n_clks; i++) { | |
d95d140e | 113 | if (hpriv->clks[i].id && !strcmp(hpriv->clks[i].id, con_id)) |
6ce73f3a SS |
114 | return hpriv->clks[i].clk; |
115 | } | |
116 | ||
117 | return NULL; | |
118 | } | |
119 | EXPORT_SYMBOL_GPL(ahci_platform_find_clk); | |
120 | ||
fd990556 BZ |
121 | /** |
122 | * ahci_platform_enable_clks - Enable platform clocks | |
123 | * @hpriv: host private area to store config values | |
124 | * | |
e28b3abf | 125 | * This function enables all the clks found for the AHCI device. |
fd990556 BZ |
126 | * |
127 | * RETURNS: | |
128 | * 0 on success otherwise a negative error code | |
129 | */ | |
130 | int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) | |
131 | { | |
e28b3abf | 132 | return clk_bulk_prepare_enable(hpriv->n_clks, hpriv->clks); |
fd990556 BZ |
133 | } |
134 | EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); | |
135 | ||
136 | /** | |
137 | * ahci_platform_disable_clks - Disable platform clocks | |
138 | * @hpriv: host private area to store config values | |
139 | * | |
e28b3abf SS |
140 | * This function disables all the clocks enabled before |
141 | * (bulk-clocks-disable function is supposed to do that in reverse | |
142 | * from the enabling procedure order). | |
fd990556 BZ |
143 | */ |
144 | void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) | |
145 | { | |
e28b3abf | 146 | clk_bulk_disable_unprepare(hpriv->n_clks, hpriv->clks); |
fd990556 BZ |
147 | } |
148 | EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); | |
149 | ||
f67f12ff SS |
150 | /** |
151 | * ahci_platform_deassert_rsts - Deassert/trigger platform resets | |
152 | * @hpriv: host private area to store config values | |
153 | * | |
154 | * This function deasserts or triggers all the reset lines found for | |
155 | * the AHCI device. | |
156 | * | |
157 | * RETURNS: | |
158 | * 0 on success otherwise a negative error code | |
159 | */ | |
160 | int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv) | |
161 | { | |
162 | if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) | |
163 | return reset_control_reset(hpriv->rsts); | |
164 | ||
165 | return reset_control_deassert(hpriv->rsts); | |
166 | } | |
167 | EXPORT_SYMBOL_GPL(ahci_platform_deassert_rsts); | |
168 | ||
169 | /** | |
170 | * ahci_platform_assert_rsts - Assert/rearm platform resets | |
171 | * @hpriv: host private area to store config values | |
172 | * | |
173 | * This function asserts or rearms (for self-deasserting resets) all | |
174 | * the reset controls found for the AHCI device. | |
175 | * | |
176 | * RETURNS: | |
177 | * 0 on success otherwise a negative error code | |
178 | */ | |
179 | int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv) | |
180 | { | |
181 | if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) | |
182 | return reset_control_rearm(hpriv->rsts); | |
183 | ||
184 | return reset_control_assert(hpriv->rsts); | |
185 | } | |
186 | EXPORT_SYMBOL_GPL(ahci_platform_assert_rsts); | |
187 | ||
c7d7ddee GC |
188 | /** |
189 | * ahci_platform_enable_regulators - Enable regulators | |
190 | * @hpriv: host private area to store config values | |
191 | * | |
a37da918 | 192 | * This function enables all the regulators found in controller and |
c7d7ddee GC |
193 | * hpriv->target_pwrs, if any. If a regulator fails to be enabled, it |
194 | * disables all the regulators already enabled in reverse order and | |
195 | * returns an error. | |
196 | * | |
197 | * RETURNS: | |
198 | * 0 on success otherwise a negative error code | |
199 | */ | |
200 | int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv) | |
201 | { | |
202 | int rc, i; | |
203 | ||
962399bb MB |
204 | rc = regulator_enable(hpriv->ahci_regulator); |
205 | if (rc) | |
206 | return rc; | |
a37da918 | 207 | |
962399bb MB |
208 | rc = regulator_enable(hpriv->phy_regulator); |
209 | if (rc) | |
210 | goto disable_ahci_pwrs; | |
f20fb266 | 211 | |
c7d7ddee GC |
212 | for (i = 0; i < hpriv->nports; i++) { |
213 | if (!hpriv->target_pwrs[i]) | |
214 | continue; | |
215 | ||
216 | rc = regulator_enable(hpriv->target_pwrs[i]); | |
217 | if (rc) | |
218 | goto disable_target_pwrs; | |
219 | } | |
220 | ||
221 | return 0; | |
222 | ||
223 | disable_target_pwrs: | |
224 | while (--i >= 0) | |
225 | if (hpriv->target_pwrs[i]) | |
226 | regulator_disable(hpriv->target_pwrs[i]); | |
227 | ||
962399bb | 228 | regulator_disable(hpriv->phy_regulator); |
f20fb266 | 229 | disable_ahci_pwrs: |
962399bb | 230 | regulator_disable(hpriv->ahci_regulator); |
c7d7ddee GC |
231 | return rc; |
232 | } | |
233 | EXPORT_SYMBOL_GPL(ahci_platform_enable_regulators); | |
234 | ||
235 | /** | |
236 | * ahci_platform_disable_regulators - Disable regulators | |
237 | * @hpriv: host private area to store config values | |
238 | * | |
a37da918 CL |
239 | * This function disables all regulators found in hpriv->target_pwrs and |
240 | * AHCI controller. | |
c7d7ddee GC |
241 | */ |
242 | void ahci_platform_disable_regulators(struct ahci_host_priv *hpriv) | |
243 | { | |
244 | int i; | |
245 | ||
246 | for (i = 0; i < hpriv->nports; i++) { | |
247 | if (!hpriv->target_pwrs[i]) | |
248 | continue; | |
249 | regulator_disable(hpriv->target_pwrs[i]); | |
250 | } | |
a37da918 | 251 | |
962399bb MB |
252 | regulator_disable(hpriv->ahci_regulator); |
253 | regulator_disable(hpriv->phy_regulator); | |
c7d7ddee GC |
254 | } |
255 | EXPORT_SYMBOL_GPL(ahci_platform_disable_regulators); | |
fd990556 BZ |
256 | /** |
257 | * ahci_platform_enable_resources - Enable platform resources | |
258 | * @hpriv: host private area to store config values | |
259 | * | |
260 | * This function enables all ahci_platform managed resources in the | |
261 | * following order: | |
262 | * 1) Regulator | |
263 | * 2) Clocks (through ahci_platform_enable_clks) | |
9d2ab995 KH |
264 | * 3) Resets |
265 | * 4) Phys | |
fd990556 BZ |
266 | * |
267 | * If resource enabling fails at any point the previous enabled resources | |
268 | * are disabled in reverse order. | |
269 | * | |
270 | * RETURNS: | |
271 | * 0 on success otherwise a negative error code | |
272 | */ | |
273 | int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) | |
274 | { | |
275 | int rc; | |
276 | ||
c7d7ddee GC |
277 | rc = ahci_platform_enable_regulators(hpriv); |
278 | if (rc) | |
279 | return rc; | |
fd990556 BZ |
280 | |
281 | rc = ahci_platform_enable_clks(hpriv); | |
282 | if (rc) | |
283 | goto disable_regulator; | |
284 | ||
f67f12ff | 285 | rc = ahci_platform_deassert_rsts(hpriv); |
f0f56716 | 286 | if (rc) |
fd17ed68 | 287 | goto disable_clks; |
f0f56716 | 288 | |
9d2ab995 KH |
289 | rc = ahci_platform_enable_phys(hpriv); |
290 | if (rc) | |
f67f12ff | 291 | goto disable_rsts; |
9d2ab995 | 292 | |
fd990556 BZ |
293 | return 0; |
294 | ||
f67f12ff SS |
295 | disable_rsts: |
296 | ahci_platform_assert_rsts(hpriv); | |
9d2ab995 | 297 | |
fd990556 BZ |
298 | disable_clks: |
299 | ahci_platform_disable_clks(hpriv); | |
300 | ||
301 | disable_regulator: | |
c7d7ddee GC |
302 | ahci_platform_disable_regulators(hpriv); |
303 | ||
fd990556 BZ |
304 | return rc; |
305 | } | |
306 | EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); | |
307 | ||
308 | /** | |
309 | * ahci_platform_disable_resources - Disable platform resources | |
310 | * @hpriv: host private area to store config values | |
311 | * | |
312 | * This function disables all ahci_platform managed resources in the | |
313 | * following order: | |
b1a9edbd | 314 | * 1) Phys |
9d2ab995 KH |
315 | * 2) Resets |
316 | * 3) Clocks (through ahci_platform_disable_clks) | |
317 | * 4) Regulator | |
fd990556 BZ |
318 | */ |
319 | void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) | |
320 | { | |
b1a9edbd | 321 | ahci_platform_disable_phys(hpriv); |
fd990556 | 322 | |
f67f12ff | 323 | ahci_platform_assert_rsts(hpriv); |
9d2ab995 | 324 | |
fd990556 BZ |
325 | ahci_platform_disable_clks(hpriv); |
326 | ||
c7d7ddee | 327 | ahci_platform_disable_regulators(hpriv); |
fd990556 BZ |
328 | } |
329 | EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); | |
330 | ||
331 | static void ahci_platform_put_resources(struct device *dev, void *res) | |
332 | { | |
333 | struct ahci_host_priv *hpriv = res; | |
334 | int c; | |
335 | ||
336 | if (hpriv->got_runtime_pm) { | |
eac7e072 | 337 | pm_runtime_put_sync(dev); |
fd990556 BZ |
338 | pm_runtime_disable(dev); |
339 | } | |
340 | ||
c7d7ddee GC |
341 | /* |
342 | * The regulators are tied to child node device and not to the | |
343 | * SATA device itself. So we can't use devm for automatically | |
344 | * releasing them. We have to do it manually here. | |
345 | */ | |
346 | for (c = 0; c < hpriv->nports; c++) | |
347 | if (hpriv->target_pwrs && hpriv->target_pwrs[c]) | |
348 | regulator_put(hpriv->target_pwrs[c]); | |
04ba9488 CL |
349 | |
350 | kfree(hpriv->target_pwrs); | |
c7d7ddee GC |
351 | } |
352 | ||
353 | static int ahci_platform_get_phy(struct ahci_host_priv *hpriv, u32 port, | |
354 | struct device *dev, struct device_node *node) | |
355 | { | |
356 | int rc; | |
357 | ||
358 | hpriv->phys[port] = devm_of_phy_get(dev, node, NULL); | |
359 | ||
360 | if (!IS_ERR(hpriv->phys[port])) | |
361 | return 0; | |
362 | ||
363 | rc = PTR_ERR(hpriv->phys[port]); | |
364 | switch (rc) { | |
365 | case -ENOSYS: | |
366 | /* No PHY support. Check if PHY is required. */ | |
e1e0a32e | 367 | if (of_property_present(node, "phys")) { |
c7d7ddee | 368 | dev_err(dev, |
2ce711f9 RH |
369 | "couldn't get PHY in node %pOFn: ENOSYS\n", |
370 | node); | |
c7d7ddee GC |
371 | break; |
372 | } | |
df561f66 | 373 | fallthrough; |
c7d7ddee GC |
374 | case -ENODEV: |
375 | /* continue normally */ | |
376 | hpriv->phys[port] = NULL; | |
377 | rc = 0; | |
378 | break; | |
090bb803 MR |
379 | case -EPROBE_DEFER: |
380 | /* Do not complain yet */ | |
381 | break; | |
c7d7ddee GC |
382 | |
383 | default: | |
384 | dev_err(dev, | |
2ce711f9 RH |
385 | "couldn't get PHY in node %pOFn: %d\n", |
386 | node, rc); | |
c7d7ddee GC |
387 | |
388 | break; | |
389 | } | |
390 | ||
391 | return rc; | |
392 | } | |
393 | ||
394 | static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, | |
395 | struct device *dev) | |
396 | { | |
397 | struct regulator *target_pwr; | |
398 | int rc = 0; | |
399 | ||
962399bb | 400 | target_pwr = regulator_get(dev, "target"); |
c7d7ddee GC |
401 | |
402 | if (!IS_ERR(target_pwr)) | |
403 | hpriv->target_pwrs[port] = target_pwr; | |
404 | else | |
405 | rc = PTR_ERR(target_pwr); | |
406 | ||
407 | return rc; | |
fd990556 BZ |
408 | } |
409 | ||
18ee7c49 SS |
410 | static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv, |
411 | struct device *dev) | |
412 | { | |
413 | struct device_node *child; | |
414 | u32 port; | |
415 | ||
416 | if (!of_property_read_u32(dev->of_node, "hba-cap", &hpriv->saved_cap)) | |
417 | hpriv->saved_cap &= (HOST_CAP_SSS | HOST_CAP_MPS); | |
418 | ||
419 | of_property_read_u32(dev->of_node, | |
420 | "ports-implemented", &hpriv->saved_port_map); | |
421 | ||
422 | for_each_child_of_node(dev->of_node, child) { | |
423 | if (!of_device_is_available(child)) | |
424 | continue; | |
425 | ||
426 | if (of_property_read_u32(child, "reg", &port)) { | |
427 | of_node_put(child); | |
428 | return -EINVAL; | |
429 | } | |
430 | ||
431 | if (!of_property_read_u32(child, "hba-port-cap", &hpriv->saved_port_cap[port])) | |
432 | hpriv->saved_port_cap[port] &= PORT_CMD_CAP; | |
433 | } | |
434 | ||
435 | return 0; | |
436 | } | |
437 | ||
fd990556 BZ |
438 | /** |
439 | * ahci_platform_get_resources - Get platform resources | |
440 | * @pdev: platform device to get resources for | |
16af2d65 | 441 | * @flags: bitmap representing the resource to get |
fd990556 BZ |
442 | * |
443 | * This function allocates an ahci_host_priv struct, and gets the following | |
444 | * resources, storing a reference to them inside the returned struct: | |
445 | * | |
446 | * 1) mmio registers (IORESOURCE_MEM 0, mandatory) | |
447 | * 2) regulator for controlling the targets power (optional) | |
a37da918 | 448 | * regulator for controlling the AHCI controller (optional) |
e28b3abf SS |
449 | * 3) all clocks specified in the devicetree node, or a single |
450 | * clock for non-OF platforms (optional) | |
9d2ab995 KH |
451 | * 4) resets, if flags has AHCI_PLATFORM_GET_RESETS (optional) |
452 | * 5) phys (optional) | |
fd990556 BZ |
453 | * |
454 | * RETURNS: | |
455 | * The allocated ahci_host_priv on success, otherwise an ERR_PTR value | |
456 | */ | |
16af2d65 KH |
457 | struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, |
458 | unsigned int flags) | |
fd990556 | 459 | { |
e28b3abf | 460 | int child_nodes, rc = -ENOMEM, enabled_ports = 0; |
fd990556 BZ |
461 | struct device *dev = &pdev->dev; |
462 | struct ahci_host_priv *hpriv; | |
b1a9edbd | 463 | struct device_node *child; |
b1a9edbd | 464 | u32 mask_port_map = 0; |
fd990556 BZ |
465 | |
466 | if (!devres_open_group(dev, NULL, GFP_KERNEL)) | |
467 | return ERR_PTR(-ENOMEM); | |
468 | ||
469 | hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), | |
470 | GFP_KERNEL); | |
471 | if (!hpriv) | |
472 | goto err_out; | |
473 | ||
474 | devres_add(dev, hpriv); | |
475 | ||
82d437e6 SS |
476 | /* |
477 | * If the DT provided an "ahci" named resource, use it. Otherwise, | |
478 | * fallback to using the default first resource for the device node. | |
479 | */ | |
480 | if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci")) | |
481 | hpriv->mmio = devm_platform_ioremap_resource_byname(pdev, "ahci"); | |
482 | else | |
483 | hpriv->mmio = devm_platform_ioremap_resource(pdev, 0); | |
fd990556 | 484 | if (IS_ERR(hpriv->mmio)) { |
fd990556 BZ |
485 | rc = PTR_ERR(hpriv->mmio); |
486 | goto err_out; | |
487 | } | |
488 | ||
e28b3abf SS |
489 | /* |
490 | * Bulk clocks getting procedure can fail to find any clock due to | |
491 | * running on a non-OF platform or due to the clocks being defined in | |
492 | * bypass of the DT firmware (like da850, spear13xx). In that case we | |
493 | * fallback to getting a single clock source right from the dev clocks | |
494 | * list. | |
495 | */ | |
496 | rc = devm_clk_bulk_get_all(dev, &hpriv->clks); | |
497 | if (rc < 0) | |
498 | goto err_out; | |
499 | ||
500 | if (rc > 0) { | |
501 | /* Got clocks in bulk */ | |
502 | hpriv->n_clks = rc; | |
503 | } else { | |
fd990556 | 504 | /* |
e28b3abf SS |
505 | * No clock bulk found: fallback to manually getting |
506 | * the optional clock. | |
fd990556 | 507 | */ |
e28b3abf SS |
508 | hpriv->clks = devm_kzalloc(dev, sizeof(*hpriv->clks), GFP_KERNEL); |
509 | if (!hpriv->clks) { | |
510 | rc = -ENOMEM; | |
511 | goto err_out; | |
512 | } | |
513 | hpriv->clks->clk = devm_clk_get_optional(dev, NULL); | |
514 | if (IS_ERR(hpriv->clks->clk)) { | |
515 | rc = PTR_ERR(hpriv->clks->clk); | |
516 | goto err_out; | |
517 | } else if (hpriv->clks->clk) { | |
518 | hpriv->clks->id = "ahci"; | |
519 | hpriv->n_clks = 1; | |
fd990556 | 520 | } |
fd990556 BZ |
521 | } |
522 | ||
962399bb | 523 | hpriv->ahci_regulator = devm_regulator_get(dev, "ahci"); |
a37da918 CL |
524 | if (IS_ERR(hpriv->ahci_regulator)) { |
525 | rc = PTR_ERR(hpriv->ahci_regulator); | |
962399bb | 526 | if (rc != 0) |
a37da918 | 527 | goto err_out; |
a37da918 CL |
528 | } |
529 | ||
962399bb | 530 | hpriv->phy_regulator = devm_regulator_get(dev, "phy"); |
f20fb266 CL |
531 | if (IS_ERR(hpriv->phy_regulator)) { |
532 | rc = PTR_ERR(hpriv->phy_regulator); | |
776c7501 | 533 | goto err_out; |
f20fb266 CL |
534 | } |
535 | ||
9d2ab995 KH |
536 | if (flags & AHCI_PLATFORM_GET_RESETS) { |
537 | hpriv->rsts = devm_reset_control_array_get_optional_shared(dev); | |
538 | if (IS_ERR(hpriv->rsts)) { | |
539 | rc = PTR_ERR(hpriv->rsts); | |
540 | goto err_out; | |
541 | } | |
f67f12ff SS |
542 | |
543 | hpriv->f_rsts = flags & AHCI_PLATFORM_RST_TRIGGER; | |
9d2ab995 KH |
544 | } |
545 | ||
3c132ea6 SS |
546 | /* |
547 | * Too many sub-nodes most likely means having something wrong with | |
548 | * the firmware. | |
549 | */ | |
550 | child_nodes = of_get_child_count(dev->of_node); | |
551 | if (child_nodes > AHCI_MAX_PORTS) { | |
552 | rc = -EINVAL; | |
553 | goto err_out; | |
554 | } | |
b1a9edbd | 555 | |
c7d7ddee GC |
556 | /* |
557 | * If no sub-node was found, we still need to set nports to | |
558 | * one in order to be able to use the | |
559 | * ahci_platform_[en|dis]able_[phys|regulators] functions. | |
560 | */ | |
3c132ea6 SS |
561 | if (child_nodes) |
562 | hpriv->nports = child_nodes; | |
563 | else | |
c7d7ddee GC |
564 | hpriv->nports = 1; |
565 | ||
a4b9f5ed | 566 | hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); |
c7d7ddee GC |
567 | if (!hpriv->phys) { |
568 | rc = -ENOMEM; | |
569 | goto err_out; | |
570 | } | |
04ba9488 CL |
571 | /* |
572 | * We cannot use devm_ here, since ahci_platform_put_resources() uses | |
573 | * target_pwrs after devm_ have freed memory | |
574 | */ | |
575 | hpriv->target_pwrs = kcalloc(hpriv->nports, sizeof(*hpriv->target_pwrs), GFP_KERNEL); | |
c7d7ddee GC |
576 | if (!hpriv->target_pwrs) { |
577 | rc = -ENOMEM; | |
578 | goto err_out; | |
579 | } | |
b1a9edbd | 580 | |
c7d7ddee | 581 | if (child_nodes) { |
b1a9edbd AT |
582 | for_each_child_of_node(dev->of_node, child) { |
583 | u32 port; | |
f627cfde | 584 | struct platform_device *port_dev __maybe_unused; |
b1a9edbd AT |
585 | |
586 | if (!of_device_is_available(child)) | |
587 | continue; | |
588 | ||
589 | if (of_property_read_u32(child, "reg", &port)) { | |
590 | rc = -EINVAL; | |
d7f76f36 | 591 | of_node_put(child); |
acbd5733 MP |
592 | goto err_out; |
593 | } | |
fd990556 | 594 | |
b1a9edbd AT |
595 | if (port >= hpriv->nports) { |
596 | dev_warn(dev, "invalid port number %d\n", port); | |
597 | continue; | |
598 | } | |
b1a9edbd AT |
599 | mask_port_map |= BIT(port); |
600 | ||
f627cfde | 601 | #ifdef CONFIG_OF_ADDRESS |
c7d7ddee GC |
602 | of_platform_device_create(child, NULL, NULL); |
603 | ||
604 | port_dev = of_find_device_by_node(child); | |
605 | ||
606 | if (port_dev) { | |
607 | rc = ahci_platform_get_regulator(hpriv, port, | |
608 | &port_dev->dev); | |
d7f76f36 ND |
609 | if (rc == -EPROBE_DEFER) { |
610 | of_node_put(child); | |
c7d7ddee | 611 | goto err_out; |
d7f76f36 | 612 | } |
b1a9edbd | 613 | } |
f627cfde | 614 | #endif |
fd990556 | 615 | |
c7d7ddee | 616 | rc = ahci_platform_get_phy(hpriv, port, dev, child); |
d7f76f36 ND |
617 | if (rc) { |
618 | of_node_put(child); | |
c7d7ddee | 619 | goto err_out; |
d7f76f36 | 620 | } |
c7d7ddee | 621 | |
b1a9edbd AT |
622 | enabled_ports++; |
623 | } | |
624 | if (!enabled_ports) { | |
625 | dev_warn(dev, "No port enabled\n"); | |
626 | rc = -ENODEV; | |
fd990556 BZ |
627 | goto err_out; |
628 | } | |
b1a9edbd AT |
629 | |
630 | if (!hpriv->mask_port_map) | |
631 | hpriv->mask_port_map = mask_port_map; | |
632 | } else { | |
633 | /* | |
634 | * If no sub-node was found, keep this for device tree | |
635 | * compatibility | |
636 | */ | |
c7d7ddee GC |
637 | rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node); |
638 | if (rc) | |
639 | goto err_out; | |
b1a9edbd | 640 | |
c7d7ddee GC |
641 | rc = ahci_platform_get_regulator(hpriv, 0, dev); |
642 | if (rc == -EPROBE_DEFER) | |
643 | goto err_out; | |
fd990556 | 644 | } |
18ee7c49 SS |
645 | |
646 | /* | |
647 | * Retrieve firmware-specific flags which then will be used to set | |
648 | * the HW-init fields of HBA and its ports | |
649 | */ | |
650 | rc = ahci_platform_get_firmware(hpriv, dev); | |
651 | if (rc) | |
652 | goto err_out; | |
653 | ||
fd990556 | 654 | pm_runtime_enable(dev); |
eac7e072 | 655 | pm_runtime_get_sync(dev); |
fd990556 BZ |
656 | hpriv->got_runtime_pm = true; |
657 | ||
658 | devres_remove_group(dev, NULL); | |
659 | return hpriv; | |
660 | ||
661 | err_out: | |
662 | devres_release_group(dev, NULL); | |
663 | return ERR_PTR(rc); | |
664 | } | |
665 | EXPORT_SYMBOL_GPL(ahci_platform_get_resources); | |
666 | ||
667 | /** | |
668 | * ahci_platform_init_host - Bring up an ahci-platform host | |
669 | * @pdev: platform device pointer for the host | |
670 | * @hpriv: ahci-host private data for the host | |
671 | * @pi_template: template for the ata_port_info to use | |
018d5ef2 | 672 | * @sht: scsi_host_template to use when registering |
fd990556 BZ |
673 | * |
674 | * This function does all the usual steps needed to bring up an | |
b1a9edbd | 675 | * ahci-platform host, note any necessary resources (ie clks, phys, etc.) |
fd990556 BZ |
676 | * must be initialized / enabled before calling this. |
677 | * | |
678 | * RETURNS: | |
679 | * 0 on success otherwise a negative error code | |
680 | */ | |
681 | int ahci_platform_init_host(struct platform_device *pdev, | |
682 | struct ahci_host_priv *hpriv, | |
018d5ef2 | 683 | const struct ata_port_info *pi_template, |
25df73d9 | 684 | const struct scsi_host_template *sht) |
fd990556 BZ |
685 | { |
686 | struct device *dev = &pdev->dev; | |
687 | struct ata_port_info pi = *pi_template; | |
688 | const struct ata_port_info *ppi[] = { &pi, NULL }; | |
689 | struct ata_host *host; | |
690 | int i, irq, n_ports, rc; | |
691 | ||
692 | irq = platform_get_irq(pdev, 0); | |
7b6acb4e | 693 | if (irq < 0) |
c034640a | 694 | return irq; |
b30d0040 SS |
695 | if (!irq) |
696 | return -EINVAL; | |
fd990556 | 697 | |
21bfd1aa RR |
698 | hpriv->irq = irq; |
699 | ||
fd990556 | 700 | /* prepare host */ |
c4121c65 | 701 | pi.private_data = (void *)(unsigned long)hpriv->flags; |
fd990556 | 702 | |
725c7b57 | 703 | ahci_save_initial_config(dev, hpriv); |
fd990556 BZ |
704 | |
705 | if (hpriv->cap & HOST_CAP_NCQ) | |
706 | pi.flags |= ATA_FLAG_NCQ; | |
707 | ||
708 | if (hpriv->cap & HOST_CAP_PMP) | |
709 | pi.flags |= ATA_FLAG_PMP; | |
710 | ||
711 | ahci_set_em_messages(hpriv, &pi); | |
712 | ||
713 | /* CAP.NP sometimes indicate the index of the last enabled | |
714 | * port, at other times, that of the last possible port, so | |
715 | * determining the maximum port number requires looking at | |
716 | * both CAP.NP and port_map. | |
717 | */ | |
718 | n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); | |
719 | ||
720 | host = ata_host_alloc_pinfo(dev, ppi, n_ports); | |
721 | if (!host) | |
722 | return -ENOMEM; | |
723 | ||
724 | host->private_data = hpriv; | |
725 | ||
726 | if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) | |
727 | host->flags |= ATA_HOST_PARALLEL_SCAN; | |
728 | else | |
729 | dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); | |
730 | ||
731 | if (pi.flags & ATA_FLAG_EM) | |
732 | ahci_reset_em(host); | |
733 | ||
734 | for (i = 0; i < host->n_ports; i++) { | |
735 | struct ata_port *ap = host->ports[i]; | |
736 | ||
737 | ata_port_desc(ap, "mmio %pR", | |
738 | platform_get_resource(pdev, IORESOURCE_MEM, 0)); | |
739 | ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); | |
740 | ||
741 | /* set enclosure management message type */ | |
742 | if (ap->flags & ATA_FLAG_EM) | |
743 | ap->em_message_type = hpriv->em_msg_type; | |
744 | ||
745 | /* disabled/not-implemented port */ | |
746 | if (!(hpriv->port_map & (1 << i))) | |
747 | ap->ops = &ata_dummy_port_ops; | |
748 | } | |
749 | ||
cc7a9e27 SS |
750 | if (hpriv->cap & HOST_CAP_64) { |
751 | rc = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); | |
752 | if (rc) { | |
ea63a899 AS |
753 | dev_err(dev, "Failed to enable 64-bit DMA.\n"); |
754 | return rc; | |
cc7a9e27 SS |
755 | } |
756 | } | |
757 | ||
fd990556 BZ |
758 | rc = ahci_reset_controller(host); |
759 | if (rc) | |
760 | return rc; | |
761 | ||
762 | ahci_init_controller(host); | |
763 | ahci_print_info(host, "platform"); | |
764 | ||
21bfd1aa | 765 | return ahci_host_activate(host, sht); |
fd990556 BZ |
766 | } |
767 | EXPORT_SYMBOL_GPL(ahci_platform_init_host); | |
768 | ||
769 | static void ahci_host_stop(struct ata_host *host) | |
770 | { | |
fd990556 BZ |
771 | struct ahci_host_priv *hpriv = host->private_data; |
772 | ||
fd990556 BZ |
773 | ahci_platform_disable_resources(hpriv); |
774 | } | |
775 | ||
8eede5bc NW |
776 | /** |
777 | * ahci_platform_shutdown - Disable interrupts and stop DMA for host ports | |
7cf5fc65 | 778 | * @pdev: platform device pointer for the host |
8eede5bc NW |
779 | * |
780 | * This function is called during system shutdown and performs the minimal | |
781 | * deconfiguration required to ensure that an ahci_platform host cannot | |
782 | * corrupt or otherwise interfere with a new kernel being started with kexec. | |
783 | */ | |
784 | void ahci_platform_shutdown(struct platform_device *pdev) | |
785 | { | |
786 | struct ata_host *host = platform_get_drvdata(pdev); | |
787 | struct ahci_host_priv *hpriv = host->private_data; | |
788 | void __iomem *mmio = hpriv->mmio; | |
789 | int i; | |
790 | ||
791 | for (i = 0; i < host->n_ports; i++) { | |
792 | struct ata_port *ap = host->ports[i]; | |
793 | ||
794 | /* Disable port interrupts */ | |
795 | if (ap->ops->freeze) | |
796 | ap->ops->freeze(ap); | |
797 | ||
798 | /* Stop the port DMA engines */ | |
799 | if (ap->ops->port_stop) | |
800 | ap->ops->port_stop(ap); | |
801 | } | |
802 | ||
803 | /* Disable and clear host interrupts */ | |
804 | writel(readl(mmio + HOST_CTL) & ~HOST_IRQ_EN, mmio + HOST_CTL); | |
805 | readl(mmio + HOST_CTL); /* flush */ | |
806 | writel(GENMASK(host->n_ports, 0), mmio + HOST_IRQ_STAT); | |
807 | } | |
808 | EXPORT_SYMBOL_GPL(ahci_platform_shutdown); | |
809 | ||
fd990556 BZ |
810 | #ifdef CONFIG_PM_SLEEP |
811 | /** | |
812 | * ahci_platform_suspend_host - Suspend an ahci-platform host | |
813 | * @dev: device pointer for the host | |
814 | * | |
815 | * This function does all the usual steps needed to suspend an | |
b1a9edbd | 816 | * ahci-platform host, note any necessary resources (ie clks, phys, etc.) |
fd990556 BZ |
817 | * must be disabled after calling this. |
818 | * | |
819 | * RETURNS: | |
820 | * 0 on success otherwise a negative error code | |
821 | */ | |
822 | int ahci_platform_suspend_host(struct device *dev) | |
823 | { | |
824 | struct ata_host *host = dev_get_drvdata(dev); | |
825 | struct ahci_host_priv *hpriv = host->private_data; | |
826 | void __iomem *mmio = hpriv->mmio; | |
827 | u32 ctl; | |
828 | ||
829 | if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { | |
830 | dev_err(dev, "firmware update required for suspend/resume\n"); | |
831 | return -EIO; | |
832 | } | |
833 | ||
834 | /* | |
835 | * AHCI spec rev1.1 section 8.3.3: | |
836 | * Software must disable interrupts prior to requesting a | |
837 | * transition of the HBA to D3 state. | |
838 | */ | |
839 | ctl = readl(mmio + HOST_CTL); | |
840 | ctl &= ~HOST_IRQ_EN; | |
841 | writel(ctl, mmio + HOST_CTL); | |
842 | readl(mmio + HOST_CTL); /* flush */ | |
843 | ||
49e54187 MR |
844 | if (hpriv->flags & AHCI_HFLAG_SUSPEND_PHYS) |
845 | ahci_platform_disable_phys(hpriv); | |
846 | ||
ec87cf37 SS |
847 | ata_host_suspend(host, PMSG_SUSPEND); |
848 | return 0; | |
fd990556 BZ |
849 | } |
850 | EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); | |
851 | ||
852 | /** | |
853 | * ahci_platform_resume_host - Resume an ahci-platform host | |
854 | * @dev: device pointer for the host | |
855 | * | |
856 | * This function does all the usual steps needed to resume an ahci-platform | |
b1a9edbd | 857 | * host, note any necessary resources (ie clks, phys, etc.) must be |
fd990556 BZ |
858 | * initialized / enabled before calling this. |
859 | * | |
860 | * RETURNS: | |
861 | * 0 on success otherwise a negative error code | |
862 | */ | |
863 | int ahci_platform_resume_host(struct device *dev) | |
864 | { | |
865 | struct ata_host *host = dev_get_drvdata(dev); | |
49e54187 | 866 | struct ahci_host_priv *hpriv = host->private_data; |
fd990556 BZ |
867 | int rc; |
868 | ||
869 | if (dev->power.power_state.event == PM_EVENT_SUSPEND) { | |
870 | rc = ahci_reset_controller(host); | |
871 | if (rc) | |
872 | return rc; | |
873 | ||
874 | ahci_init_controller(host); | |
875 | } | |
876 | ||
49e54187 MR |
877 | if (hpriv->flags & AHCI_HFLAG_SUSPEND_PHYS) |
878 | ahci_platform_enable_phys(hpriv); | |
879 | ||
fd990556 BZ |
880 | ata_host_resume(host); |
881 | ||
882 | return 0; | |
883 | } | |
884 | EXPORT_SYMBOL_GPL(ahci_platform_resume_host); | |
885 | ||
886 | /** | |
887 | * ahci_platform_suspend - Suspend an ahci-platform device | |
888 | * @dev: the platform device to suspend | |
889 | * | |
890 | * This function suspends the host associated with the device, followed by | |
891 | * disabling all the resources of the device. | |
892 | * | |
893 | * RETURNS: | |
894 | * 0 on success otherwise a negative error code | |
895 | */ | |
896 | int ahci_platform_suspend(struct device *dev) | |
897 | { | |
fd990556 BZ |
898 | struct ata_host *host = dev_get_drvdata(dev); |
899 | struct ahci_host_priv *hpriv = host->private_data; | |
900 | int rc; | |
901 | ||
eac7e072 | 902 | rc = ahci_platform_suspend_host(dev); |
fd990556 BZ |
903 | if (rc) |
904 | return rc; | |
905 | ||
eac7e072 | 906 | ahci_platform_disable_resources(hpriv); |
fd990556 BZ |
907 | |
908 | return 0; | |
fd990556 | 909 | } |
eac7e072 | 910 | EXPORT_SYMBOL_GPL(ahci_platform_suspend); |
fd990556 BZ |
911 | |
912 | /** | |
913 | * ahci_platform_resume - Resume an ahci-platform device | |
914 | * @dev: the platform device to resume | |
915 | * | |
916 | * This function enables all the resources of the device followed by | |
917 | * resuming the host associated with the device. | |
918 | * | |
919 | * RETURNS: | |
920 | * 0 on success otherwise a negative error code | |
921 | */ | |
922 | int ahci_platform_resume(struct device *dev) | |
923 | { | |
eac7e072 TH |
924 | struct ata_host *host = dev_get_drvdata(dev); |
925 | struct ahci_host_priv *hpriv = host->private_data; | |
fd990556 BZ |
926 | int rc; |
927 | ||
eac7e072 | 928 | rc = ahci_platform_enable_resources(hpriv); |
fd990556 BZ |
929 | if (rc) |
930 | return rc; | |
931 | ||
eac7e072 TH |
932 | rc = ahci_platform_resume_host(dev); |
933 | if (rc) | |
934 | goto disable_resources; | |
935 | ||
fd990556 BZ |
936 | /* We resumed so update PM runtime state */ |
937 | pm_runtime_disable(dev); | |
938 | pm_runtime_set_active(dev); | |
939 | pm_runtime_enable(dev); | |
940 | ||
941 | return 0; | |
aece27a2 | 942 | |
eac7e072 TH |
943 | disable_resources: |
944 | ahci_platform_disable_resources(hpriv); | |
aece27a2 | 945 | |
eac7e072 TH |
946 | return rc; |
947 | } | |
948 | EXPORT_SYMBOL_GPL(ahci_platform_resume); | |
fd990556 BZ |
949 | #endif |
950 | ||
951 | MODULE_DESCRIPTION("AHCI SATA platform library"); | |
952 | MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); | |
953 | MODULE_LICENSE("GPL"); |