Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/libata.h>
0003 #include <linux/cdrom.h>
0004 #include <linux/pm_runtime.h>
0005 #include <linux/module.h>
0006 #include <linux/pm_qos.h>
0007 #include <scsi/scsi_device.h>
0008 
0009 #include "libata.h"
0010 
0011 static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */
0012 module_param(zpodd_poweroff_delay, int, 0644);
0013 MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds");
0014 
0015 enum odd_mech_type {
0016     ODD_MECH_TYPE_SLOT,
0017     ODD_MECH_TYPE_DRAWER,
0018     ODD_MECH_TYPE_UNSUPPORTED,
0019 };
0020 
0021 struct zpodd {
0022     enum odd_mech_type  mech_type; /* init during probe, RO afterwards */
0023     struct ata_device   *dev;
0024 
0025     /* The following fields are synchronized by PM core. */
0026     bool            from_notify; /* resumed as a result of
0027                           * acpi wake notification */
0028     bool            zp_ready; /* ZP ready state */
0029     unsigned long       last_ready; /* last ZP ready timestamp */
0030     bool            zp_sampled; /* ZP ready state sampled */
0031     bool            powered_off; /* ODD is powered off
0032                           * during suspend */
0033 };
0034 
0035 static int eject_tray(struct ata_device *dev)
0036 {
0037     struct ata_taskfile tf;
0038     static const char cdb[ATAPI_CDB_LEN] = {  GPCMD_START_STOP_UNIT,
0039         0, 0, 0,
0040         0x02,     /* LoEj */
0041         0, 0, 0, 0, 0, 0, 0,
0042     };
0043 
0044     ata_tf_init(dev, &tf);
0045     tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
0046     tf.command = ATA_CMD_PACKET;
0047     tf.protocol = ATAPI_PROT_NODATA;
0048 
0049     return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
0050 }
0051 
0052 /* Per the spec, only slot type and drawer type ODD can be supported */
0053 static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
0054 {
0055     char *buf;
0056     unsigned int ret;
0057     struct rm_feature_desc *desc;
0058     struct ata_taskfile tf;
0059     static const char cdb[ATAPI_CDB_LEN] = {  GPCMD_GET_CONFIGURATION,
0060             2,      /* only 1 feature descriptor requested */
0061             0, 3,   /* 3, removable medium feature */
0062             0, 0, 0,/* reserved */
0063             0, 16,
0064             0, 0, 0,
0065     };
0066 
0067     buf = kzalloc(16, GFP_KERNEL);
0068     if (!buf)
0069         return ODD_MECH_TYPE_UNSUPPORTED;
0070     desc = (void *)(buf + 8);
0071 
0072     ata_tf_init(dev, &tf);
0073     tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
0074     tf.command = ATA_CMD_PACKET;
0075     tf.protocol = ATAPI_PROT_PIO;
0076     tf.lbam = 16;
0077 
0078     ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
0079                 buf, 16, 0);
0080     if (ret) {
0081         kfree(buf);
0082         return ODD_MECH_TYPE_UNSUPPORTED;
0083     }
0084 
0085     if (be16_to_cpu(desc->feature_code) != 3) {
0086         kfree(buf);
0087         return ODD_MECH_TYPE_UNSUPPORTED;
0088     }
0089 
0090     if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) {
0091         kfree(buf);
0092         return ODD_MECH_TYPE_SLOT;
0093     } else if (desc->mech_type == 1 && desc->load == 0 &&
0094            desc->eject == 1) {
0095         kfree(buf);
0096         return ODD_MECH_TYPE_DRAWER;
0097     } else {
0098         kfree(buf);
0099         return ODD_MECH_TYPE_UNSUPPORTED;
0100     }
0101 }
0102 
0103 /* Test if ODD is zero power ready by sense code */
0104 static bool zpready(struct ata_device *dev)
0105 {
0106     u8 sense_key, *sense_buf;
0107     unsigned int ret, asc, ascq, add_len;
0108     struct zpodd *zpodd = dev->zpodd;
0109 
0110     ret = atapi_eh_tur(dev, &sense_key);
0111 
0112     if (!ret || sense_key != NOT_READY)
0113         return false;
0114 
0115     sense_buf = dev->link->ap->sector_buf;
0116     ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
0117     if (ret)
0118         return false;
0119 
0120     /* sense valid */
0121     if ((sense_buf[0] & 0x7f) != 0x70)
0122         return false;
0123 
0124     add_len = sense_buf[7];
0125     /* has asc and ascq */
0126     if (add_len < 6)
0127         return false;
0128 
0129     asc = sense_buf[12];
0130     ascq = sense_buf[13];
0131 
0132     if (zpodd->mech_type == ODD_MECH_TYPE_SLOT)
0133         /* no media inside */
0134         return asc == 0x3a;
0135     else
0136         /* no media inside and door closed */
0137         return asc == 0x3a && ascq == 0x01;
0138 }
0139 
0140 /*
0141  * Update the zpodd->zp_ready field. This field will only be set
0142  * if the ODD has stayed in ZP ready state for zpodd_poweroff_delay
0143  * time, and will be used to decide if power off is allowed. If it
0144  * is set, it will be cleared during resume from powered off state.
0145  */
0146 void zpodd_on_suspend(struct ata_device *dev)
0147 {
0148     struct zpodd *zpodd = dev->zpodd;
0149     unsigned long expires;
0150 
0151     if (!zpready(dev)) {
0152         zpodd->zp_sampled = false;
0153         zpodd->zp_ready = false;
0154         return;
0155     }
0156 
0157     if (!zpodd->zp_sampled) {
0158         zpodd->zp_sampled = true;
0159         zpodd->last_ready = jiffies;
0160         return;
0161     }
0162 
0163     expires = zpodd->last_ready +
0164           msecs_to_jiffies(zpodd_poweroff_delay * 1000);
0165     if (time_before(jiffies, expires))
0166         return;
0167 
0168     zpodd->zp_ready = true;
0169 }
0170 
0171 bool zpodd_zpready(struct ata_device *dev)
0172 {
0173     struct zpodd *zpodd = dev->zpodd;
0174     return zpodd->zp_ready;
0175 }
0176 
0177 /*
0178  * Enable runtime wake capability through ACPI and set the powered_off flag,
0179  * this flag will be used during resume to decide what operations are needed
0180  * to take.
0181  *
0182  * Also, media poll needs to be silenced, so that it doesn't bring the ODD
0183  * back to full power state every few seconds.
0184  */
0185 void zpodd_enable_run_wake(struct ata_device *dev)
0186 {
0187     struct zpodd *zpodd = dev->zpodd;
0188 
0189     sdev_disable_disk_events(dev->sdev);
0190 
0191     zpodd->powered_off = true;
0192     acpi_pm_set_device_wakeup(&dev->tdev, true);
0193 }
0194 
0195 /* Disable runtime wake capability if it is enabled */
0196 void zpodd_disable_run_wake(struct ata_device *dev)
0197 {
0198     struct zpodd *zpodd = dev->zpodd;
0199 
0200     if (zpodd->powered_off)
0201         acpi_pm_set_device_wakeup(&dev->tdev, false);
0202 }
0203 
0204 /*
0205  * Post power on processing after the ODD has been recovered. If the
0206  * ODD wasn't powered off during suspend, it doesn't do anything.
0207  *
0208  * For drawer type ODD, if it is powered on due to user pressed the
0209  * eject button, the tray needs to be ejected. This can only be done
0210  * after the ODD has been recovered, i.e. link is initialized and
0211  * device is able to process NON_DATA PIO command, as eject needs to
0212  * send command for the ODD to process.
0213  *
0214  * The from_notify flag set in wake notification handler function
0215  * zpodd_wake_dev represents if power on is due to user's action.
0216  *
0217  * For both types of ODD, several fields need to be reset.
0218  */
0219 void zpodd_post_poweron(struct ata_device *dev)
0220 {
0221     struct zpodd *zpodd = dev->zpodd;
0222 
0223     if (!zpodd->powered_off)
0224         return;
0225 
0226     zpodd->powered_off = false;
0227 
0228     if (zpodd->from_notify) {
0229         zpodd->from_notify = false;
0230         if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
0231             eject_tray(dev);
0232     }
0233 
0234     zpodd->zp_sampled = false;
0235     zpodd->zp_ready = false;
0236 
0237     sdev_enable_disk_events(dev->sdev);
0238 }
0239 
0240 static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
0241 {
0242     struct ata_device *ata_dev = context;
0243     struct zpodd *zpodd = ata_dev->zpodd;
0244     struct device *dev = &ata_dev->sdev->sdev_gendev;
0245 
0246     if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
0247         zpodd->from_notify = true;
0248         pm_runtime_resume(dev);
0249     }
0250 }
0251 
0252 static void ata_acpi_add_pm_notifier(struct ata_device *dev)
0253 {
0254     acpi_handle handle = ata_dev_acpi_handle(dev);
0255     acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
0256                     zpodd_wake_dev, dev);
0257 }
0258 
0259 static void ata_acpi_remove_pm_notifier(struct ata_device *dev)
0260 {
0261     acpi_handle handle = ata_dev_acpi_handle(dev);
0262     acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev);
0263 }
0264 
0265 void zpodd_init(struct ata_device *dev)
0266 {
0267     struct acpi_device *adev = ACPI_COMPANION(&dev->tdev);
0268     enum odd_mech_type mech_type;
0269     struct zpodd *zpodd;
0270 
0271     if (dev->zpodd || !adev || !acpi_device_can_poweroff(adev))
0272         return;
0273 
0274     mech_type = zpodd_get_mech_type(dev);
0275     if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
0276         return;
0277 
0278     zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
0279     if (!zpodd)
0280         return;
0281 
0282     zpodd->mech_type = mech_type;
0283 
0284     ata_acpi_add_pm_notifier(dev);
0285     zpodd->dev = dev;
0286     dev->zpodd = zpodd;
0287     dev_pm_qos_expose_flags(&dev->tdev, 0);
0288 }
0289 
0290 void zpodd_exit(struct ata_device *dev)
0291 {
0292     ata_acpi_remove_pm_notifier(dev);
0293     kfree(dev->zpodd);
0294     dev->zpodd = NULL;
0295 }