]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.8.11/iwlwifi-pcie-fix-splc-structure-parsing.patch
6.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.8.11 / iwlwifi-pcie-fix-splc-structure-parsing.patch
CommitLineData
f1ee1543
GKH
1From e0d9727c111a5917a1184c71c1a8e6f78c7fc41d Mon Sep 17 00:00:00 2001
2From: Luca Coelho <luciano.coelho@intel.com>
3Date: Thu, 13 Oct 2016 10:07:07 +0300
4Subject: iwlwifi: pcie: fix SPLC structure parsing
5
6From: Luca Coelho <luciano.coelho@intel.com>
7
8commit e0d9727c111a5917a1184c71c1a8e6f78c7fc41d upstream.
9
10The SPLC data parsing is too restrictive and was not trying find the
11correct element for WiFi. This causes problems with some BIOSes where
12the SPLC method exists, but doesn't have a WiFi entry on the first
13element of the list. The domain type values are also incorrect
14according to the specification.
15
16Fix this by complying with the actual specification.
17
18Additionally, replace all occurrences of SPLX to SPLC, since SPLX is
19only a structure internal to the ACPI tables, and may not even exist.
20
21Fixes: bcb079a14d75 ("iwlwifi: pcie: retrieve and parse ACPI power limitations")
22Reported-by: Chris Rorvick <chris@rorvick.com>
23Tested-by: Paul Bolle <pebolle@tiscali.nl>
24Tested-by: Chris Rorvick <chris@rorvick.com>
25Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
26Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
27
28---
29 drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 77 +++++++++++++++-----------
30 1 file changed, 47 insertions(+), 30 deletions(-)
31
32--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
33+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
34@@ -526,48 +526,64 @@ static const struct pci_device_id iwl_hw
35 MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
36
37 #ifdef CONFIG_ACPI
38-#define SPL_METHOD "SPLC"
39-#define SPL_DOMAINTYPE_MODULE BIT(0)
40-#define SPL_DOMAINTYPE_WIFI BIT(1)
41-#define SPL_DOMAINTYPE_WIGIG BIT(2)
42-#define SPL_DOMAINTYPE_RFEM BIT(3)
43+#define ACPI_SPLC_METHOD "SPLC"
44+#define ACPI_SPLC_DOMAIN_WIFI (0x07)
45
46-static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
47+static u64 splc_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splc)
48 {
49- union acpi_object *limits, *domain_type, *power_limit;
50+ union acpi_object *data_pkg, *dflt_pwr_limit;
51+ int i;
52
53- if (splx->type != ACPI_TYPE_PACKAGE ||
54- splx->package.count != 2 ||
55- splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
56- splx->package.elements[0].integer.value != 0) {
57- IWL_ERR(trans, "Unsupported splx structure\n");
58+ /* We need at least two elements, one for the revision and one
59+ * for the data itself. Also check that the revision is
60+ * supported (currently only revision 0).
61+ */
62+ if (splc->type != ACPI_TYPE_PACKAGE ||
63+ splc->package.count < 2 ||
64+ splc->package.elements[0].type != ACPI_TYPE_INTEGER ||
65+ splc->package.elements[0].integer.value != 0) {
66+ IWL_DEBUG_INFO(trans,
67+ "Unsupported structure returned by the SPLC method. Ignoring.\n");
68 return 0;
69 }
70
71- limits = &splx->package.elements[1];
72- if (limits->type != ACPI_TYPE_PACKAGE ||
73- limits->package.count < 2 ||
74- limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
75- limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
76- IWL_ERR(trans, "Invalid limits element\n");
77- return 0;
78+ /* loop through all the packages to find the one for WiFi */
79+ for (i = 1; i < splc->package.count; i++) {
80+ union acpi_object *domain;
81+
82+ data_pkg = &splc->package.elements[i];
83+
84+ /* Skip anything that is not a package with the right
85+ * amount of elements (i.e. at least 2 integers).
86+ */
87+ if (data_pkg->type != ACPI_TYPE_PACKAGE ||
88+ data_pkg->package.count < 2 ||
89+ data_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
90+ data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
91+ continue;
92+
93+ domain = &data_pkg->package.elements[0];
94+ if (domain->integer.value == ACPI_SPLC_DOMAIN_WIFI)
95+ break;
96+
97+ data_pkg = NULL;
98 }
99
100- domain_type = &limits->package.elements[0];
101- power_limit = &limits->package.elements[1];
102- if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
103- IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
104+ if (!data_pkg) {
105+ IWL_DEBUG_INFO(trans,
106+ "No element for the WiFi domain returned by the SPLC method.\n");
107 return 0;
108 }
109
110- return power_limit->integer.value;
111+ dflt_pwr_limit = &data_pkg->package.elements[1];
112+ return dflt_pwr_limit->integer.value;
113 }
114
115 static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
116 {
117 acpi_handle pxsx_handle;
118 acpi_handle handle;
119- struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
120+ struct acpi_buffer splc = {ACPI_ALLOCATE_BUFFER, NULL};
121 acpi_status status;
122
123 pxsx_handle = ACPI_HANDLE(&pdev->dev);
124@@ -578,23 +594,24 @@ static void set_dflt_pwr_limit(struct iw
125 }
126
127 /* Get the method's handle */
128- status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
129+ status = acpi_get_handle(pxsx_handle, (acpi_string)ACPI_SPLC_METHOD,
130+ &handle);
131 if (ACPI_FAILURE(status)) {
132- IWL_DEBUG_INFO(trans, "SPL method not found\n");
133+ IWL_DEBUG_INFO(trans, "SPLC method not found\n");
134 return;
135 }
136
137 /* Call SPLC with no arguments */
138- status = acpi_evaluate_object(handle, NULL, NULL, &splx);
139+ status = acpi_evaluate_object(handle, NULL, NULL, &splc);
140 if (ACPI_FAILURE(status)) {
141 IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
142 return;
143 }
144
145- trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
146+ trans->dflt_pwr_limit = splc_get_pwr_limit(trans, splc.pointer);
147 IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
148 trans->dflt_pwr_limit);
149- kfree(splx.pointer);
150+ kfree(splc.pointer);
151 }
152
153 #else /* CONFIG_ACPI */