Johnny Hughes
2019-02-04 c1f36c28393a7bb126cbf436cd6a4077a5b5c313
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
From c75c91833dd0998ece4c2fbc6bf97a87e19551e3 Mon Sep 17 00:00:00 2001
From: Khuong Dinh <khuong.dinh@amperecomputing.com>
Date: Sun, 1 Jul 2018 12:15:46 +0200
Subject: [PATCH 05/11] BACKPORT: 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.
 
This patch is required to support AHCI ALPM de-feature for Ampere Computing
eMAG SATA.
This patch is backported from:
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?h=next-20180913&id=240630e61870e62e39a97225048f9945848fa5f5
 
Change-Id: I077e886a4faff18e2cb884c361065d9bd0a22e50
Cc: stable@vger.kernel.org
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Khuong Dinh <khuong.dinh@amperecomputing.com>
---
 drivers/ata/ahci.c        | 59 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/libata-core.c |  3 +++
 include/linux/libata.h    |  1 +
 3 files changed, 63 insertions(+)
 
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 9f78bb0..e0c2de6 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1260,6 +1260,59 @@ static bool ahci_broken_suspend(struct pci_dev *pdev)
     return strcmp(buf, dmi->driver_data) < 0;
 }
 
+static bool ahci_broken_lpm(struct pci_dev *pdev)
+{
+    static const struct dmi_system_id sysids[] = {
+        /* Various Lenovo 50 series have LPM issues with older BIOSen */
+        {
+            .matches = {
+                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X250"),
+            },
+            .driver_data = "20180406", /* 1.31 */
+        },
+        {
+            .matches = {
+                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L450"),
+            },
+            .driver_data = "20180420", /* 1.28 */
+        },
+        {
+            .matches = {
+                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T450s"),
+            },
+            .driver_data = "20180315", /* 1.33 */
+        },
+        {
+            .matches = {
+                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W541"),
+            },
+            /*
+             * Note date based on release notes, 2.35 has been
+             * reported to be good, but I've been unable to get
+             * a hold of the reporter to get the DMI BIOS date.
+             * TODO: fix this.
+             */
+            .driver_data = "20180310", /* 2.35 */
+        },
+        { }    /* terminate list */
+    };
+    const struct dmi_system_id *dmi = dmi_first_match(sysids);
+    int year, month, date;
+    char buf[9];
+
+    if (!dmi)
+        return false;
+
+    dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
+    snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
+
+    return strcmp(buf, dmi->driver_data) < 0;
+}
+
 static bool ahci_broken_online(struct pci_dev *pdev)
 {
 #define ENCODE_BUSDEVFN(bus, slot, func)            \
@@ -1670,6 +1723,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
             "quirky BIOS, skipping spindown on poweroff\n");
     }
 
+    if (ahci_broken_lpm(pdev)) {
+        pi.flags |= ATA_FLAG_NO_LPM;
+        dev_warn(&pdev->dev,
+             "BIOS update required for Link Power Management support\n");
+    }
+
     if (ahci_broken_suspend(pdev)) {
         hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
         dev_warn(&pdev->dev,
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index ee4c1ec..35346c1 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2501,6 +2501,9 @@ int ata_dev_configure(struct ata_device *dev)
         (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
         dev->horkage |= ATA_HORKAGE_NOLPM;
 
+    if (ap->flags & ATA_FLAG_NO_LPM)
+        dev->horkage |= ATA_HORKAGE_NOLPM;
+
     if (dev->horkage & ATA_HORKAGE_NOLPM) {
         ata_dev_warn(dev, "LPM support broken, forcing max_power\n");
         dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 6b7a196..a4a7e0f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -211,6 +211,7 @@ enum {
     ATA_FLAG_SLAVE_POSS    = (1 << 0), /* host supports slave dev */
                         /* (doesn't imply presence) */
     ATA_FLAG_SATA        = (1 << 1),
+    ATA_FLAG_NO_LPM        = (1 << 2), /* host not happy with LPM */
     ATA_FLAG_NO_LOG_PAGE    = (1 << 5), /* do not issue log page read */
     ATA_FLAG_NO_ATAPI    = (1 << 6), /* No ATAPI support */
     ATA_FLAG_PIO_DMA    = (1 << 7), /* PIO cmds via DMA */
-- 
1.8.3.1