diff options
-rw-r--r-- | drivers/ata/libata-core.c | 27 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 18 | ||||
-rw-r--r-- | include/linux/libata.h | 2 |
3 files changed, 44 insertions, 3 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 1393a5890ed5..1a3dbd1b196e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
@@ -2222,6 +2222,16 @@ int ata_dev_configure(struct ata_device *dev) | |||
2222 | if (rc) | 2222 | if (rc) |
2223 | return rc; | 2223 | return rc; |
2224 | 2224 | ||
2225 | /* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */ | ||
2226 | if ((dev->horkage & ATA_HORKAGE_WD_BROKEN_LPM) && | ||
2227 | (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) | ||
2228 | dev->horkage |= ATA_HORKAGE_NOLPM; | ||
2229 | |||
2230 | if (dev->horkage & ATA_HORKAGE_NOLPM) { | ||
2231 | ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); | ||
2232 | dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER; | ||
2233 | } | ||
2234 | |||
2225 | /* let ACPI work its magic */ | 2235 | /* let ACPI work its magic */ |
2226 | rc = ata_acpi_on_devcfg(dev); | 2236 | rc = ata_acpi_on_devcfg(dev); |
2227 | if (rc) | 2237 | if (rc) |
@@ -4216,6 +4226,23 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { | |||
4216 | { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, | 4226 | { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, |
4217 | { "Crucial_CT???M500SSD1", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, | 4227 | { "Crucial_CT???M500SSD1", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, |
4218 | 4228 | ||
4229 | /* | ||
4230 | * Some WD SATA-I drives spin up and down erratically when the link | ||
4231 | * is put into the slumber mode. We don't have full list of the | ||
4232 | * affected devices. Disable LPM if the device matches one of the | ||
4233 | * known prefixes and is SATA-1. As a side effect LPM partial is | ||
4234 | * lost too. | ||
4235 | * | ||
4236 | * https://bugzilla.kernel.org/show_bug.cgi?id=57211 | ||
4237 | */ | ||
4238 | { "WDC WD800JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4239 | { "WDC WD1200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4240 | { "WDC WD1600JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4241 | { "WDC WD2000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4242 | { "WDC WD2500JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4243 | { "WDC WD3000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4244 | { "WDC WD3200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, | ||
4245 | |||
4219 | /* End Marker */ | 4246 | /* End Marker */ |
4220 | { } | 4247 | { } |
4221 | }; | 4248 | }; |
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 176f62950e3d..75ef9527603e 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -111,12 +111,14 @@ static const char *ata_lpm_policy_names[] = { | |||
111 | [ATA_LPM_MIN_POWER] = "min_power", | 111 | [ATA_LPM_MIN_POWER] = "min_power", |
112 | }; | 112 | }; |
113 | 113 | ||
114 | static ssize_t ata_scsi_lpm_store(struct device *dev, | 114 | static ssize_t ata_scsi_lpm_store(struct device *device, |
115 | struct device_attribute *attr, | 115 | struct device_attribute *attr, |
116 | const char *buf, size_t count) | 116 | const char *buf, size_t count) |
117 | { | 117 | { |
118 | struct Scsi_Host *shost = class_to_shost(dev); | 118 | struct Scsi_Host *shost = class_to_shost(device); |
119 | struct ata_port *ap = ata_shost_to_port(shost); | 119 | struct ata_port *ap = ata_shost_to_port(shost); |
120 | struct ata_link *link; | ||
121 | struct ata_device *dev; | ||
120 | enum ata_lpm_policy policy; | 122 | enum ata_lpm_policy policy; |
121 | unsigned long flags; | 123 | unsigned long flags; |
122 | 124 | ||
@@ -132,10 +134,20 @@ static ssize_t ata_scsi_lpm_store(struct device *dev, | |||
132 | return -EINVAL; | 134 | return -EINVAL; |
133 | 135 | ||
134 | spin_lock_irqsave(ap->lock, flags); | 136 | spin_lock_irqsave(ap->lock, flags); |
137 | |||
138 | ata_for_each_link(link, ap, EDGE) { | ||
139 | ata_for_each_dev(dev, &ap->link, ENABLED) { | ||
140 | if (dev->horkage & ATA_HORKAGE_NOLPM) { | ||
141 | count = -EOPNOTSUPP; | ||
142 | goto out_unlock; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
135 | ap->target_lpm_policy = policy; | 147 | ap->target_lpm_policy = policy; |
136 | ata_port_schedule_eh(ap); | 148 | ata_port_schedule_eh(ap); |
149 | out_unlock: | ||
137 | spin_unlock_irqrestore(ap->lock, flags); | 150 | spin_unlock_irqrestore(ap->lock, flags); |
138 | |||
139 | return count; | 151 | return count; |
140 | } | 152 | } |
141 | 153 | ||
diff --git a/include/linux/libata.h b/include/linux/libata.h index 9b503376738f..bec6dbe939a0 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -419,6 +419,8 @@ enum { | |||
419 | ATA_HORKAGE_MAX_SEC_LBA48 = (1 << 17), /* Set max sects to 65535 */ | 419 | ATA_HORKAGE_MAX_SEC_LBA48 = (1 << 17), /* Set max sects to 65535 */ |
420 | ATA_HORKAGE_ATAPI_DMADIR = (1 << 18), /* device requires dmadir */ | 420 | ATA_HORKAGE_ATAPI_DMADIR = (1 << 18), /* device requires dmadir */ |
421 | ATA_HORKAGE_NO_NCQ_TRIM = (1 << 19), /* don't use queued TRIM */ | 421 | ATA_HORKAGE_NO_NCQ_TRIM = (1 << 19), /* don't use queued TRIM */ |
422 | ATA_HORKAGE_NOLPM = (1 << 20), /* don't use LPM */ | ||
423 | ATA_HORKAGE_WD_BROKEN_LPM = (1 << 21), /* some WDs have broken LPM */ | ||
422 | 424 | ||
423 | /* DMA mask for user DMA control: User visible values; DO NOT | 425 | /* DMA mask for user DMA control: User visible values; DO NOT |
424 | renumber */ | 426 | renumber */ |