aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-07-26 02:59:26 -0400
committerJeff Garzik <jeff@garzik.org>2006-07-29 04:01:31 -0400
commitc1332875cbe0c148c7f200d4f9b36b64e34d9872 (patch)
treec4456bfe0a3892989081c1ed1faaff145b9a8837 /drivers/scsi
parentd91542c11f3981768367815cf087ad36e792ea4a (diff)
[PATCH] ahci: implement Power Management support
Implement power management support. Original implementation is from Zhao, Forrest <forrest.zhao@intel.com> Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/ahci.c84
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);
215static void ahci_thaw(struct ata_port *ap); 215static void ahci_thaw(struct ata_port *ap);
216static void ahci_error_handler(struct ata_port *ap); 216static void ahci_error_handler(struct ata_port *ap);
217static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); 217static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
218static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
219static int ahci_port_resume(struct ata_port *ap);
220static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
221static int ahci_pci_device_resume(struct pci_dev *pdev);
218static void ahci_remove_one (struct pci_dev *pdev); 222static void ahci_remove_one (struct pci_dev *pdev);
219 223
220static struct scsi_host_template ahci_sht = { 224static 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
239static const struct ata_port_operations ahci_ops = { 245static 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
1213static 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
1232static 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
1244static 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
1264static 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
1202static int ahci_port_start(struct ata_port *ap) 1286static 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;