diff options
Diffstat (limited to 'drivers/ata/ahci_platform.c')
-rw-r--r-- | drivers/ata/ahci_platform.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 43b875810d1b..48be4e189163 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c | |||
@@ -202,6 +202,71 @@ static int __devexit ahci_remove(struct platform_device *pdev) | |||
202 | return 0; | 202 | return 0; |
203 | } | 203 | } |
204 | 204 | ||
205 | #ifdef CONFIG_PM | ||
206 | static int ahci_suspend(struct device *dev) | ||
207 | { | ||
208 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
209 | struct ata_host *host = dev_get_drvdata(dev); | ||
210 | struct ahci_host_priv *hpriv = host->private_data; | ||
211 | void __iomem *mmio = hpriv->mmio; | ||
212 | u32 ctl; | ||
213 | int rc; | ||
214 | |||
215 | if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { | ||
216 | dev_err(dev, "firmware update required for suspend/resume\n"); | ||
217 | return -EIO; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * AHCI spec rev1.1 section 8.3.3: | ||
222 | * Software must disable interrupts prior to requesting a | ||
223 | * transition of the HBA to D3 state. | ||
224 | */ | ||
225 | ctl = readl(mmio + HOST_CTL); | ||
226 | ctl &= ~HOST_IRQ_EN; | ||
227 | writel(ctl, mmio + HOST_CTL); | ||
228 | readl(mmio + HOST_CTL); /* flush */ | ||
229 | |||
230 | rc = ata_host_suspend(host, PMSG_SUSPEND); | ||
231 | if (rc) | ||
232 | return rc; | ||
233 | |||
234 | if (pdata && pdata->suspend) | ||
235 | return pdata->suspend(dev); | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int ahci_resume(struct device *dev) | ||
240 | { | ||
241 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
242 | struct ata_host *host = dev_get_drvdata(dev); | ||
243 | int rc; | ||
244 | |||
245 | if (pdata && pdata->resume) { | ||
246 | rc = pdata->resume(dev); | ||
247 | if (rc) | ||
248 | return rc; | ||
249 | } | ||
250 | |||
251 | if (dev->power.power_state.event == PM_EVENT_SUSPEND) { | ||
252 | rc = ahci_reset_controller(host); | ||
253 | if (rc) | ||
254 | return rc; | ||
255 | |||
256 | ahci_init_controller(host); | ||
257 | } | ||
258 | |||
259 | ata_host_resume(host); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static struct dev_pm_ops ahci_pm_ops = { | ||
265 | .suspend = &ahci_suspend, | ||
266 | .resume = &ahci_resume, | ||
267 | }; | ||
268 | #endif | ||
269 | |||
205 | static const struct of_device_id ahci_of_match[] = { | 270 | static const struct of_device_id ahci_of_match[] = { |
206 | { .compatible = "calxeda,hb-ahci", }, | 271 | { .compatible = "calxeda,hb-ahci", }, |
207 | {}, | 272 | {}, |
@@ -214,6 +279,9 @@ static struct platform_driver ahci_driver = { | |||
214 | .name = "ahci", | 279 | .name = "ahci", |
215 | .owner = THIS_MODULE, | 280 | .owner = THIS_MODULE, |
216 | .of_match_table = ahci_of_match, | 281 | .of_match_table = ahci_of_match, |
282 | #ifdef CONFIG_PM | ||
283 | .pm = &ahci_pm_ops, | ||
284 | #endif | ||
217 | }, | 285 | }, |
218 | .id_table = ahci_devtype, | 286 | .id_table = ahci_devtype, |
219 | }; | 287 | }; |