diff options
author | Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> | 2014-03-25 14:51:39 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2014-03-25 15:17:13 -0400 |
commit | fd990556f0fa25446c6bfa9cf4c9e49d387d4472 (patch) | |
tree | 0020bd48b0312034f1d84d4560e323961d052669 /drivers/ata | |
parent | 83b03fd67b9b3fa3795871169f3c08c35b3d6ea8 (diff) |
ata: move library code from ahci_platform.c to libahci_platform.c
Move AHCI platform library code from ahci_platform.c to
libahci_platform.c and fix dependencies for ahci_st,
ahci_imx and ahci_sunxi drivers.
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/Kconfig | 7 | ||||
-rw-r--r-- | drivers/ata/Makefile | 10 | ||||
-rw-r--r-- | drivers/ata/ahci_platform.c | 515 | ||||
-rw-r--r-- | drivers/ata/libahci_platform.c | 541 |
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 | ||
100 | config AHCI_ST | 100 | config 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 | ||
109 | config AHCI_IMX | 108 | config 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 | ||
118 | config AHCI_SUNXI | 117 | config 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 | ||
127 | config AHCI_XGENE | 126 | config 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 |
5 | obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o | 5 | obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o |
6 | obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o | 6 | obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o |
7 | obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o | 7 | obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o |
8 | obj-$(CONFIG_SATA_FSL) += sata_fsl.o | 8 | obj-$(CONFIG_SATA_FSL) += sata_fsl.o |
9 | obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o | 9 | obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o |
10 | obj-$(CONFIG_SATA_SIL24) += sata_sil24.o | 10 | obj-$(CONFIG_SATA_SIL24) += sata_sil24.o |
11 | obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o | 11 | obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o |
12 | obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o | 12 | obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o |
13 | obj-$(CONFIG_AHCI_IMX) += ahci_imx.o | 13 | obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o |
14 | obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o | 14 | obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o |
15 | obj-$(CONFIG_AHCI_ST) += ahci_st.o | 15 | obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o |
16 | obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o | 16 | obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o |
17 | 17 | ||
18 | # SFF w/ custom DMA | 18 | # SFF w/ custom DMA |
19 | obj-$(CONFIG_PDC_ADMA) += pdc_adma.o | 19 | obj-$(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 | ||
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 | ||
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 | |||
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 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 | */ | ||
52 | int 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 | |||
63 | disable_unprepare_clk: | ||
64 | while (--c >= 0) | ||
65 | clk_disable_unprepare(hpriv->clks[c]); | ||
66 | return rc; | ||
67 | } | ||
68 | EXPORT_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 | */ | ||
77 | void 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 | } | ||
85 | EXPORT_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 | */ | ||
103 | int 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 | |||
131 | disable_clks: | ||
132 | ahci_platform_disable_clks(hpriv); | ||
133 | |||
134 | disable_regulator: | ||
135 | if (hpriv->target_pwr) | ||
136 | regulator_disable(hpriv->target_pwr); | ||
137 | return rc; | ||
138 | } | ||
139 | EXPORT_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 | */ | ||
151 | void 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 | } | ||
163 | EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); | ||
164 | |||
165 | static 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 | */ | ||
195 | struct 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 | |||
275 | err_out: | ||
276 | devres_release_group(dev, NULL); | ||
277 | return ERR_PTR(rc); | ||
278 | } | ||
279 | EXPORT_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 | */ | ||
296 | int 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 | } | ||
374 | EXPORT_SYMBOL_GPL(ahci_platform_init_host); | ||
375 | |||
376 | static 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 | */ | ||
400 | int 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 | } | ||
424 | EXPORT_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 | */ | ||
437 | int 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 | } | ||
454 | EXPORT_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 | */ | ||
466 | int 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 | |||
487 | resume_host: | ||
488 | ahci_platform_resume_host(dev); | ||
489 | return rc; | ||
490 | } | ||
491 | EXPORT_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 | */ | ||
503 | int 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 | |||
531 | disable_resources: | ||
532 | ahci_platform_disable_resources(hpriv); | ||
533 | |||
534 | return rc; | ||
535 | } | ||
536 | EXPORT_SYMBOL_GPL(ahci_platform_resume); | ||
537 | #endif | ||
538 | |||
539 | MODULE_DESCRIPTION("AHCI SATA platform library"); | ||
540 | MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); | ||
541 | MODULE_LICENSE("GPL"); | ||