xref: /kernel/linux/linux-5.10/drivers/ide/ide-pm.c (revision 8c2ecf20)
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