18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/kernel.h> 38c2ecf20Sopenharmony_ci#include <linux/gfp.h> 48c2ecf20Sopenharmony_ci#include <linux/ide.h> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ciint generic_ide_suspend(struct device *dev, pm_message_t mesg) 78c2ecf20Sopenharmony_ci{ 88c2ecf20Sopenharmony_ci ide_drive_t *drive = to_ide_device(dev); 98c2ecf20Sopenharmony_ci ide_drive_t *pair = ide_get_pair_dev(drive); 108c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 118c2ecf20Sopenharmony_ci struct request *rq; 128c2ecf20Sopenharmony_ci struct ide_pm_state rqpm; 138c2ecf20Sopenharmony_ci int ret; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci if (ide_port_acpi(hwif)) { 168c2ecf20Sopenharmony_ci /* call ACPI _GTM only once */ 178c2ecf20Sopenharmony_ci if ((drive->dn & 1) == 0 || pair == NULL) 188c2ecf20Sopenharmony_ci ide_acpi_get_timing(hwif); 198c2ecf20Sopenharmony_ci } 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci memset(&rqpm, 0, sizeof(rqpm)); 228c2ecf20Sopenharmony_ci rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); 238c2ecf20Sopenharmony_ci ide_req(rq)->type = ATA_PRIV_PM_SUSPEND; 248c2ecf20Sopenharmony_ci ide_req(rq)->special = &rqpm; 258c2ecf20Sopenharmony_ci rqpm.pm_step = IDE_PM_START_SUSPEND; 268c2ecf20Sopenharmony_ci if (mesg.event == PM_EVENT_PRETHAW) 278c2ecf20Sopenharmony_ci mesg.event = PM_EVENT_FREEZE; 288c2ecf20Sopenharmony_ci rqpm.pm_state = mesg.event; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci blk_execute_rq(drive->queue, NULL, rq, 0); 318c2ecf20Sopenharmony_ci ret = scsi_req(rq)->result ? -EIO : 0; 328c2ecf20Sopenharmony_ci blk_put_request(rq); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (ret == 0 && ide_port_acpi(hwif)) { 358c2ecf20Sopenharmony_ci /* call ACPI _PS3 only after both devices are suspended */ 368c2ecf20Sopenharmony_ci if ((drive->dn & 1) || pair == NULL) 378c2ecf20Sopenharmony_ci ide_acpi_set_state(hwif, 0); 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return ret; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int ide_pm_execute_rq(struct request *rq) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct request_queue *q = rq->q; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (unlikely(blk_queue_dying(q))) { 488c2ecf20Sopenharmony_ci rq->rq_flags |= RQF_QUIET; 498c2ecf20Sopenharmony_ci scsi_req(rq)->result = -ENXIO; 508c2ecf20Sopenharmony_ci blk_mq_end_request(rq, BLK_STS_OK); 518c2ecf20Sopenharmony_ci return -ENXIO; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci blk_execute_rq(q, NULL, rq, true); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return scsi_req(rq)->result ? -EIO : 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ciint generic_ide_resume(struct device *dev) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci ide_drive_t *drive = to_ide_device(dev); 618c2ecf20Sopenharmony_ci ide_drive_t *pair = ide_get_pair_dev(drive); 628c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 638c2ecf20Sopenharmony_ci struct request *rq; 648c2ecf20Sopenharmony_ci struct ide_pm_state rqpm; 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci blk_mq_start_stopped_hw_queues(drive->queue, true); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (ide_port_acpi(hwif)) { 708c2ecf20Sopenharmony_ci /* call ACPI _PS0 / _STM only once */ 718c2ecf20Sopenharmony_ci if ((drive->dn & 1) == 0 || pair == NULL) { 728c2ecf20Sopenharmony_ci ide_acpi_set_state(hwif, 1); 738c2ecf20Sopenharmony_ci ide_acpi_push_timing(hwif); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ide_acpi_exec_tfs(drive); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci memset(&rqpm, 0, sizeof(rqpm)); 808c2ecf20Sopenharmony_ci rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, BLK_MQ_REQ_PM); 818c2ecf20Sopenharmony_ci ide_req(rq)->type = ATA_PRIV_PM_RESUME; 828c2ecf20Sopenharmony_ci ide_req(rq)->special = &rqpm; 838c2ecf20Sopenharmony_ci rqpm.pm_step = IDE_PM_START_RESUME; 848c2ecf20Sopenharmony_ci rqpm.pm_state = PM_EVENT_ON; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci err = ide_pm_execute_rq(rq); 878c2ecf20Sopenharmony_ci blk_put_request(rq); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (err == 0 && dev->driver) { 908c2ecf20Sopenharmony_ci struct ide_driver *drv = to_ide_driver(dev->driver); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (drv->resume) 938c2ecf20Sopenharmony_ci drv->resume(drive); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid ide_complete_power_step(ide_drive_t *drive, struct request *rq) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct ide_pm_state *pm = ide_req(rq)->special; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#ifdef DEBUG_PM 1048c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: complete_power_step(step: %d)\n", 1058c2ecf20Sopenharmony_ci drive->name, pm->pm_step); 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci if (drive->media != ide_disk) 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci switch (pm->pm_step) { 1118c2ecf20Sopenharmony_ci case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */ 1128c2ecf20Sopenharmony_ci if (pm->pm_state == PM_EVENT_FREEZE) 1138c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_COMPLETED; 1148c2ecf20Sopenharmony_ci else 1158c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_STANDBY; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case IDE_PM_STANDBY: /* Suspend step 2 (standby) */ 1188c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_COMPLETED; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */ 1218c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_IDLE; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case IDE_PM_IDLE: /* Resume step 2 (idle)*/ 1248c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_RESTORE_DMA; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ciide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct ide_pm_state *pm = ide_req(rq)->special; 1328c2ecf20Sopenharmony_ci struct ide_cmd cmd = { }; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci switch (pm->pm_step) { 1358c2ecf20Sopenharmony_ci case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */ 1368c2ecf20Sopenharmony_ci if (drive->media != ide_disk) 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci /* Not supported? Switch to next step now. */ 1398c2ecf20Sopenharmony_ci if (ata_id_flush_enabled(drive->id) == 0 || 1408c2ecf20Sopenharmony_ci (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) { 1418c2ecf20Sopenharmony_ci ide_complete_power_step(drive, rq); 1428c2ecf20Sopenharmony_ci return ide_stopped; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci if (ata_id_flush_ext_enabled(drive->id)) 1458c2ecf20Sopenharmony_ci cmd.tf.command = ATA_CMD_FLUSH_EXT; 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci cmd.tf.command = ATA_CMD_FLUSH; 1488c2ecf20Sopenharmony_ci goto out_do_tf; 1498c2ecf20Sopenharmony_ci case IDE_PM_STANDBY: /* Suspend step 2 (standby) */ 1508c2ecf20Sopenharmony_ci cmd.tf.command = ATA_CMD_STANDBYNOW1; 1518c2ecf20Sopenharmony_ci goto out_do_tf; 1528c2ecf20Sopenharmony_ci case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */ 1538c2ecf20Sopenharmony_ci ide_set_max_pio(drive); 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * skip IDE_PM_IDLE for ATAPI devices 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci if (drive->media != ide_disk) 1588c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_RESTORE_DMA; 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci ide_complete_power_step(drive, rq); 1618c2ecf20Sopenharmony_ci return ide_stopped; 1628c2ecf20Sopenharmony_ci case IDE_PM_IDLE: /* Resume step 2 (idle) */ 1638c2ecf20Sopenharmony_ci cmd.tf.command = ATA_CMD_IDLEIMMEDIATE; 1648c2ecf20Sopenharmony_ci goto out_do_tf; 1658c2ecf20Sopenharmony_ci case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */ 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * Right now, all we do is call ide_set_dma(drive), 1688c2ecf20Sopenharmony_ci * we could be smarter and check for current xfer_speed 1698c2ecf20Sopenharmony_ci * in struct drive etc... 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci if (drive->hwif->dma_ops == NULL) 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * TODO: respect IDE_DFLAG_USING_DMA 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci ide_set_dma(drive); 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci pm->pm_step = IDE_PM_COMPLETED; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ide_stopped; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciout_do_tf: 1858c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; 1868c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; 1878c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_NODATA; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return do_rw_taskfile(drive, &cmd); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/** 1938c2ecf20Sopenharmony_ci * ide_complete_pm_rq - end the current Power Management request 1948c2ecf20Sopenharmony_ci * @drive: target drive 1958c2ecf20Sopenharmony_ci * @rq: request 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * This function cleans up the current PM request and stops the queue 1988c2ecf20Sopenharmony_ci * if necessary. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_civoid ide_complete_pm_rq(ide_drive_t *drive, struct request *rq) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct request_queue *q = drive->queue; 2038c2ecf20Sopenharmony_ci struct ide_pm_state *pm = ide_req(rq)->special; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ide_complete_power_step(drive, rq); 2068c2ecf20Sopenharmony_ci if (pm->pm_step != IDE_PM_COMPLETED) 2078c2ecf20Sopenharmony_ci return; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#ifdef DEBUG_PM 2108c2ecf20Sopenharmony_ci printk("%s: completing PM request, %s\n", drive->name, 2118c2ecf20Sopenharmony_ci (ide_req(rq)->type == ATA_PRIV_PM_SUSPEND) ? "suspend" : "resume"); 2128c2ecf20Sopenharmony_ci#endif 2138c2ecf20Sopenharmony_ci if (ide_req(rq)->type == ATA_PRIV_PM_SUSPEND) 2148c2ecf20Sopenharmony_ci blk_mq_stop_hw_queues(q); 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci drive->dev_flags &= ~IDE_DFLAG_BLOCKED; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci drive->hwif->rq = NULL; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci blk_mq_end_request(rq, BLK_STS_OK); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid ide_check_pm_state(ide_drive_t *drive, struct request *rq) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct ide_pm_state *pm = ide_req(rq)->special; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (blk_rq_is_private(rq) && 2288c2ecf20Sopenharmony_ci ide_req(rq)->type == ATA_PRIV_PM_SUSPEND && 2298c2ecf20Sopenharmony_ci pm->pm_step == IDE_PM_START_SUSPEND) 2308c2ecf20Sopenharmony_ci /* Mark drive blocked when starting the suspend sequence. */ 2318c2ecf20Sopenharmony_ci drive->dev_flags |= IDE_DFLAG_BLOCKED; 2328c2ecf20Sopenharmony_ci else if (blk_rq_is_private(rq) && 2338c2ecf20Sopenharmony_ci ide_req(rq)->type == ATA_PRIV_PM_RESUME && 2348c2ecf20Sopenharmony_ci pm->pm_step == IDE_PM_START_RESUME) { 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * The first thing we do on wakeup is to wait for BSY bit to 2378c2ecf20Sopenharmony_ci * go away (with a looong timeout) as a drive on this hwif may 2388c2ecf20Sopenharmony_ci * just be POSTing itself. 2398c2ecf20Sopenharmony_ci * We do that before even selecting as the "other" device on 2408c2ecf20Sopenharmony_ci * the bus may be broken enough to walk on our toes at this 2418c2ecf20Sopenharmony_ci * point. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 2448c2ecf20Sopenharmony_ci const struct ide_tp_ops *tp_ops = hwif->tp_ops; 2458c2ecf20Sopenharmony_ci struct request_queue *q = drive->queue; 2468c2ecf20Sopenharmony_ci int rc; 2478c2ecf20Sopenharmony_ci#ifdef DEBUG_PM 2488c2ecf20Sopenharmony_ci printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name); 2498c2ecf20Sopenharmony_ci#endif 2508c2ecf20Sopenharmony_ci rc = ide_wait_not_busy(hwif, 35000); 2518c2ecf20Sopenharmony_ci if (rc) 2528c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); 2538c2ecf20Sopenharmony_ci tp_ops->dev_select(drive); 2548c2ecf20Sopenharmony_ci tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); 2558c2ecf20Sopenharmony_ci rc = ide_wait_not_busy(hwif, 100000); 2568c2ecf20Sopenharmony_ci if (rc) 2578c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci blk_mq_start_hw_queues(q); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci} 262