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