diff options
Diffstat (limited to 'drivers/scsi/ahci.c')
-rw-r--r-- | drivers/scsi/ahci.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index a9e0c5f79096..909a4dc79d08 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c | |||
@@ -215,6 +215,10 @@ static void ahci_freeze(struct ata_port *ap); | |||
215 | static void ahci_thaw(struct ata_port *ap); | 215 | static void ahci_thaw(struct ata_port *ap); |
216 | static void ahci_error_handler(struct ata_port *ap); | 216 | static void ahci_error_handler(struct ata_port *ap); |
217 | static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); | 217 | static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); |
218 | static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); | ||
219 | static int ahci_port_resume(struct ata_port *ap); | ||
220 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); | ||
221 | static int ahci_pci_device_resume(struct pci_dev *pdev); | ||
218 | static void ahci_remove_one (struct pci_dev *pdev); | 222 | static void ahci_remove_one (struct pci_dev *pdev); |
219 | 223 | ||
220 | static struct scsi_host_template ahci_sht = { | 224 | static struct scsi_host_template ahci_sht = { |
@@ -234,6 +238,8 @@ static struct scsi_host_template ahci_sht = { | |||
234 | .slave_configure = ata_scsi_slave_config, | 238 | .slave_configure = ata_scsi_slave_config, |
235 | .slave_destroy = ata_scsi_slave_destroy, | 239 | .slave_destroy = ata_scsi_slave_destroy, |
236 | .bios_param = ata_std_bios_param, | 240 | .bios_param = ata_std_bios_param, |
241 | .suspend = ata_scsi_device_suspend, | ||
242 | .resume = ata_scsi_device_resume, | ||
237 | }; | 243 | }; |
238 | 244 | ||
239 | static const struct ata_port_operations ahci_ops = { | 245 | static const struct ata_port_operations ahci_ops = { |
@@ -260,6 +266,9 @@ static const struct ata_port_operations ahci_ops = { | |||
260 | .error_handler = ahci_error_handler, | 266 | .error_handler = ahci_error_handler, |
261 | .post_internal_cmd = ahci_post_internal_cmd, | 267 | .post_internal_cmd = ahci_post_internal_cmd, |
262 | 268 | ||
269 | .port_suspend = ahci_port_suspend, | ||
270 | .port_resume = ahci_port_resume, | ||
271 | |||
263 | .port_start = ahci_port_start, | 272 | .port_start = ahci_port_start, |
264 | .port_stop = ahci_port_stop, | 273 | .port_stop = ahci_port_stop, |
265 | }; | 274 | }; |
@@ -361,6 +370,8 @@ static struct pci_driver ahci_pci_driver = { | |||
361 | .name = DRV_NAME, | 370 | .name = DRV_NAME, |
362 | .id_table = ahci_pci_tbl, | 371 | .id_table = ahci_pci_tbl, |
363 | .probe = ahci_init_one, | 372 | .probe = ahci_init_one, |
373 | .suspend = ahci_pci_device_suspend, | ||
374 | .resume = ahci_pci_device_resume, | ||
364 | .remove = ahci_remove_one, | 375 | .remove = ahci_remove_one, |
365 | }; | 376 | }; |
366 | 377 | ||
@@ -1199,6 +1210,79 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) | |||
1199 | } | 1210 | } |
1200 | } | 1211 | } |
1201 | 1212 | ||
1213 | static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) | ||
1214 | { | ||
1215 | struct ahci_host_priv *hpriv = ap->host_set->private_data; | ||
1216 | struct ahci_port_priv *pp = ap->private_data; | ||
1217 | void __iomem *mmio = ap->host_set->mmio_base; | ||
1218 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); | ||
1219 | const char *emsg = NULL; | ||
1220 | int rc; | ||
1221 | |||
1222 | rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); | ||
1223 | if (rc) { | ||
1224 | ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); | ||
1225 | ahci_init_port(port_mmio, hpriv->cap, | ||
1226 | pp->cmd_slot_dma, pp->rx_fis_dma); | ||
1227 | } | ||
1228 | |||
1229 | return rc; | ||
1230 | } | ||
1231 | |||
1232 | static int ahci_port_resume(struct ata_port *ap) | ||
1233 | { | ||
1234 | struct ahci_port_priv *pp = ap->private_data; | ||
1235 | struct ahci_host_priv *hpriv = ap->host_set->private_data; | ||
1236 | void __iomem *mmio = ap->host_set->mmio_base; | ||
1237 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); | ||
1238 | |||
1239 | ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); | ||
1240 | |||
1241 | return 0; | ||
1242 | } | ||
1243 | |||
1244 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) | ||
1245 | { | ||
1246 | struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); | ||
1247 | void __iomem *mmio = host_set->mmio_base; | ||
1248 | u32 ctl; | ||
1249 | |||
1250 | if (mesg.event == PM_EVENT_SUSPEND) { | ||
1251 | /* AHCI spec rev1.1 section 8.3.3: | ||
1252 | * Software must disable interrupts prior to requesting a | ||
1253 | * transition of the HBA to D3 state. | ||
1254 | */ | ||
1255 | ctl = readl(mmio + HOST_CTL); | ||
1256 | ctl &= ~HOST_IRQ_EN; | ||
1257 | writel(ctl, mmio + HOST_CTL); | ||
1258 | readl(mmio + HOST_CTL); /* flush */ | ||
1259 | } | ||
1260 | |||
1261 | return ata_pci_device_suspend(pdev, mesg); | ||
1262 | } | ||
1263 | |||
1264 | static int ahci_pci_device_resume(struct pci_dev *pdev) | ||
1265 | { | ||
1266 | struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); | ||
1267 | struct ahci_host_priv *hpriv = host_set->private_data; | ||
1268 | void __iomem *mmio = host_set->mmio_base; | ||
1269 | int rc; | ||
1270 | |||
1271 | ata_pci_device_do_resume(pdev); | ||
1272 | |||
1273 | if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { | ||
1274 | rc = ahci_reset_controller(mmio, pdev); | ||
1275 | if (rc) | ||
1276 | return rc; | ||
1277 | |||
1278 | ahci_init_controller(mmio, pdev, host_set->n_ports, hpriv->cap); | ||
1279 | } | ||
1280 | |||
1281 | ata_host_set_resume(host_set); | ||
1282 | |||
1283 | return 0; | ||
1284 | } | ||
1285 | |||
1202 | static int ahci_port_start(struct ata_port *ap) | 1286 | static int ahci_port_start(struct ata_port *ap) |
1203 | { | 1287 | { |
1204 | struct device *dev = ap->host_set->dev; | 1288 | struct device *dev = ap->host_set->dev; |