diff options
author | Brian Norris <computersforpeace@gmail.com> | 2012-11-02 03:46:17 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2012-12-03 05:15:15 -0500 |
commit | 1896b15eddb4ff7266bccf3efa981885ecf80ab1 (patch) | |
tree | 81d96f5b9db3cabf778346d39ece8d7a571ab2dc /drivers/ata/ahci_platform.c | |
parent | 9a99e4768a6dcfa50d1cda1b2f55c9ea5c4213cc (diff) |
ahci_platform: perform platform exit in host_stop() hook
AHCI platform devices may provide an exit() routine, via
ahci_platform_data, that powers off the SATA core. Such a routine should
be executed from the ata_port_operations host_stop() hook. That way, the
ATA subsystem can perform any last-minute hardware cleanup (via devres,
for example), then trigger the power-off at the appropriate time.
This patch fixes bus errors triggered during module removal or device
unbinding, seen on an SoC SATA core.
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/ahci_platform.c')
-rw-r--r-- | drivers/ata/ahci_platform.c | 30 |
1 files changed, 23 insertions, 7 deletions
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index fbbfa2bccbcd..5aa4309d4481 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c | |||
@@ -25,6 +25,8 @@ | |||
25 | #include <linux/ahci_platform.h> | 25 | #include <linux/ahci_platform.h> |
26 | #include "ahci.h" | 26 | #include "ahci.h" |
27 | 27 | ||
28 | static void ahci_host_stop(struct ata_host *host); | ||
29 | |||
28 | enum ahci_type { | 30 | enum ahci_type { |
29 | AHCI, /* standard platform ahci */ | 31 | AHCI, /* standard platform ahci */ |
30 | IMX53_AHCI, /* ahci on i.mx53 */ | 32 | IMX53_AHCI, /* ahci on i.mx53 */ |
@@ -47,6 +49,15 @@ static struct platform_device_id ahci_devtype[] = { | |||
47 | }; | 49 | }; |
48 | MODULE_DEVICE_TABLE(platform, ahci_devtype); | 50 | MODULE_DEVICE_TABLE(platform, ahci_devtype); |
49 | 51 | ||
52 | struct ata_port_operations ahci_platform_ops = { | ||
53 | .inherits = &ahci_ops, | ||
54 | .host_stop = ahci_host_stop, | ||
55 | }; | ||
56 | |||
57 | struct ata_port_operations ahci_platform_retry_srst_ops = { | ||
58 | .inherits = &ahci_pmp_retry_srst_ops, | ||
59 | .host_stop = ahci_host_stop, | ||
60 | }; | ||
50 | 61 | ||
51 | static const struct ata_port_info ahci_port_info[] = { | 62 | static const struct ata_port_info ahci_port_info[] = { |
52 | /* by features */ | 63 | /* by features */ |
@@ -54,20 +65,20 @@ static const struct ata_port_info ahci_port_info[] = { | |||
54 | .flags = AHCI_FLAG_COMMON, | 65 | .flags = AHCI_FLAG_COMMON, |
55 | .pio_mask = ATA_PIO4, | 66 | .pio_mask = ATA_PIO4, |
56 | .udma_mask = ATA_UDMA6, | 67 | .udma_mask = ATA_UDMA6, |
57 | .port_ops = &ahci_ops, | 68 | .port_ops = &ahci_platform_ops, |
58 | }, | 69 | }, |
59 | [IMX53_AHCI] = { | 70 | [IMX53_AHCI] = { |
60 | .flags = AHCI_FLAG_COMMON, | 71 | .flags = AHCI_FLAG_COMMON, |
61 | .pio_mask = ATA_PIO4, | 72 | .pio_mask = ATA_PIO4, |
62 | .udma_mask = ATA_UDMA6, | 73 | .udma_mask = ATA_UDMA6, |
63 | .port_ops = &ahci_pmp_retry_srst_ops, | 74 | .port_ops = &ahci_platform_retry_srst_ops, |
64 | }, | 75 | }, |
65 | [STRICT_AHCI] = { | 76 | [STRICT_AHCI] = { |
66 | AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), | 77 | AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), |
67 | .flags = AHCI_FLAG_COMMON, | 78 | .flags = AHCI_FLAG_COMMON, |
68 | .pio_mask = ATA_PIO4, | 79 | .pio_mask = ATA_PIO4, |
69 | .udma_mask = ATA_UDMA6, | 80 | .udma_mask = ATA_UDMA6, |
70 | .port_ops = &ahci_ops, | 81 | .port_ops = &ahci_platform_ops, |
71 | }, | 82 | }, |
72 | }; | 83 | }; |
73 | 84 | ||
@@ -221,12 +232,19 @@ free_clk: | |||
221 | static int __devexit ahci_remove(struct platform_device *pdev) | 232 | static int __devexit ahci_remove(struct platform_device *pdev) |
222 | { | 233 | { |
223 | struct device *dev = &pdev->dev; | 234 | struct device *dev = &pdev->dev; |
224 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
225 | struct ata_host *host = dev_get_drvdata(dev); | 235 | struct ata_host *host = dev_get_drvdata(dev); |
226 | struct ahci_host_priv *hpriv = host->private_data; | ||
227 | 236 | ||
228 | ata_host_detach(host); | 237 | ata_host_detach(host); |
229 | 238 | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static void ahci_host_stop(struct ata_host *host) | ||
243 | { | ||
244 | struct device *dev = host->dev; | ||
245 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
246 | struct ahci_host_priv *hpriv = host->private_data; | ||
247 | |||
230 | if (pdata && pdata->exit) | 248 | if (pdata && pdata->exit) |
231 | pdata->exit(dev); | 249 | pdata->exit(dev); |
232 | 250 | ||
@@ -234,8 +252,6 @@ static int __devexit ahci_remove(struct platform_device *pdev) | |||
234 | clk_disable_unprepare(hpriv->clk); | 252 | clk_disable_unprepare(hpriv->clk); |
235 | clk_put(hpriv->clk); | 253 | clk_put(hpriv->clk); |
236 | } | 254 | } |
237 | |||
238 | return 0; | ||
239 | } | 255 | } |
240 | 256 | ||
241 | #ifdef CONFIG_PM_SLEEP | 257 | #ifdef CONFIG_PM_SLEEP |