]>
Commit | Line | Data |
---|---|---|
f1ee1543 GKH |
1 | From e0d9727c111a5917a1184c71c1a8e6f78c7fc41d Mon Sep 17 00:00:00 2001 |
2 | From: Luca Coelho <luciano.coelho@intel.com> | |
3 | Date: Thu, 13 Oct 2016 10:07:07 +0300 | |
4 | Subject: iwlwifi: pcie: fix SPLC structure parsing | |
5 | ||
6 | From: Luca Coelho <luciano.coelho@intel.com> | |
7 | ||
8 | commit e0d9727c111a5917a1184c71c1a8e6f78c7fc41d upstream. | |
9 | ||
10 | The SPLC data parsing is too restrictive and was not trying find the | |
11 | correct element for WiFi. This causes problems with some BIOSes where | |
12 | the SPLC method exists, but doesn't have a WiFi entry on the first | |
13 | element of the list. The domain type values are also incorrect | |
14 | according to the specification. | |
15 | ||
16 | Fix this by complying with the actual specification. | |
17 | ||
18 | Additionally, replace all occurrences of SPLX to SPLC, since SPLX is | |
19 | only a structure internal to the ACPI tables, and may not even exist. | |
20 | ||
21 | Fixes: bcb079a14d75 ("iwlwifi: pcie: retrieve and parse ACPI power limitations") | |
22 | Reported-by: Chris Rorvick <chris@rorvick.com> | |
23 | Tested-by: Paul Bolle <pebolle@tiscali.nl> | |
24 | Tested-by: Chris Rorvick <chris@rorvick.com> | |
25 | Signed-off-by: Luca Coelho <luciano.coelho@intel.com> | |
26 | Signed-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 */ |