summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2018-07-01 06:15:46 -0400
committerTejun Heo <tj@kernel.org>2018-07-02 10:54:38 -0400
commit240630e61870e62e39a97225048f9945848fa5f5 (patch)
tree8dfa3ff969ae958f0925db3ec3822c4b6792d46a
parent8740fa6f5c834a881b9b0d560e62c6fe3c1e60ef (diff)
ahci: Disable LPM on Lenovo 50 series laptops with a too old BIOS
There have been several reports of LPM related hard freezes about once a day on multiple Lenovo 50 series models. Strange enough these reports where not disk model specific as LPM issues usually are and some users with the exact same disk + laptop where seeing them while other users where not seeing these issues. It turns out that enabling LPM triggers a firmware bug somewhere, which has been fixed in later BIOS versions. This commit adds a new ahci_broken_lpm() function and a new ATA_FLAG_NO_LPM for dealing with this. The ahci_broken_lpm() function contains DMI match info for the 4 models which are known to be affected by this and the DMI BIOS date field for known good BIOS versions. If the BIOS date is older then the one in the table LPM will be disabled and a warning will be printed. Note the BIOS dates are for known good versions, some older versions may work too, but we don't know for sure, the table is using dates from BIOS versions for which users have confirmed that upgrading to that version makes the problem go away. Unfortunately I've been unable to get hold of the reporter who reported that BIOS version 2.35 fixed the problems on the W541 for him. I've been able to verify the DMI_SYS_VENDOR and DMI_PRODUCT_VERSION from an older dmidecode, but I don't know the exact BIOS date as reported in the DMI. Lenovo keeps a changelog with dates in their release notes, but the dates there are the release dates not the build dates which are in DMI. So I've chosen to set the date to which we compare to one day past the release date of the 2.34 BIOS. I plan to fix this with a follow up commit once I've the necessary info. Cc: stable@vger.kernel.org Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r--drivers/ata/ahci.c59
-rw-r--r--drivers/ata/libata-core.c3
-rw-r--r--include/linux/libata.h1
3 files changed, 63 insertions, 0 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 738fb22978dd..fdeb3b4d0f4a 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1280,6 +1280,59 @@ static bool ahci_broken_suspend(struct pci_dev *pdev)
1280 return strcmp(buf, dmi->driver_data) < 0; 1280 return strcmp(buf, dmi->driver_data) < 0;
1281} 1281}
1282 1282
1283static bool ahci_broken_lpm(struct pci_dev *pdev)
1284{
1285 static const struct dmi_system_id sysids[] = {
1286 /* Various Lenovo 50 series have LPM issues with older BIOSen */
1287 {
1288 .matches = {
1289 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1290 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X250"),
1291 },
1292 .driver_data = "20180406", /* 1.31 */
1293 },
1294 {
1295 .matches = {
1296 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1297 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L450"),
1298 },
1299 .driver_data = "20180420", /* 1.28 */
1300 },
1301 {
1302 .matches = {
1303 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1304 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T450s"),
1305 },
1306 .driver_data = "20180315", /* 1.33 */
1307 },
1308 {
1309 .matches = {
1310 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1311 DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W541"),
1312 },
1313 /*
1314 * Note date based on release notes, 2.35 has been
1315 * reported to be good, but I've been unable to get
1316 * a hold of the reporter to get the DMI BIOS date.
1317 * TODO: fix this.
1318 */
1319 .driver_data = "20180310", /* 2.35 */
1320 },
1321 { } /* terminate list */
1322 };
1323 const struct dmi_system_id *dmi = dmi_first_match(sysids);
1324 int year, month, date;
1325 char buf[9];
1326
1327 if (!dmi)
1328 return false;
1329
1330 dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
1331 snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
1332
1333 return strcmp(buf, dmi->driver_data) < 0;
1334}
1335
1283static bool ahci_broken_online(struct pci_dev *pdev) 1336static bool ahci_broken_online(struct pci_dev *pdev)
1284{ 1337{
1285#define ENCODE_BUSDEVFN(bus, slot, func) \ 1338#define ENCODE_BUSDEVFN(bus, slot, func) \
@@ -1694,6 +1747,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
1694 "quirky BIOS, skipping spindown on poweroff\n"); 1747 "quirky BIOS, skipping spindown on poweroff\n");
1695 } 1748 }
1696 1749
1750 if (ahci_broken_lpm(pdev)) {
1751 pi.flags |= ATA_FLAG_NO_LPM;
1752 dev_warn(&pdev->dev,
1753 "BIOS update required for Link Power Management support\n");
1754 }
1755
1697 if (ahci_broken_suspend(pdev)) { 1756 if (ahci_broken_suspend(pdev)) {
1698 hpriv->flags |= AHCI_HFLAG_NO_SUSPEND; 1757 hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
1699 dev_warn(&pdev->dev, 1758 dev_warn(&pdev->dev,
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 27d15ed7fa3d..cc71c63df381 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2493,6 +2493,9 @@ int ata_dev_configure(struct ata_device *dev)
2493 (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) 2493 (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
2494 dev->horkage |= ATA_HORKAGE_NOLPM; 2494 dev->horkage |= ATA_HORKAGE_NOLPM;
2495 2495
2496 if (ap->flags & ATA_FLAG_NO_LPM)
2497 dev->horkage |= ATA_HORKAGE_NOLPM;
2498
2496 if (dev->horkage & ATA_HORKAGE_NOLPM) { 2499 if (dev->horkage & ATA_HORKAGE_NOLPM) {
2497 ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); 2500 ata_dev_warn(dev, "LPM support broken, forcing max_power\n");
2498 dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER; 2501 dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index a2257e380789..32f247cb5e9e 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -210,6 +210,7 @@ enum {
210 ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ 210 ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
211 /* (doesn't imply presence) */ 211 /* (doesn't imply presence) */
212 ATA_FLAG_SATA = (1 << 1), 212 ATA_FLAG_SATA = (1 << 1),
213 ATA_FLAG_NO_LPM = (1 << 2), /* host not happy with LPM */
213 ATA_FLAG_NO_LOG_PAGE = (1 << 5), /* do not issue log page read */ 214 ATA_FLAG_NO_LOG_PAGE = (1 << 5), /* do not issue log page read */
214 ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */ 215 ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */
215 ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */ 216 ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */