xref: /linux/drivers/ata/libata-zpodd.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2afe75951SAaron Lu #include <linux/libata.h>
3afe75951SAaron Lu #include <linux/cdrom.h>
4f064a20dSAaron Lu #include <linux/pm_runtime.h>
53dc67440SAaron Lu #include <linux/module.h>
6f1bc1e4cSAaron Lu #include <linux/pm_qos.h>
7f064a20dSAaron Lu #include <scsi/scsi_device.h>
8afe75951SAaron Lu 
9afe75951SAaron Lu #include "libata.h"
10afe75951SAaron Lu 
113dc67440SAaron Lu static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */
123dc67440SAaron Lu module_param(zpodd_poweroff_delay, int, 0644);
133dc67440SAaron Lu MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds");
143dc67440SAaron Lu 
15afe75951SAaron Lu enum odd_mech_type {
16afe75951SAaron Lu 	ODD_MECH_TYPE_SLOT,
17afe75951SAaron Lu 	ODD_MECH_TYPE_DRAWER,
18afe75951SAaron Lu 	ODD_MECH_TYPE_UNSUPPORTED,
19afe75951SAaron Lu };
20afe75951SAaron Lu 
21afe75951SAaron Lu struct zpodd {
22afe75951SAaron Lu 	enum odd_mech_type	mech_type; /* init during probe, RO afterwards */
23afe75951SAaron Lu 	struct ata_device	*dev;
24f064a20dSAaron Lu 
25f064a20dSAaron Lu 	/* The following fields are synchronized by PM core. */
26f064a20dSAaron Lu 	bool			from_notify; /* resumed as a result of
27f064a20dSAaron Lu 					      * acpi wake notification */
283dc67440SAaron Lu 	bool			zp_ready; /* ZP ready state */
293dc67440SAaron Lu 	unsigned long		last_ready; /* last ZP ready timestamp */
303dc67440SAaron Lu 	bool			zp_sampled; /* ZP ready state sampled */
3121334205SAaron Lu 	bool			powered_off; /* ODD is powered off
3221334205SAaron Lu 					      *	during suspend */
33afe75951SAaron Lu };
34afe75951SAaron Lu 
eject_tray(struct ata_device * dev)3521334205SAaron Lu static int eject_tray(struct ata_device *dev)
3621334205SAaron Lu {
37d0887c43SSergei Shtylyov 	struct ata_taskfile tf;
3818c9a99bSDan Carpenter 	static const char cdb[ATAPI_CDB_LEN] = {  GPCMD_START_STOP_UNIT,
3921334205SAaron Lu 		0, 0, 0,
4021334205SAaron Lu 		0x02,     /* LoEj */
4121334205SAaron Lu 		0, 0, 0, 0, 0, 0, 0,
4221334205SAaron Lu 	};
4321334205SAaron Lu 
44d0887c43SSergei Shtylyov 	ata_tf_init(dev, &tf);
4521334205SAaron Lu 	tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
4621334205SAaron Lu 	tf.command = ATA_CMD_PACKET;
4721334205SAaron Lu 	tf.protocol = ATAPI_PROT_NODATA;
4821334205SAaron Lu 
4921334205SAaron Lu 	return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
5021334205SAaron Lu }
5121334205SAaron Lu 
52afe75951SAaron Lu /* Per the spec, only slot type and drawer type ODD can be supported */
zpodd_get_mech_type(struct ata_device * dev)53afe75951SAaron Lu static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
54afe75951SAaron Lu {
55dd08a8d9Sraymond pang 	char *buf;
56afe75951SAaron Lu 	unsigned int ret;
57dd08a8d9Sraymond pang 	struct rm_feature_desc *desc;
58d0887c43SSergei Shtylyov 	struct ata_taskfile tf;
5971d6c505SKees Cook 	static const char cdb[ATAPI_CDB_LEN] = {  GPCMD_GET_CONFIGURATION,
60afe75951SAaron Lu 			2,      /* only 1 feature descriptor requested */
61afe75951SAaron Lu 			0, 3,   /* 3, removable medium feature */
62afe75951SAaron Lu 			0, 0, 0,/* reserved */
63dd08a8d9Sraymond pang 			0, 16,
64afe75951SAaron Lu 			0, 0, 0,
65afe75951SAaron Lu 	};
66afe75951SAaron Lu 
67dd08a8d9Sraymond pang 	buf = kzalloc(16, GFP_KERNEL);
68dd08a8d9Sraymond pang 	if (!buf)
69dd08a8d9Sraymond pang 		return ODD_MECH_TYPE_UNSUPPORTED;
70dd08a8d9Sraymond pang 	desc = (void *)(buf + 8);
71dd08a8d9Sraymond pang 
72d0887c43SSergei Shtylyov 	ata_tf_init(dev, &tf);
73afe75951SAaron Lu 	tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
74afe75951SAaron Lu 	tf.command = ATA_CMD_PACKET;
75afe75951SAaron Lu 	tf.protocol = ATAPI_PROT_PIO;
76dd08a8d9Sraymond pang 	tf.lbam = 16;
77afe75951SAaron Lu 
78afe75951SAaron Lu 	ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
79dd08a8d9Sraymond pang 				buf, 16, 0);
80dd08a8d9Sraymond pang 	if (ret) {
81dd08a8d9Sraymond pang 		kfree(buf);
82afe75951SAaron Lu 		return ODD_MECH_TYPE_UNSUPPORTED;
83dd08a8d9Sraymond pang 	}
84afe75951SAaron Lu 
85dd08a8d9Sraymond pang 	if (be16_to_cpu(desc->feature_code) != 3) {
86dd08a8d9Sraymond pang 		kfree(buf);
87afe75951SAaron Lu 		return ODD_MECH_TYPE_UNSUPPORTED;
88dd08a8d9Sraymond pang 	}
89afe75951SAaron Lu 
90dd08a8d9Sraymond pang 	if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) {
91dd08a8d9Sraymond pang 		kfree(buf);
92afe75951SAaron Lu 		return ODD_MECH_TYPE_SLOT;
93dd08a8d9Sraymond pang 	} else if (desc->mech_type == 1 && desc->load == 0 &&
94dd08a8d9Sraymond pang 		   desc->eject == 1) {
95dd08a8d9Sraymond pang 		kfree(buf);
96afe75951SAaron Lu 		return ODD_MECH_TYPE_DRAWER;
97dd08a8d9Sraymond pang 	} else {
98dd08a8d9Sraymond pang 		kfree(buf);
99afe75951SAaron Lu 		return ODD_MECH_TYPE_UNSUPPORTED;
100afe75951SAaron Lu 	}
101dd08a8d9Sraymond pang }
102afe75951SAaron Lu 
1033dc67440SAaron Lu /* Test if ODD is zero power ready by sense code */
zpready(struct ata_device * dev)1043dc67440SAaron Lu static bool zpready(struct ata_device *dev)
1053dc67440SAaron Lu {
1063dc67440SAaron Lu 	u8 sense_key, *sense_buf;
1073dc67440SAaron Lu 	unsigned int ret, asc, ascq, add_len;
1083dc67440SAaron Lu 	struct zpodd *zpodd = dev->zpodd;
1093dc67440SAaron Lu 
1103dc67440SAaron Lu 	ret = atapi_eh_tur(dev, &sense_key);
1113dc67440SAaron Lu 
1123dc67440SAaron Lu 	if (!ret || sense_key != NOT_READY)
1133dc67440SAaron Lu 		return false;
1143dc67440SAaron Lu 
115*da65bbddSDamien Le Moal 	sense_buf = dev->sector_buf;
1163dc67440SAaron Lu 	ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
1173dc67440SAaron Lu 	if (ret)
1183dc67440SAaron Lu 		return false;
1193dc67440SAaron Lu 
1203dc67440SAaron Lu 	/* sense valid */
1213dc67440SAaron Lu 	if ((sense_buf[0] & 0x7f) != 0x70)
1223dc67440SAaron Lu 		return false;
1233dc67440SAaron Lu 
1243dc67440SAaron Lu 	add_len = sense_buf[7];
1253dc67440SAaron Lu 	/* has asc and ascq */
1263dc67440SAaron Lu 	if (add_len < 6)
1273dc67440SAaron Lu 		return false;
1283dc67440SAaron Lu 
1293dc67440SAaron Lu 	asc = sense_buf[12];
1303dc67440SAaron Lu 	ascq = sense_buf[13];
1313dc67440SAaron Lu 
1323dc67440SAaron Lu 	if (zpodd->mech_type == ODD_MECH_TYPE_SLOT)
1333dc67440SAaron Lu 		/* no media inside */
1343dc67440SAaron Lu 		return asc == 0x3a;
1353dc67440SAaron Lu 	else
1363dc67440SAaron Lu 		/* no media inside and door closed */
1373dc67440SAaron Lu 		return asc == 0x3a && ascq == 0x01;
1383dc67440SAaron Lu }
1393dc67440SAaron Lu 
1403dc67440SAaron Lu /*
1413dc67440SAaron Lu  * Update the zpodd->zp_ready field. This field will only be set
1423dc67440SAaron Lu  * if the ODD has stayed in ZP ready state for zpodd_poweroff_delay
1433dc67440SAaron Lu  * time, and will be used to decide if power off is allowed. If it
1443dc67440SAaron Lu  * is set, it will be cleared during resume from powered off state.
1453dc67440SAaron Lu  */
zpodd_on_suspend(struct ata_device * dev)1463dc67440SAaron Lu void zpodd_on_suspend(struct ata_device *dev)
1473dc67440SAaron Lu {
1483dc67440SAaron Lu 	struct zpodd *zpodd = dev->zpodd;
1493dc67440SAaron Lu 	unsigned long expires;
1503dc67440SAaron Lu 
1513dc67440SAaron Lu 	if (!zpready(dev)) {
1523dc67440SAaron Lu 		zpodd->zp_sampled = false;
153a59b9aaeSAaron Lu 		zpodd->zp_ready = false;
1543dc67440SAaron Lu 		return;
1553dc67440SAaron Lu 	}
1563dc67440SAaron Lu 
1573dc67440SAaron Lu 	if (!zpodd->zp_sampled) {
1583dc67440SAaron Lu 		zpodd->zp_sampled = true;
1593dc67440SAaron Lu 		zpodd->last_ready = jiffies;
1603dc67440SAaron Lu 		return;
1613dc67440SAaron Lu 	}
1623dc67440SAaron Lu 
1633dc67440SAaron Lu 	expires = zpodd->last_ready +
1643dc67440SAaron Lu 		  msecs_to_jiffies(zpodd_poweroff_delay * 1000);
1653dc67440SAaron Lu 	if (time_before(jiffies, expires))
1663dc67440SAaron Lu 		return;
1673dc67440SAaron Lu 
1683dc67440SAaron Lu 	zpodd->zp_ready = true;
1693dc67440SAaron Lu }
1703dc67440SAaron Lu 
zpodd_zpready(struct ata_device * dev)17121334205SAaron Lu bool zpodd_zpready(struct ata_device *dev)
17221334205SAaron Lu {
17321334205SAaron Lu 	struct zpodd *zpodd = dev->zpodd;
17421334205SAaron Lu 	return zpodd->zp_ready;
17521334205SAaron Lu }
17621334205SAaron Lu 
17721334205SAaron Lu /*
17821334205SAaron Lu  * Enable runtime wake capability through ACPI and set the powered_off flag,
17921334205SAaron Lu  * this flag will be used during resume to decide what operations are needed
18021334205SAaron Lu  * to take.
1816f4c827eSAaron Lu  *
1826f4c827eSAaron Lu  * Also, media poll needs to be silenced, so that it doesn't bring the ODD
1836f4c827eSAaron Lu  * back to full power state every few seconds.
18421334205SAaron Lu  */
zpodd_enable_run_wake(struct ata_device * dev)18521334205SAaron Lu void zpodd_enable_run_wake(struct ata_device *dev)
18621334205SAaron Lu {
18721334205SAaron Lu 	struct zpodd *zpodd = dev->zpodd;
18821334205SAaron Lu 
1896f4c827eSAaron Lu 	sdev_disable_disk_events(dev->sdev);
1906f4c827eSAaron Lu 
19121334205SAaron Lu 	zpodd->powered_off = true;
1924d183d04SRafael J. Wysocki 	acpi_pm_set_device_wakeup(&dev->tdev, true);
19321334205SAaron Lu }
19421334205SAaron Lu 
19521334205SAaron Lu /* Disable runtime wake capability if it is enabled */
zpodd_disable_run_wake(struct ata_device * dev)19621334205SAaron Lu void zpodd_disable_run_wake(struct ata_device *dev)
19721334205SAaron Lu {
19821334205SAaron Lu 	struct zpodd *zpodd = dev->zpodd;
19921334205SAaron Lu 
2004d183d04SRafael J. Wysocki 	if (zpodd->powered_off)
2014d183d04SRafael J. Wysocki 		acpi_pm_set_device_wakeup(&dev->tdev, false);
20221334205SAaron Lu }
20321334205SAaron Lu 
20421334205SAaron Lu /*
20521334205SAaron Lu  * Post power on processing after the ODD has been recovered. If the
20621334205SAaron Lu  * ODD wasn't powered off during suspend, it doesn't do anything.
20721334205SAaron Lu  *
20821334205SAaron Lu  * For drawer type ODD, if it is powered on due to user pressed the
20921334205SAaron Lu  * eject button, the tray needs to be ejected. This can only be done
21021334205SAaron Lu  * after the ODD has been recovered, i.e. link is initialized and
21121334205SAaron Lu  * device is able to process NON_DATA PIO command, as eject needs to
21221334205SAaron Lu  * send command for the ODD to process.
21321334205SAaron Lu  *
21421334205SAaron Lu  * The from_notify flag set in wake notification handler function
21521334205SAaron Lu  * zpodd_wake_dev represents if power on is due to user's action.
21621334205SAaron Lu  *
21721334205SAaron Lu  * For both types of ODD, several fields need to be reset.
21821334205SAaron Lu  */
zpodd_post_poweron(struct ata_device * dev)21921334205SAaron Lu void zpodd_post_poweron(struct ata_device *dev)
22021334205SAaron Lu {
22121334205SAaron Lu 	struct zpodd *zpodd = dev->zpodd;
22221334205SAaron Lu 
22321334205SAaron Lu 	if (!zpodd->powered_off)
22421334205SAaron Lu 		return;
22521334205SAaron Lu 
22621334205SAaron Lu 	zpodd->powered_off = false;
22721334205SAaron Lu 
22821334205SAaron Lu 	if (zpodd->from_notify) {
22921334205SAaron Lu 		zpodd->from_notify = false;
23021334205SAaron Lu 		if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
23121334205SAaron Lu 			eject_tray(dev);
23221334205SAaron Lu 	}
23321334205SAaron Lu 
23421334205SAaron Lu 	zpodd->zp_sampled = false;
23521334205SAaron Lu 	zpodd->zp_ready = false;
2366f4c827eSAaron Lu 
2376f4c827eSAaron Lu 	sdev_enable_disk_events(dev->sdev);
23821334205SAaron Lu }
23921334205SAaron Lu 
zpodd_wake_dev(acpi_handle handle,u32 event,void * context)240f064a20dSAaron Lu static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
241f064a20dSAaron Lu {
242f064a20dSAaron Lu 	struct ata_device *ata_dev = context;
243f064a20dSAaron Lu 	struct zpodd *zpodd = ata_dev->zpodd;
244f064a20dSAaron Lu 	struct device *dev = &ata_dev->sdev->sdev_gendev;
245f064a20dSAaron Lu 
24653637e07SAaron Lu 	if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
247f064a20dSAaron Lu 		zpodd->from_notify = true;
248f064a20dSAaron Lu 		pm_runtime_resume(dev);
249f064a20dSAaron Lu 	}
250f064a20dSAaron Lu }
251f064a20dSAaron Lu 
ata_acpi_add_pm_notifier(struct ata_device * dev)252f064a20dSAaron Lu static void ata_acpi_add_pm_notifier(struct ata_device *dev)
253f064a20dSAaron Lu {
254f064a20dSAaron Lu 	acpi_handle handle = ata_dev_acpi_handle(dev);
255f064a20dSAaron Lu 	acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
256f064a20dSAaron Lu 				    zpodd_wake_dev, dev);
257f064a20dSAaron Lu }
258f064a20dSAaron Lu 
ata_acpi_remove_pm_notifier(struct ata_device * dev)259f064a20dSAaron Lu static void ata_acpi_remove_pm_notifier(struct ata_device *dev)
260f064a20dSAaron Lu {
261f1bc1e4cSAaron Lu 	acpi_handle handle = ata_dev_acpi_handle(dev);
262f064a20dSAaron Lu 	acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev);
263f064a20dSAaron Lu }
264f064a20dSAaron Lu 
zpodd_init(struct ata_device * dev)265afe75951SAaron Lu void zpodd_init(struct ata_device *dev)
266afe75951SAaron Lu {
267d9202036SAaron Lu 	struct acpi_device *adev = ACPI_COMPANION(&dev->tdev);
268afe75951SAaron Lu 	enum odd_mech_type mech_type;
269afe75951SAaron Lu 	struct zpodd *zpodd;
270afe75951SAaron Lu 
271d9202036SAaron Lu 	if (dev->zpodd || !adev || !acpi_device_can_poweroff(adev))
272afe75951SAaron Lu 		return;
273afe75951SAaron Lu 
274afe75951SAaron Lu 	mech_type = zpodd_get_mech_type(dev);
275afe75951SAaron Lu 	if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
276afe75951SAaron Lu 		return;
277afe75951SAaron Lu 
278afe75951SAaron Lu 	zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
279afe75951SAaron Lu 	if (!zpodd)
280afe75951SAaron Lu 		return;
281afe75951SAaron Lu 
282afe75951SAaron Lu 	zpodd->mech_type = mech_type;
283afe75951SAaron Lu 
284f064a20dSAaron Lu 	ata_acpi_add_pm_notifier(dev);
285afe75951SAaron Lu 	zpodd->dev = dev;
286afe75951SAaron Lu 	dev->zpodd = zpodd;
287f1bc1e4cSAaron Lu 	dev_pm_qos_expose_flags(&dev->tdev, 0);
288afe75951SAaron Lu }
289afe75951SAaron Lu 
zpodd_exit(struct ata_device * dev)290afe75951SAaron Lu void zpodd_exit(struct ata_device *dev)
291afe75951SAaron Lu {
292f064a20dSAaron Lu 	ata_acpi_remove_pm_notifier(dev);
293afe75951SAaron Lu 	kfree(dev->zpodd);
294afe75951SAaron Lu 	dev->zpodd = NULL;
295afe75951SAaron Lu }
296