diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 137 |
1 files changed, 99 insertions, 38 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 79b20da0a256..97d9f0488cc1 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -65,8 +65,8 @@ #include "libata-transport.h" const struct ata_port_operations ata_base_port_ops = { - .prereset = ata_std_prereset, - .postreset = ata_std_postreset, + .reset.prereset = ata_std_prereset, + .reset.postreset = ata_std_postreset, .error_handler = ata_std_error_handler, .sched_eh = ata_std_sched_eh, .end_eh = ata_std_end_eh, @@ -2154,14 +2154,46 @@ retry: return err_mask; } +static inline void ata_clear_log_directory(struct ata_device *dev) +{ + memset(dev->gp_log_dir, 0, ATA_SECT_SIZE); +} + +static int ata_read_log_directory(struct ata_device *dev) +{ + u16 version; + + /* If the log page is already cached, do nothing. */ + version = get_unaligned_le16(&dev->gp_log_dir[0]); + if (version == 0x0001) + return 0; + + if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->gp_log_dir, 1)) { + ata_clear_log_directory(dev); + return -EIO; + } + + version = get_unaligned_le16(&dev->gp_log_dir[0]); + if (version != 0x0001) { + ata_dev_err(dev, "Invalid log directory version 0x%04x\n", + version); + ata_clear_log_directory(dev); + dev->quirks |= ATA_QUIRK_NO_LOG_DIR; + return -EINVAL; + } + + return 0; +} + static int ata_log_supported(struct ata_device *dev, u8 log) { if (dev->quirks & ATA_QUIRK_NO_LOG_DIR) return 0; - if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->sector_buf, 1)) + if (ata_read_log_directory(dev)) return 0; - return get_unaligned_le16(&dev->sector_buf[log * 2]); + + return get_unaligned_le16(&dev->gp_log_dir[log * 2]); } static bool ata_identify_page_supported(struct ata_device *dev, u8 page) @@ -2421,18 +2453,7 @@ static void ata_dev_config_zac(struct ata_device *dev) dev->zac_zones_optimal_nonseq = U32_MAX; dev->zac_zones_max_open = U32_MAX; - /* - * Always set the 'ZAC' flag for Host-managed devices. - */ - if (dev->class == ATA_DEV_ZAC) - dev->flags |= ATA_DFLAG_ZAC; - else if (ata_id_zoned_cap(dev->id) == 0x01) - /* - * Check for host-aware devices. - */ - dev->flags |= ATA_DFLAG_ZAC; - - if (!(dev->flags & ATA_DFLAG_ZAC)) + if (!ata_dev_is_zac(dev)) return; if (!ata_identify_page_supported(dev, ATA_LOG_ZONED_INFORMATION)) { @@ -2495,7 +2516,7 @@ static void ata_dev_config_trusted(struct ata_device *dev) dev->flags |= ATA_DFLAG_TRUSTED; } -void ata_dev_cleanup_cdl_resources(struct ata_device *dev) +static void ata_dev_cleanup_cdl_resources(struct ata_device *dev) { kfree(dev->cdl); dev->cdl = NULL; @@ -2801,17 +2822,70 @@ out: kfree(buf); } +/* + * Configure features related to link power management. + */ +static void ata_dev_config_lpm(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + + if (ap->flags & ATA_FLAG_NO_LPM) { + /* + * When the port does not support LPM, we cannot support it on + * the device either. + */ + dev->quirks |= ATA_QUIRK_NOLPM; + } else { + /* + * Some WD SATA-1 drives have issues with LPM, turn on NOLPM for + * them. + */ + if ((dev->quirks & ATA_QUIRK_WD_BROKEN_LPM) && + (dev->id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) + dev->quirks |= ATA_QUIRK_NOLPM; + + /* ATI specific quirk */ + if ((dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI) && + ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI)) + dev->quirks |= ATA_QUIRK_NOLPM; + } + + if (dev->quirks & ATA_QUIRK_NOLPM && + ap->target_lpm_policy != ATA_LPM_MAX_POWER) { + ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); + ap->target_lpm_policy = ATA_LPM_MAX_POWER; + } + + /* + * Device Initiated Power Management (DIPM) is normally disabled by + * default on a device. However, DIPM may have been enabled and that + * setting kept even after COMRESET because of the Software Settings + * Preservation feature. So if the port does not support DIPM and the + * device does, disable DIPM on the device. + */ + if (ap->flags & ATA_FLAG_NO_DIPM && ata_id_has_dipm(dev->id)) { + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_DISABLE, SATA_DIPM); + if (err_mask && err_mask != AC_ERR_DEV) + ata_dev_err(dev, "Disable DIPM failed, Emask 0x%x\n", + err_mask); + } +} + static void ata_dev_print_features(struct ata_device *dev) { if (!(dev->flags & ATA_DFLAG_FEATURES_MASK)) return; ata_dev_info(dev, - "Features:%s%s%s%s%s%s%s%s\n", + "Features:%s%s%s%s%s%s%s%s%s%s\n", dev->flags & ATA_DFLAG_FUA ? " FUA" : "", dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "", dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "", dev->flags & ATA_DFLAG_DEVSLP ? " Dev-Sleep" : "", + ata_id_has_hipm(dev->id) ? " HIPM" : "", + ata_id_has_dipm(dev->id) ? " DIPM" : "", dev->flags & ATA_DFLAG_NCQ_SEND_RECV ? " NCQ-sndrcv" : "", dev->flags & ATA_DFLAG_NCQ_PRIO ? " NCQ-prio" : "", dev->flags & ATA_DFLAG_CDL ? " CDL" : "", @@ -2848,6 +2922,9 @@ int ata_dev_configure(struct ata_device *dev) return 0; } + /* Clear the general purpose log directory cache. */ + ata_clear_log_directory(dev); + /* Set quirks */ dev->quirks |= ata_dev_quirks(dev); ata_force_quirks(dev); @@ -2871,23 +2948,6 @@ int ata_dev_configure(struct ata_device *dev) if (rc) return rc; - /* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */ - if ((dev->quirks & ATA_QUIRK_WD_BROKEN_LPM) && - (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) - dev->quirks |= ATA_QUIRK_NOLPM; - - if (dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI && - ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI)) - dev->quirks |= ATA_QUIRK_NOLPM; - - if (ap->flags & ATA_FLAG_NO_LPM) - dev->quirks |= ATA_QUIRK_NOLPM; - - if (dev->quirks & ATA_QUIRK_NOLPM) { - ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); - dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER; - } - /* let ACPI work its magic */ rc = ata_acpi_on_devcfg(dev); if (rc) @@ -2974,6 +3034,7 @@ int ata_dev_configure(struct ata_device *dev) ata_dev_config_chs(dev); } + ata_dev_config_lpm(dev); ata_dev_config_fua(dev); ata_dev_config_devslp(dev); ata_dev_config_sense_reporting(dev); @@ -3449,7 +3510,7 @@ static int ata_dev_set_mode(struct ata_device *dev) } /** - * ata_do_set_mode - Program timings and issue SET FEATURES - XFER + * ata_set_mode - Program timings and issue SET FEATURES - XFER * @link: link on which timings will be programmed * @r_failed_dev: out parameter for failed device * @@ -3465,7 +3526,7 @@ static int ata_dev_set_mode(struct ata_device *dev) * 0 on success, negative errno otherwise */ -int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) +int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) { struct ata_port *ap = link->ap; struct ata_device *dev; @@ -3546,7 +3607,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) *r_failed_dev = dev; return rc; } -EXPORT_SYMBOL_GPL(ata_do_set_mode); +EXPORT_SYMBOL_GPL(ata_set_mode); /** * ata_wait_ready - wait for link to become ready |