diff options
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r-- | drivers/platform/x86/Kconfig | 34 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acerhdf.c | 602 | ||||
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 111 | ||||
-rw-r--r-- | drivers/platform/x86/asus_acpi.c | 30 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 56 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 126 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 87 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 411 |
9 files changed, 1279 insertions, 179 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c682ac536415..7232fe7104aa 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -34,10 +34,27 @@ config ACER_WMI | |||
34 | If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M | 34 | If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M |
35 | here. | 35 | here. |
36 | 36 | ||
37 | config ACERHDF | ||
38 | tristate "Acer Aspire One temperature and fan driver" | ||
39 | depends on THERMAL && THERMAL_HWMON && ACPI | ||
40 | ---help--- | ||
41 | This is a driver for Acer Aspire One netbooks. It allows to access | ||
42 | the temperature sensor and to control the fan. | ||
43 | |||
44 | After loading this driver the BIOS is still in control of the fan. | ||
45 | To let the kernel handle the fan, do: | ||
46 | echo -n enabled > /sys/class/thermal/thermal_zone0/mode | ||
47 | |||
48 | For more information about this driver see | ||
49 | <http://piie.net/files/acerhdf_README.txt> | ||
50 | |||
51 | If you have an Acer Aspire One netbook, say Y or M | ||
52 | here. | ||
53 | |||
37 | config ASUS_LAPTOP | 54 | config ASUS_LAPTOP |
38 | tristate "Asus Laptop Extras (EXPERIMENTAL)" | 55 | tristate "Asus Laptop Extras" |
39 | depends on ACPI | 56 | depends on ACPI |
40 | depends on EXPERIMENTAL && !ACPI_ASUS | 57 | depends on !ACPI_ASUS |
41 | select LEDS_CLASS | 58 | select LEDS_CLASS |
42 | select NEW_LEDS | 59 | select NEW_LEDS |
43 | select BACKLIGHT_CLASS_DEVICE | 60 | select BACKLIGHT_CLASS_DEVICE |
@@ -45,12 +62,12 @@ config ASUS_LAPTOP | |||
45 | ---help--- | 62 | ---help--- |
46 | This is the new Linux driver for Asus laptops. It may also support some | 63 | This is the new Linux driver for Asus laptops. It may also support some |
47 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate | 64 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate |
48 | standard ACPI events that go through /proc/acpi/events. It also adds | 65 | standard ACPI events and input events. It also adds |
49 | support for video output switching, LCD backlight control, Bluetooth and | 66 | support for video output switching, LCD backlight control, Bluetooth and |
50 | Wlan control, and most importantly, allows you to blink those fancy LEDs. | 67 | Wlan control, and most importantly, allows you to blink those fancy LEDs. |
51 | 68 | ||
52 | For more information and a userspace daemon for handling the extra | 69 | For more information and a userspace daemon for handling the extra |
53 | buttons see <http://acpi4asus.sf.net/>. | 70 | buttons see <http://acpi4asus.sf.net>. |
54 | 71 | ||
55 | If you have an ACPI-compatible ASUS laptop, say Y or M here. | 72 | If you have an ACPI-compatible ASUS laptop, say Y or M here. |
56 | 73 | ||
@@ -342,7 +359,10 @@ config EEEPC_LAPTOP | |||
342 | select HWMON | 359 | select HWMON |
343 | ---help--- | 360 | ---help--- |
344 | This driver supports the Fn-Fx keys on Eee PC laptops. | 361 | This driver supports the Fn-Fx keys on Eee PC laptops. |
345 | It also adds the ability to switch camera/wlan on/off. | 362 | |
363 | It also gives access to some extra laptop functionalities like | ||
364 | Bluetooth, backlight and allows powering on/off some other | ||
365 | devices. | ||
346 | 366 | ||
347 | If you have an Eee PC laptop, say Y or M here. | 367 | If you have an Eee PC laptop, say Y or M here. |
348 | 368 | ||
@@ -369,7 +389,7 @@ config ACPI_WMI | |||
369 | any ACPI-WMI devices. | 389 | any ACPI-WMI devices. |
370 | 390 | ||
371 | config ACPI_ASUS | 391 | config ACPI_ASUS |
372 | tristate "ASUS/Medion Laptop Extras" | 392 | tristate "ASUS/Medion Laptop Extras (DEPRECATED)" |
373 | depends on ACPI | 393 | depends on ACPI |
374 | select BACKLIGHT_CLASS_DEVICE | 394 | select BACKLIGHT_CLASS_DEVICE |
375 | ---help--- | 395 | ---help--- |
@@ -390,7 +410,7 @@ config ACPI_ASUS | |||
390 | parameters. | 410 | parameters. |
391 | 411 | ||
392 | More information and a userspace daemon for handling the extra buttons | 412 | More information and a userspace daemon for handling the extra buttons |
393 | at <http://sourceforge.net/projects/acpi4asus/>. | 413 | at <http://acpi4asus.sf.net>. |
394 | 414 | ||
395 | If you have an ACPI-compatible ASUS laptop, say Y or M here. This | 415 | If you have an ACPI-compatible ASUS laptop, say Y or M here. This |
396 | driver is still under development, so if your laptop is unsupported or | 416 | driver is still under development, so if your laptop is unsupported or |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e40c7bd1b87e..641b8bfa5538 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o | |||
9 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o | 9 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o |
10 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o | 10 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o |
11 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o | 11 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o |
12 | obj-$(CONFIG_ACERHDF) += acerhdf.o | ||
12 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | 13 | obj-$(CONFIG_HP_WMI) += hp-wmi.o |
13 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 14 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
14 | obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o | 15 | obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o |
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c new file mode 100644 index 000000000000..bdfee177eefb --- /dev/null +++ b/drivers/platform/x86/acerhdf.c | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * acerhdf - A driver which monitors the temperature | ||
3 | * of the aspire one netbook, turns on/off the fan | ||
4 | * as soon as the upper/lower threshold is reached. | ||
5 | * | ||
6 | * (C) 2009 - Peter Feuerer peter (a) piie.net | ||
7 | * http://piie.net | ||
8 | * 2009 Borislav Petkov <petkovbb@gmail.com> | ||
9 | * | ||
10 | * Inspired by and many thanks to: | ||
11 | * o acerfand - Rachel Greenham | ||
12 | * o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com | ||
13 | * - Petr Tomasek tomasek (#) etf,cuni,cz | ||
14 | * - Carlos Corbacho cathectic (at) gmail.com | ||
15 | * o lkml - Matthew Garrett | ||
16 | * - Borislav Petkov | ||
17 | * - Andreas Mohr | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License as published by | ||
21 | * the Free Software Foundation; either version 2 of the License, or | ||
22 | * (at your option) any later version. | ||
23 | * | ||
24 | * This program is distributed in the hope that it will be useful, | ||
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
27 | * GNU General Public License for more details. | ||
28 | * | ||
29 | * You should have received a copy of the GNU General Public License | ||
30 | * along with this program; if not, write to the Free Software | ||
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
32 | */ | ||
33 | |||
34 | #define pr_fmt(fmt) "acerhdf: " fmt | ||
35 | |||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/module.h> | ||
38 | #include <linux/fs.h> | ||
39 | #include <linux/dmi.h> | ||
40 | #include <acpi/acpi_drivers.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/thermal.h> | ||
43 | #include <linux/platform_device.h> | ||
44 | |||
45 | /* | ||
46 | * The driver is started with "kernel mode off" by default. That means, the BIOS | ||
47 | * is still in control of the fan. In this mode the driver allows to read the | ||
48 | * temperature of the cpu and a userspace tool may take over control of the fan. | ||
49 | * If the driver is switched to "kernel mode" (e.g. via module parameter) the | ||
50 | * driver is in full control of the fan. If you want the module to be started in | ||
51 | * kernel mode by default, define the following: | ||
52 | */ | ||
53 | #undef START_IN_KERNEL_MODE | ||
54 | |||
55 | #define DRV_VER "0.5.13" | ||
56 | |||
57 | /* | ||
58 | * According to the Atom N270 datasheet, | ||
59 | * (http://download.intel.com/design/processor/datashts/320032.pdf) the | ||
60 | * CPU's optimal operating limits denoted in junction temperature as | ||
61 | * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So, | ||
62 | * assume 89°C is critical temperature. | ||
63 | */ | ||
64 | #define ACERHDF_TEMP_CRIT 89 | ||
65 | #define ACERHDF_FAN_OFF 0 | ||
66 | #define ACERHDF_FAN_AUTO 1 | ||
67 | |||
68 | /* | ||
69 | * No matter what value the user puts into the fanon variable, turn on the fan | ||
70 | * at 80 degree Celsius to prevent hardware damage | ||
71 | */ | ||
72 | #define ACERHDF_MAX_FANON 80 | ||
73 | |||
74 | /* | ||
75 | * Maximum interval between two temperature checks is 15 seconds, as the die | ||
76 | * can get hot really fast under heavy load (plus we shouldn't forget about | ||
77 | * possible impact of _external_ aggressive sources such as heaters, sun etc.) | ||
78 | */ | ||
79 | #define ACERHDF_MAX_INTERVAL 15 | ||
80 | |||
81 | #ifdef START_IN_KERNEL_MODE | ||
82 | static int kernelmode = 1; | ||
83 | #else | ||
84 | static int kernelmode; | ||
85 | #endif | ||
86 | |||
87 | static unsigned int interval = 10; | ||
88 | static unsigned int fanon = 63; | ||
89 | static unsigned int fanoff = 58; | ||
90 | static unsigned int verbose; | ||
91 | static unsigned int fanstate = ACERHDF_FAN_AUTO; | ||
92 | static char force_bios[16]; | ||
93 | static unsigned int prev_interval; | ||
94 | struct thermal_zone_device *thz_dev; | ||
95 | struct thermal_cooling_device *cl_dev; | ||
96 | struct platform_device *acerhdf_dev; | ||
97 | |||
98 | module_param(kernelmode, uint, 0); | ||
99 | MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); | ||
100 | module_param(interval, uint, 0600); | ||
101 | MODULE_PARM_DESC(interval, "Polling interval of temperature check"); | ||
102 | module_param(fanon, uint, 0600); | ||
103 | MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature"); | ||
104 | module_param(fanoff, uint, 0600); | ||
105 | MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); | ||
106 | module_param(verbose, uint, 0600); | ||
107 | MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); | ||
108 | module_param_string(force_bios, force_bios, 16, 0); | ||
109 | MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); | ||
110 | |||
111 | /* BIOS settings */ | ||
112 | struct bios_settings_t { | ||
113 | const char *vendor; | ||
114 | const char *version; | ||
115 | unsigned char fanreg; | ||
116 | unsigned char tempreg; | ||
117 | unsigned char fancmd[2]; /* fan off and auto commands */ | ||
118 | }; | ||
119 | |||
120 | /* Register addresses and values for different BIOS versions */ | ||
121 | static const struct bios_settings_t bios_tbl[] = { | ||
122 | {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, | ||
123 | {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, | ||
124 | {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, | ||
125 | {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, | ||
126 | {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, | ||
127 | {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, | ||
128 | {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, | ||
129 | {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, | ||
130 | {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, | ||
131 | {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, | ||
132 | {"", "", 0, 0, {0, 0} } | ||
133 | }; | ||
134 | |||
135 | static const struct bios_settings_t *bios_cfg __read_mostly; | ||
136 | |||
137 | |||
138 | static int acerhdf_get_temp(int *temp) | ||
139 | { | ||
140 | u8 read_temp; | ||
141 | |||
142 | if (ec_read(bios_cfg->tempreg, &read_temp)) | ||
143 | return -EINVAL; | ||
144 | |||
145 | *temp = read_temp; | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int acerhdf_get_fanstate(int *state) | ||
151 | { | ||
152 | u8 fan; | ||
153 | bool tmp; | ||
154 | |||
155 | if (ec_read(bios_cfg->fanreg, &fan)) | ||
156 | return -EINVAL; | ||
157 | |||
158 | tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]); | ||
159 | *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static void acerhdf_change_fanstate(int state) | ||
165 | { | ||
166 | unsigned char cmd; | ||
167 | |||
168 | if (verbose) | ||
169 | pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ? | ||
170 | "OFF" : "ON"); | ||
171 | |||
172 | if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { | ||
173 | pr_err("invalid fan state %d requested, setting to auto!\n", | ||
174 | state); | ||
175 | state = ACERHDF_FAN_AUTO; | ||
176 | } | ||
177 | |||
178 | cmd = bios_cfg->fancmd[state]; | ||
179 | fanstate = state; | ||
180 | |||
181 | ec_write(bios_cfg->fanreg, cmd); | ||
182 | } | ||
183 | |||
184 | static void acerhdf_check_param(struct thermal_zone_device *thermal) | ||
185 | { | ||
186 | if (fanon > ACERHDF_MAX_FANON) { | ||
187 | pr_err("fanon temperature too high, set to %d\n", | ||
188 | ACERHDF_MAX_FANON); | ||
189 | fanon = ACERHDF_MAX_FANON; | ||
190 | } | ||
191 | |||
192 | if (kernelmode && prev_interval != interval) { | ||
193 | if (interval > ACERHDF_MAX_INTERVAL) { | ||
194 | pr_err("interval too high, set to %d\n", | ||
195 | ACERHDF_MAX_INTERVAL); | ||
196 | interval = ACERHDF_MAX_INTERVAL; | ||
197 | } | ||
198 | if (verbose) | ||
199 | pr_notice("interval changed to: %d\n", | ||
200 | interval); | ||
201 | thermal->polling_delay = interval*1000; | ||
202 | prev_interval = interval; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * This is the thermal zone callback which does the delayed polling of the fan | ||
208 | * state. We do check /sysfs-originating settings here in acerhdf_check_param() | ||
209 | * as late as the polling interval is since we can't do that in the respective | ||
210 | * accessors of the module parameters. | ||
211 | */ | ||
212 | static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, | ||
213 | unsigned long *t) | ||
214 | { | ||
215 | int temp, err = 0; | ||
216 | |||
217 | acerhdf_check_param(thermal); | ||
218 | |||
219 | err = acerhdf_get_temp(&temp); | ||
220 | if (err) | ||
221 | return err; | ||
222 | |||
223 | if (verbose) | ||
224 | pr_notice("temp %d\n", temp); | ||
225 | |||
226 | *t = temp; | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int acerhdf_bind(struct thermal_zone_device *thermal, | ||
231 | struct thermal_cooling_device *cdev) | ||
232 | { | ||
233 | /* if the cooling device is the one from acerhdf bind it */ | ||
234 | if (cdev != cl_dev) | ||
235 | return 0; | ||
236 | |||
237 | if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { | ||
238 | pr_err("error binding cooling dev\n"); | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int acerhdf_unbind(struct thermal_zone_device *thermal, | ||
245 | struct thermal_cooling_device *cdev) | ||
246 | { | ||
247 | if (cdev != cl_dev) | ||
248 | return 0; | ||
249 | |||
250 | if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { | ||
251 | pr_err("error unbinding cooling dev\n"); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static inline void acerhdf_revert_to_bios_mode(void) | ||
258 | { | ||
259 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
260 | kernelmode = 0; | ||
261 | if (thz_dev) | ||
262 | thz_dev->polling_delay = 0; | ||
263 | pr_notice("kernel mode fan control OFF\n"); | ||
264 | } | ||
265 | static inline void acerhdf_enable_kernelmode(void) | ||
266 | { | ||
267 | kernelmode = 1; | ||
268 | |||
269 | thz_dev->polling_delay = interval*1000; | ||
270 | thermal_zone_device_update(thz_dev); | ||
271 | pr_notice("kernel mode fan control ON\n"); | ||
272 | } | ||
273 | |||
274 | static int acerhdf_get_mode(struct thermal_zone_device *thermal, | ||
275 | enum thermal_device_mode *mode) | ||
276 | { | ||
277 | if (verbose) | ||
278 | pr_notice("kernel mode fan control %d\n", kernelmode); | ||
279 | |||
280 | *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED | ||
281 | : THERMAL_DEVICE_DISABLED; | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * set operation mode; | ||
288 | * enabled: the thermal layer of the kernel takes care about | ||
289 | * the temperature and the fan. | ||
290 | * disabled: the BIOS takes control of the fan. | ||
291 | */ | ||
292 | static int acerhdf_set_mode(struct thermal_zone_device *thermal, | ||
293 | enum thermal_device_mode mode) | ||
294 | { | ||
295 | if (mode == THERMAL_DEVICE_DISABLED && kernelmode) | ||
296 | acerhdf_revert_to_bios_mode(); | ||
297 | else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode) | ||
298 | acerhdf_enable_kernelmode(); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip, | ||
304 | enum thermal_trip_type *type) | ||
305 | { | ||
306 | if (trip == 0) | ||
307 | *type = THERMAL_TRIP_ACTIVE; | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip, | ||
313 | unsigned long *temp) | ||
314 | { | ||
315 | if (trip == 0) | ||
316 | *temp = fanon; | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, | ||
322 | unsigned long *temperature) | ||
323 | { | ||
324 | *temperature = ACERHDF_TEMP_CRIT; | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /* bind callback functions to thermalzone */ | ||
329 | struct thermal_zone_device_ops acerhdf_dev_ops = { | ||
330 | .bind = acerhdf_bind, | ||
331 | .unbind = acerhdf_unbind, | ||
332 | .get_temp = acerhdf_get_ec_temp, | ||
333 | .get_mode = acerhdf_get_mode, | ||
334 | .set_mode = acerhdf_set_mode, | ||
335 | .get_trip_type = acerhdf_get_trip_type, | ||
336 | .get_trip_temp = acerhdf_get_trip_temp, | ||
337 | .get_crit_temp = acerhdf_get_crit_temp, | ||
338 | }; | ||
339 | |||
340 | |||
341 | /* | ||
342 | * cooling device callback functions | ||
343 | * get maximal fan cooling state | ||
344 | */ | ||
345 | static int acerhdf_get_max_state(struct thermal_cooling_device *cdev, | ||
346 | unsigned long *state) | ||
347 | { | ||
348 | *state = 1; | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev, | ||
354 | unsigned long *state) | ||
355 | { | ||
356 | int err = 0, tmp; | ||
357 | |||
358 | err = acerhdf_get_fanstate(&tmp); | ||
359 | if (err) | ||
360 | return err; | ||
361 | |||
362 | *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0; | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /* change current fan state - is overwritten when running in kernel mode */ | ||
367 | static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev, | ||
368 | unsigned long state) | ||
369 | { | ||
370 | int cur_temp, cur_state, err = 0; | ||
371 | |||
372 | if (!kernelmode) | ||
373 | return 0; | ||
374 | |||
375 | err = acerhdf_get_temp(&cur_temp); | ||
376 | if (err) { | ||
377 | pr_err("error reading temperature, hand off control to BIOS\n"); | ||
378 | goto err_out; | ||
379 | } | ||
380 | |||
381 | err = acerhdf_get_fanstate(&cur_state); | ||
382 | if (err) { | ||
383 | pr_err("error reading fan state, hand off control to BIOS\n"); | ||
384 | goto err_out; | ||
385 | } | ||
386 | |||
387 | if (state == 0) { | ||
388 | /* turn fan off only if below fanoff temperature */ | ||
389 | if ((cur_state == ACERHDF_FAN_AUTO) && | ||
390 | (cur_temp < fanoff)) | ||
391 | acerhdf_change_fanstate(ACERHDF_FAN_OFF); | ||
392 | } else { | ||
393 | if (cur_state == ACERHDF_FAN_OFF) | ||
394 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
395 | } | ||
396 | return 0; | ||
397 | |||
398 | err_out: | ||
399 | acerhdf_revert_to_bios_mode(); | ||
400 | return -EINVAL; | ||
401 | } | ||
402 | |||
403 | /* bind fan callbacks to fan device */ | ||
404 | struct thermal_cooling_device_ops acerhdf_cooling_ops = { | ||
405 | .get_max_state = acerhdf_get_max_state, | ||
406 | .get_cur_state = acerhdf_get_cur_state, | ||
407 | .set_cur_state = acerhdf_set_cur_state, | ||
408 | }; | ||
409 | |||
410 | /* suspend / resume functionality */ | ||
411 | static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) | ||
412 | { | ||
413 | if (kernelmode) | ||
414 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
415 | |||
416 | if (verbose) | ||
417 | pr_notice("going suspend\n"); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int acerhdf_resume(struct platform_device *device) | ||
423 | { | ||
424 | if (verbose) | ||
425 | pr_notice("resuming\n"); | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int __devinit acerhdf_probe(struct platform_device *device) | ||
431 | { | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int acerhdf_remove(struct platform_device *device) | ||
436 | { | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | struct platform_driver acerhdf_drv = { | ||
441 | .driver = { | ||
442 | .name = "acerhdf", | ||
443 | .owner = THIS_MODULE, | ||
444 | }, | ||
445 | .probe = acerhdf_probe, | ||
446 | .remove = acerhdf_remove, | ||
447 | .suspend = acerhdf_suspend, | ||
448 | .resume = acerhdf_resume, | ||
449 | }; | ||
450 | |||
451 | |||
452 | /* check hardware */ | ||
453 | static int acerhdf_check_hardware(void) | ||
454 | { | ||
455 | char const *vendor, *version, *product; | ||
456 | int i; | ||
457 | |||
458 | /* get BIOS data */ | ||
459 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); | ||
460 | version = dmi_get_system_info(DMI_BIOS_VERSION); | ||
461 | product = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
462 | |||
463 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); | ||
464 | |||
465 | if (!force_bios[0]) { | ||
466 | if (strncmp(product, "AO", 2)) { | ||
467 | pr_err("no Aspire One hardware found\n"); | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | } else { | ||
471 | pr_info("forcing BIOS version: %s\n", version); | ||
472 | version = force_bios; | ||
473 | kernelmode = 0; | ||
474 | } | ||
475 | |||
476 | if (verbose) | ||
477 | pr_info("BIOS info: %s %s, product: %s\n", | ||
478 | vendor, version, product); | ||
479 | |||
480 | /* search BIOS version and vendor in BIOS settings table */ | ||
481 | for (i = 0; bios_tbl[i].version[0]; i++) { | ||
482 | if (!strcmp(bios_tbl[i].vendor, vendor) && | ||
483 | !strcmp(bios_tbl[i].version, version)) { | ||
484 | bios_cfg = &bios_tbl[i]; | ||
485 | break; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | if (!bios_cfg) { | ||
490 | pr_err("unknown (unsupported) BIOS version %s/%s, " | ||
491 | "please report, aborting!\n", vendor, version); | ||
492 | return -EINVAL; | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * if started with kernel mode off, prevent the kernel from switching | ||
497 | * off the fan | ||
498 | */ | ||
499 | if (!kernelmode) { | ||
500 | pr_notice("Fan control off, to enable do:\n"); | ||
501 | pr_notice("echo -n \"enabled\" > " | ||
502 | "/sys/class/thermal/thermal_zone0/mode\n"); | ||
503 | } | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int acerhdf_register_platform(void) | ||
509 | { | ||
510 | int err = 0; | ||
511 | |||
512 | err = platform_driver_register(&acerhdf_drv); | ||
513 | if (err) | ||
514 | return err; | ||
515 | |||
516 | acerhdf_dev = platform_device_alloc("acerhdf", -1); | ||
517 | platform_device_add(acerhdf_dev); | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | static void acerhdf_unregister_platform(void) | ||
523 | { | ||
524 | if (!acerhdf_dev) | ||
525 | return; | ||
526 | |||
527 | platform_device_del(acerhdf_dev); | ||
528 | platform_driver_unregister(&acerhdf_drv); | ||
529 | } | ||
530 | |||
531 | static int acerhdf_register_thermal(void) | ||
532 | { | ||
533 | cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, | ||
534 | &acerhdf_cooling_ops); | ||
535 | |||
536 | if (IS_ERR(cl_dev)) | ||
537 | return -EINVAL; | ||
538 | |||
539 | thz_dev = thermal_zone_device_register("acerhdf", 1, NULL, | ||
540 | &acerhdf_dev_ops, 0, 0, 0, | ||
541 | (kernelmode) ? interval*1000 : 0); | ||
542 | if (IS_ERR(thz_dev)) | ||
543 | return -EINVAL; | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static void acerhdf_unregister_thermal(void) | ||
549 | { | ||
550 | if (cl_dev) { | ||
551 | thermal_cooling_device_unregister(cl_dev); | ||
552 | cl_dev = NULL; | ||
553 | } | ||
554 | |||
555 | if (thz_dev) { | ||
556 | thermal_zone_device_unregister(thz_dev); | ||
557 | thz_dev = NULL; | ||
558 | } | ||
559 | } | ||
560 | |||
561 | static int __init acerhdf_init(void) | ||
562 | { | ||
563 | int err = 0; | ||
564 | |||
565 | err = acerhdf_check_hardware(); | ||
566 | if (err) | ||
567 | goto out_err; | ||
568 | |||
569 | err = acerhdf_register_platform(); | ||
570 | if (err) | ||
571 | goto err_unreg; | ||
572 | |||
573 | err = acerhdf_register_thermal(); | ||
574 | if (err) | ||
575 | goto err_unreg; | ||
576 | |||
577 | return 0; | ||
578 | |||
579 | err_unreg: | ||
580 | acerhdf_unregister_thermal(); | ||
581 | acerhdf_unregister_platform(); | ||
582 | |||
583 | out_err: | ||
584 | return -ENODEV; | ||
585 | } | ||
586 | |||
587 | static void __exit acerhdf_exit(void) | ||
588 | { | ||
589 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
590 | acerhdf_unregister_thermal(); | ||
591 | acerhdf_unregister_platform(); | ||
592 | } | ||
593 | |||
594 | MODULE_LICENSE("GPL"); | ||
595 | MODULE_AUTHOR("Peter Feuerer"); | ||
596 | MODULE_DESCRIPTION("Aspire One temperature and fan driver"); | ||
597 | MODULE_ALIAS("dmi:*:*Acer*:*:"); | ||
598 | MODULE_ALIAS("dmi:*:*Gateway*:*:"); | ||
599 | MODULE_ALIAS("dmi:*:*Packard Bell*:*:"); | ||
600 | |||
601 | module_init(acerhdf_init); | ||
602 | module_exit(acerhdf_exit); | ||
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index bfc1a8892a32..db657bbeec90 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -33,6 +33,8 @@ | |||
33 | * Sam Lin - GPS support | 33 | * Sam Lin - GPS support |
34 | */ | 34 | */ |
35 | 35 | ||
36 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
37 | |||
36 | #include <linux/kernel.h> | 38 | #include <linux/kernel.h> |
37 | #include <linux/module.h> | 39 | #include <linux/module.h> |
38 | #include <linux/init.h> | 40 | #include <linux/init.h> |
@@ -53,9 +55,10 @@ | |||
53 | #define ASUS_HOTK_NAME "Asus Laptop Support" | 55 | #define ASUS_HOTK_NAME "Asus Laptop Support" |
54 | #define ASUS_HOTK_CLASS "hotkey" | 56 | #define ASUS_HOTK_CLASS "hotkey" |
55 | #define ASUS_HOTK_DEVICE_NAME "Hotkey" | 57 | #define ASUS_HOTK_DEVICE_NAME "Hotkey" |
56 | #define ASUS_HOTK_FILE "asus-laptop" | 58 | #define ASUS_HOTK_FILE KBUILD_MODNAME |
57 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." | 59 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." |
58 | 60 | ||
61 | |||
59 | /* | 62 | /* |
60 | * Some events we use, same for all Asus | 63 | * Some events we use, same for all Asus |
61 | */ | 64 | */ |
@@ -207,13 +210,17 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids); | |||
207 | 210 | ||
208 | static int asus_hotk_add(struct acpi_device *device); | 211 | static int asus_hotk_add(struct acpi_device *device); |
209 | static int asus_hotk_remove(struct acpi_device *device, int type); | 212 | static int asus_hotk_remove(struct acpi_device *device, int type); |
213 | static void asus_hotk_notify(struct acpi_device *device, u32 event); | ||
214 | |||
210 | static struct acpi_driver asus_hotk_driver = { | 215 | static struct acpi_driver asus_hotk_driver = { |
211 | .name = ASUS_HOTK_NAME, | 216 | .name = ASUS_HOTK_NAME, |
212 | .class = ASUS_HOTK_CLASS, | 217 | .class = ASUS_HOTK_CLASS, |
213 | .ids = asus_device_ids, | 218 | .ids = asus_device_ids, |
219 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
214 | .ops = { | 220 | .ops = { |
215 | .add = asus_hotk_add, | 221 | .add = asus_hotk_add, |
216 | .remove = asus_hotk_remove, | 222 | .remove = asus_hotk_remove, |
223 | .notify = asus_hotk_notify, | ||
217 | }, | 224 | }, |
218 | }; | 225 | }; |
219 | 226 | ||
@@ -323,7 +330,7 @@ static int read_wireless_status(int mask) | |||
323 | 330 | ||
324 | rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); | 331 | rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); |
325 | if (ACPI_FAILURE(rv)) | 332 | if (ACPI_FAILURE(rv)) |
326 | printk(ASUS_WARNING "Error reading Wireless status\n"); | 333 | pr_warning("Error reading Wireless status\n"); |
327 | else | 334 | else |
328 | return (status & mask) ? 1 : 0; | 335 | return (status & mask) ? 1 : 0; |
329 | 336 | ||
@@ -337,7 +344,7 @@ static int read_gps_status(void) | |||
337 | 344 | ||
338 | rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status); | 345 | rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status); |
339 | if (ACPI_FAILURE(rv)) | 346 | if (ACPI_FAILURE(rv)) |
340 | printk(ASUS_WARNING "Error reading GPS status\n"); | 347 | pr_warning("Error reading GPS status\n"); |
341 | else | 348 | else |
342 | return status ? 1 : 0; | 349 | return status ? 1 : 0; |
343 | 350 | ||
@@ -377,7 +384,7 @@ static void write_status(acpi_handle handle, int out, int mask) | |||
377 | } | 384 | } |
378 | 385 | ||
379 | if (write_acpi_int(handle, NULL, out, NULL)) | 386 | if (write_acpi_int(handle, NULL, out, NULL)) |
380 | printk(ASUS_WARNING " write failed %x\n", mask); | 387 | pr_warning(" write failed %x\n", mask); |
381 | } | 388 | } |
382 | 389 | ||
383 | /* /sys/class/led handlers */ | 390 | /* /sys/class/led handlers */ |
@@ -420,7 +427,7 @@ static int set_lcd_state(int value) | |||
420 | NULL, NULL, NULL); | 427 | NULL, NULL, NULL); |
421 | 428 | ||
422 | if (ACPI_FAILURE(status)) | 429 | if (ACPI_FAILURE(status)) |
423 | printk(ASUS_WARNING "Error switching LCD\n"); | 430 | pr_warning("Error switching LCD\n"); |
424 | } | 431 | } |
425 | 432 | ||
426 | write_status(NULL, lcd, LCD_ON); | 433 | write_status(NULL, lcd, LCD_ON); |
@@ -444,7 +451,7 @@ static int read_brightness(struct backlight_device *bd) | |||
444 | 451 | ||
445 | rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); | 452 | rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); |
446 | if (ACPI_FAILURE(rv)) | 453 | if (ACPI_FAILURE(rv)) |
447 | printk(ASUS_WARNING "Error reading brightness\n"); | 454 | pr_warning("Error reading brightness\n"); |
448 | 455 | ||
449 | return value; | 456 | return value; |
450 | } | 457 | } |
@@ -457,7 +464,7 @@ static int set_brightness(struct backlight_device *bd, int value) | |||
457 | /* 0 <= value <= 15 */ | 464 | /* 0 <= value <= 15 */ |
458 | 465 | ||
459 | if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { | 466 | if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { |
460 | printk(ASUS_WARNING "Error changing brightness\n"); | 467 | pr_warning("Error changing brightness\n"); |
461 | ret = -EIO; | 468 | ret = -EIO; |
462 | } | 469 | } |
463 | 470 | ||
@@ -587,7 +594,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, | |||
587 | rv = parse_arg(buf, count, &value); | 594 | rv = parse_arg(buf, count, &value); |
588 | if (rv > 0) { | 595 | if (rv > 0) { |
589 | if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) | 596 | if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) |
590 | printk(ASUS_WARNING "LED display write failed\n"); | 597 | pr_warning("LED display write failed\n"); |
591 | else | 598 | else |
592 | hotk->ledd_status = (u32) value; | 599 | hotk->ledd_status = (u32) value; |
593 | } | 600 | } |
@@ -632,7 +639,7 @@ static void set_display(int value) | |||
632 | { | 639 | { |
633 | /* no sanity check needed for now */ | 640 | /* no sanity check needed for now */ |
634 | if (write_acpi_int(display_set_handle, NULL, value, NULL)) | 641 | if (write_acpi_int(display_set_handle, NULL, value, NULL)) |
635 | printk(ASUS_WARNING "Error setting display\n"); | 642 | pr_warning("Error setting display\n"); |
636 | return; | 643 | return; |
637 | } | 644 | } |
638 | 645 | ||
@@ -647,7 +654,7 @@ static int read_display(void) | |||
647 | rv = acpi_evaluate_integer(display_get_handle, NULL, | 654 | rv = acpi_evaluate_integer(display_get_handle, NULL, |
648 | NULL, &value); | 655 | NULL, &value); |
649 | if (ACPI_FAILURE(rv)) | 656 | if (ACPI_FAILURE(rv)) |
650 | printk(ASUS_WARNING "Error reading display status\n"); | 657 | pr_warning("Error reading display status\n"); |
651 | } | 658 | } |
652 | 659 | ||
653 | value &= 0x0F; /* needed for some models, shouldn't hurt others */ | 660 | value &= 0x0F; /* needed for some models, shouldn't hurt others */ |
@@ -689,7 +696,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr, | |||
689 | static void set_light_sens_switch(int value) | 696 | static void set_light_sens_switch(int value) |
690 | { | 697 | { |
691 | if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) | 698 | if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) |
692 | printk(ASUS_WARNING "Error setting light sensor switch\n"); | 699 | pr_warning("Error setting light sensor switch\n"); |
693 | hotk->light_switch = value; | 700 | hotk->light_switch = value; |
694 | } | 701 | } |
695 | 702 | ||
@@ -714,7 +721,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, | |||
714 | static void set_light_sens_level(int value) | 721 | static void set_light_sens_level(int value) |
715 | { | 722 | { |
716 | if (write_acpi_int(ls_level_handle, NULL, value, NULL)) | 723 | if (write_acpi_int(ls_level_handle, NULL, value, NULL)) |
717 | printk(ASUS_WARNING "Error setting light sensor level\n"); | 724 | pr_warning("Error setting light sensor level\n"); |
718 | hotk->light_level = value; | 725 | hotk->light_level = value; |
719 | } | 726 | } |
720 | 727 | ||
@@ -812,7 +819,7 @@ static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) | |||
812 | return -EINVAL; | 819 | return -EINVAL; |
813 | } | 820 | } |
814 | 821 | ||
815 | static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | 822 | static void asus_hotk_notify(struct acpi_device *device, u32 event) |
816 | { | 823 | { |
817 | static struct key_entry *key; | 824 | static struct key_entry *key; |
818 | u16 count; | 825 | u16 count; |
@@ -975,11 +982,11 @@ static int asus_hotk_get_info(void) | |||
975 | */ | 982 | */ |
976 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); | 983 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); |
977 | if (ACPI_FAILURE(status)) | 984 | if (ACPI_FAILURE(status)) |
978 | printk(ASUS_WARNING "Couldn't get the DSDT table header\n"); | 985 | pr_warning("Couldn't get the DSDT table header\n"); |
979 | 986 | ||
980 | /* We have to write 0 on init this far for all ASUS models */ | 987 | /* We have to write 0 on init this far for all ASUS models */ |
981 | if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { | 988 | if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { |
982 | printk(ASUS_ERR "Hotkey initialization failed\n"); | 989 | pr_err("Hotkey initialization failed\n"); |
983 | return -ENODEV; | 990 | return -ENODEV; |
984 | } | 991 | } |
985 | 992 | ||
@@ -987,9 +994,9 @@ static int asus_hotk_get_info(void) | |||
987 | status = | 994 | status = |
988 | acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); | 995 | acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); |
989 | if (ACPI_FAILURE(status)) | 996 | if (ACPI_FAILURE(status)) |
990 | printk(ASUS_WARNING "Error calling BSTS\n"); | 997 | pr_warning("Error calling BSTS\n"); |
991 | else if (bsts_result) | 998 | else if (bsts_result) |
992 | printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n", | 999 | pr_notice("BSTS called, 0x%02x returned\n", |
993 | (uint) bsts_result); | 1000 | (uint) bsts_result); |
994 | 1001 | ||
995 | /* This too ... */ | 1002 | /* This too ... */ |
@@ -1020,7 +1027,7 @@ static int asus_hotk_get_info(void) | |||
1020 | return -ENOMEM; | 1027 | return -ENOMEM; |
1021 | 1028 | ||
1022 | if (*string) | 1029 | if (*string) |
1023 | printk(ASUS_NOTICE " %s model detected\n", string); | 1030 | pr_notice(" %s model detected\n", string); |
1024 | 1031 | ||
1025 | ASUS_HANDLE_INIT(mled_set); | 1032 | ASUS_HANDLE_INIT(mled_set); |
1026 | ASUS_HANDLE_INIT(tled_set); | 1033 | ASUS_HANDLE_INIT(tled_set); |
@@ -1077,7 +1084,7 @@ static int asus_input_init(void) | |||
1077 | 1084 | ||
1078 | hotk->inputdev = input_allocate_device(); | 1085 | hotk->inputdev = input_allocate_device(); |
1079 | if (!hotk->inputdev) { | 1086 | if (!hotk->inputdev) { |
1080 | printk(ASUS_INFO "Unable to allocate input device\n"); | 1087 | pr_info("Unable to allocate input device\n"); |
1081 | return 0; | 1088 | return 0; |
1082 | } | 1089 | } |
1083 | hotk->inputdev->name = "Asus Laptop extra buttons"; | 1090 | hotk->inputdev->name = "Asus Laptop extra buttons"; |
@@ -1096,7 +1103,7 @@ static int asus_input_init(void) | |||
1096 | } | 1103 | } |
1097 | result = input_register_device(hotk->inputdev); | 1104 | result = input_register_device(hotk->inputdev); |
1098 | if (result) { | 1105 | if (result) { |
1099 | printk(ASUS_INFO "Unable to register input device\n"); | 1106 | pr_info("Unable to register input device\n"); |
1100 | input_free_device(hotk->inputdev); | 1107 | input_free_device(hotk->inputdev); |
1101 | } | 1108 | } |
1102 | return result; | 1109 | return result; |
@@ -1113,7 +1120,7 @@ static int asus_hotk_check(void) | |||
1113 | if (hotk->device->status.present) { | 1120 | if (hotk->device->status.present) { |
1114 | result = asus_hotk_get_info(); | 1121 | result = asus_hotk_get_info(); |
1115 | } else { | 1122 | } else { |
1116 | printk(ASUS_ERR "Hotkey device not present, aborting\n"); | 1123 | pr_err("Hotkey device not present, aborting\n"); |
1117 | return -EINVAL; | 1124 | return -EINVAL; |
1118 | } | 1125 | } |
1119 | 1126 | ||
@@ -1124,13 +1131,12 @@ static int asus_hotk_found; | |||
1124 | 1131 | ||
1125 | static int asus_hotk_add(struct acpi_device *device) | 1132 | static int asus_hotk_add(struct acpi_device *device) |
1126 | { | 1133 | { |
1127 | acpi_status status = AE_OK; | ||
1128 | int result; | 1134 | int result; |
1129 | 1135 | ||
1130 | if (!device) | 1136 | if (!device) |
1131 | return -EINVAL; | 1137 | return -EINVAL; |
1132 | 1138 | ||
1133 | printk(ASUS_NOTICE "Asus Laptop Support version %s\n", | 1139 | pr_notice("Asus Laptop Support version %s\n", |
1134 | ASUS_LAPTOP_VERSION); | 1140 | ASUS_LAPTOP_VERSION); |
1135 | 1141 | ||
1136 | hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); | 1142 | hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); |
@@ -1149,15 +1155,6 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1149 | 1155 | ||
1150 | asus_hotk_add_fs(); | 1156 | asus_hotk_add_fs(); |
1151 | 1157 | ||
1152 | /* | ||
1153 | * We install the handler, it will receive the hotk in parameter, so, we | ||
1154 | * could add other data to the hotk struct | ||
1155 | */ | ||
1156 | status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY, | ||
1157 | asus_hotk_notify, hotk); | ||
1158 | if (ACPI_FAILURE(status)) | ||
1159 | printk(ASUS_ERR "Error installing notify handler\n"); | ||
1160 | |||
1161 | asus_hotk_found = 1; | 1158 | asus_hotk_found = 1; |
1162 | 1159 | ||
1163 | /* WLED and BLED are on by default */ | 1160 | /* WLED and BLED are on by default */ |
@@ -1198,16 +1195,9 @@ end: | |||
1198 | 1195 | ||
1199 | static int asus_hotk_remove(struct acpi_device *device, int type) | 1196 | static int asus_hotk_remove(struct acpi_device *device, int type) |
1200 | { | 1197 | { |
1201 | acpi_status status = 0; | ||
1202 | |||
1203 | if (!device || !acpi_driver_data(device)) | 1198 | if (!device || !acpi_driver_data(device)) |
1204 | return -EINVAL; | 1199 | return -EINVAL; |
1205 | 1200 | ||
1206 | status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY, | ||
1207 | asus_hotk_notify); | ||
1208 | if (ACPI_FAILURE(status)) | ||
1209 | printk(ASUS_ERR "Error removing notify handler\n"); | ||
1210 | |||
1211 | kfree(hotk->name); | 1201 | kfree(hotk->name); |
1212 | kfree(hotk); | 1202 | kfree(hotk); |
1213 | 1203 | ||
@@ -1260,8 +1250,7 @@ static int asus_backlight_init(struct device *dev) | |||
1260 | bd = backlight_device_register(ASUS_HOTK_FILE, dev, | 1250 | bd = backlight_device_register(ASUS_HOTK_FILE, dev, |
1261 | NULL, &asusbl_ops); | 1251 | NULL, &asusbl_ops); |
1262 | if (IS_ERR(bd)) { | 1252 | if (IS_ERR(bd)) { |
1263 | printk(ASUS_ERR | 1253 | pr_err("Could not register asus backlight device\n"); |
1264 | "Could not register asus backlight device\n"); | ||
1265 | asus_backlight_device = NULL; | 1254 | asus_backlight_device = NULL; |
1266 | return PTR_ERR(bd); | 1255 | return PTR_ERR(bd); |
1267 | } | 1256 | } |
@@ -1334,7 +1323,6 @@ out: | |||
1334 | 1323 | ||
1335 | static int __init asus_laptop_init(void) | 1324 | static int __init asus_laptop_init(void) |
1336 | { | 1325 | { |
1337 | struct device *dev; | ||
1338 | int result; | 1326 | int result; |
1339 | 1327 | ||
1340 | if (acpi_disabled) | 1328 | if (acpi_disabled) |
@@ -1356,24 +1344,10 @@ static int __init asus_laptop_init(void) | |||
1356 | return -ENODEV; | 1344 | return -ENODEV; |
1357 | } | 1345 | } |
1358 | 1346 | ||
1359 | dev = acpi_get_physical_device(hotk->device->handle); | ||
1360 | |||
1361 | if (!acpi_video_backlight_support()) { | ||
1362 | result = asus_backlight_init(dev); | ||
1363 | if (result) | ||
1364 | goto fail_backlight; | ||
1365 | } else | ||
1366 | printk(ASUS_INFO "Brightness ignored, must be controlled by " | ||
1367 | "ACPI video driver\n"); | ||
1368 | |||
1369 | result = asus_input_init(); | 1347 | result = asus_input_init(); |
1370 | if (result) | 1348 | if (result) |
1371 | goto fail_input; | 1349 | goto fail_input; |
1372 | 1350 | ||
1373 | result = asus_led_init(dev); | ||
1374 | if (result) | ||
1375 | goto fail_led; | ||
1376 | |||
1377 | /* Register platform stuff */ | 1351 | /* Register platform stuff */ |
1378 | result = platform_driver_register(&asuspf_driver); | 1352 | result = platform_driver_register(&asuspf_driver); |
1379 | if (result) | 1353 | if (result) |
@@ -1394,8 +1368,27 @@ static int __init asus_laptop_init(void) | |||
1394 | if (result) | 1368 | if (result) |
1395 | goto fail_sysfs; | 1369 | goto fail_sysfs; |
1396 | 1370 | ||
1371 | result = asus_led_init(&asuspf_device->dev); | ||
1372 | if (result) | ||
1373 | goto fail_led; | ||
1374 | |||
1375 | if (!acpi_video_backlight_support()) { | ||
1376 | result = asus_backlight_init(&asuspf_device->dev); | ||
1377 | if (result) | ||
1378 | goto fail_backlight; | ||
1379 | } else | ||
1380 | pr_info("Brightness ignored, must be controlled by " | ||
1381 | "ACPI video driver\n"); | ||
1382 | |||
1397 | return 0; | 1383 | return 0; |
1398 | 1384 | ||
1385 | fail_backlight: | ||
1386 | asus_led_exit(); | ||
1387 | |||
1388 | fail_led: | ||
1389 | sysfs_remove_group(&asuspf_device->dev.kobj, | ||
1390 | &asuspf_attribute_group); | ||
1391 | |||
1399 | fail_sysfs: | 1392 | fail_sysfs: |
1400 | platform_device_del(asuspf_device); | 1393 | platform_device_del(asuspf_device); |
1401 | 1394 | ||
@@ -1406,15 +1399,9 @@ fail_platform_device1: | |||
1406 | platform_driver_unregister(&asuspf_driver); | 1399 | platform_driver_unregister(&asuspf_driver); |
1407 | 1400 | ||
1408 | fail_platform_driver: | 1401 | fail_platform_driver: |
1409 | asus_led_exit(); | ||
1410 | |||
1411 | fail_led: | ||
1412 | asus_input_exit(); | 1402 | asus_input_exit(); |
1413 | 1403 | ||
1414 | fail_input: | 1404 | fail_input: |
1415 | asus_backlight_exit(); | ||
1416 | |||
1417 | fail_backlight: | ||
1418 | 1405 | ||
1419 | return result; | 1406 | return result; |
1420 | } | 1407 | } |
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index ba1f7497e4b9..ddf5240ade8c 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c | |||
@@ -455,6 +455,8 @@ static struct asus_hotk *hotk; | |||
455 | */ | 455 | */ |
456 | static int asus_hotk_add(struct acpi_device *device); | 456 | static int asus_hotk_add(struct acpi_device *device); |
457 | static int asus_hotk_remove(struct acpi_device *device, int type); | 457 | static int asus_hotk_remove(struct acpi_device *device, int type); |
458 | static void asus_hotk_notify(struct acpi_device *device, u32 event); | ||
459 | |||
458 | static const struct acpi_device_id asus_device_ids[] = { | 460 | static const struct acpi_device_id asus_device_ids[] = { |
459 | {"ATK0100", 0}, | 461 | {"ATK0100", 0}, |
460 | {"", 0}, | 462 | {"", 0}, |
@@ -465,9 +467,11 @@ static struct acpi_driver asus_hotk_driver = { | |||
465 | .name = "asus_acpi", | 467 | .name = "asus_acpi", |
466 | .class = ACPI_HOTK_CLASS, | 468 | .class = ACPI_HOTK_CLASS, |
467 | .ids = asus_device_ids, | 469 | .ids = asus_device_ids, |
470 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
468 | .ops = { | 471 | .ops = { |
469 | .add = asus_hotk_add, | 472 | .add = asus_hotk_add, |
470 | .remove = asus_hotk_remove, | 473 | .remove = asus_hotk_remove, |
474 | .notify = asus_hotk_notify, | ||
471 | }, | 475 | }, |
472 | }; | 476 | }; |
473 | 477 | ||
@@ -1101,12 +1105,20 @@ static int asus_hotk_remove_fs(struct acpi_device *device) | |||
1101 | return 0; | 1105 | return 0; |
1102 | } | 1106 | } |
1103 | 1107 | ||
1104 | static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | 1108 | static void asus_hotk_notify(struct acpi_device *device, u32 event) |
1105 | { | 1109 | { |
1106 | /* TODO Find a better way to handle events count. */ | 1110 | /* TODO Find a better way to handle events count. */ |
1107 | if (!hotk) | 1111 | if (!hotk) |
1108 | return; | 1112 | return; |
1109 | 1113 | ||
1114 | /* | ||
1115 | * The BIOS *should* be sending us device events, but apparently | ||
1116 | * Asus uses system events instead, so just ignore any device | ||
1117 | * events we get. | ||
1118 | */ | ||
1119 | if (event > ACPI_MAX_SYS_NOTIFY) | ||
1120 | return; | ||
1121 | |||
1110 | if ((event & ~((u32) BR_UP)) < 16) | 1122 | if ((event & ~((u32) BR_UP)) < 16) |
1111 | hotk->brightness = (event & ~((u32) BR_UP)); | 1123 | hotk->brightness = (event & ~((u32) BR_UP)); |
1112 | else if ((event & ~((u32) BR_DOWN)) < 16) | 1124 | else if ((event & ~((u32) BR_DOWN)) < 16) |
@@ -1346,15 +1358,6 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1346 | if (result) | 1358 | if (result) |
1347 | goto end; | 1359 | goto end; |
1348 | 1360 | ||
1349 | /* | ||
1350 | * We install the handler, it will receive the hotk in parameter, so, we | ||
1351 | * could add other data to the hotk struct | ||
1352 | */ | ||
1353 | status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, | ||
1354 | asus_hotk_notify, hotk); | ||
1355 | if (ACPI_FAILURE(status)) | ||
1356 | printk(KERN_ERR " Error installing notify handler\n"); | ||
1357 | |||
1358 | /* For laptops without GPLV: init the hotk->brightness value */ | 1361 | /* For laptops without GPLV: init the hotk->brightness value */ |
1359 | if ((!hotk->methods->brightness_get) | 1362 | if ((!hotk->methods->brightness_get) |
1360 | && (!hotk->methods->brightness_status) | 1363 | && (!hotk->methods->brightness_status) |
@@ -1389,16 +1392,9 @@ end: | |||
1389 | 1392 | ||
1390 | static int asus_hotk_remove(struct acpi_device *device, int type) | 1393 | static int asus_hotk_remove(struct acpi_device *device, int type) |
1391 | { | 1394 | { |
1392 | acpi_status status = 0; | ||
1393 | |||
1394 | if (!device || !acpi_driver_data(device)) | 1395 | if (!device || !acpi_driver_data(device)) |
1395 | return -EINVAL; | 1396 | return -EINVAL; |
1396 | 1397 | ||
1397 | status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, | ||
1398 | asus_hotk_notify); | ||
1399 | if (ACPI_FAILURE(status)) | ||
1400 | printk(KERN_ERR "Asus ACPI: Error removing notify handler\n"); | ||
1401 | |||
1402 | asus_hotk_remove_fs(device); | 1398 | asus_hotk_remove_fs(device); |
1403 | 1399 | ||
1404 | kfree(hotk); | 1400 | kfree(hotk); |
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 2fab94162147..0f900cc9fa7a 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -46,10 +46,53 @@ struct key_entry { | |||
46 | u16 keycode; | 46 | u16 keycode; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | enum { KE_KEY, KE_SW, KE_END }; | 49 | enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; |
50 | |||
51 | /* | ||
52 | * Certain keys are flagged as KE_IGNORE. All of these are either | ||
53 | * notifications (rather than requests for change) or are also sent | ||
54 | * via the keyboard controller so should not be sent again. | ||
55 | */ | ||
50 | 56 | ||
51 | static struct key_entry dell_wmi_keymap[] = { | 57 | static struct key_entry dell_wmi_keymap[] = { |
52 | {KE_KEY, 0xe045, KEY_PROG1}, | 58 | {KE_KEY, 0xe045, KEY_PROG1}, |
59 | {KE_KEY, 0xe009, KEY_EJECTCD}, | ||
60 | |||
61 | /* These also contain the brightness level at offset 6 */ | ||
62 | {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, | ||
63 | {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, | ||
64 | |||
65 | /* Battery health status button */ | ||
66 | {KE_KEY, 0xe007, KEY_BATTERY}, | ||
67 | |||
68 | /* This is actually for all radios. Although physically a | ||
69 | * switch, the notification does not provide an indication of | ||
70 | * state and so it should be reported as a key */ | ||
71 | {KE_KEY, 0xe008, KEY_WLAN}, | ||
72 | |||
73 | /* The next device is at offset 6, the active devices are at | ||
74 | offset 8 and the attached devices at offset 10 */ | ||
75 | {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE}, | ||
76 | |||
77 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, | ||
78 | |||
79 | /* BIOS error detected */ | ||
80 | {KE_IGNORE, 0xe00d, KEY_RESERVED}, | ||
81 | |||
82 | /* Wifi Catcher */ | ||
83 | {KE_KEY, 0xe011, KEY_PROG2}, | ||
84 | |||
85 | /* Ambient light sensor toggle */ | ||
86 | {KE_IGNORE, 0xe013, KEY_RESERVED}, | ||
87 | |||
88 | {KE_IGNORE, 0xe020, KEY_MUTE}, | ||
89 | {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, | ||
90 | {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, | ||
91 | {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, | ||
92 | {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, | ||
93 | {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, | ||
94 | {KE_IGNORE, 0xe045, KEY_NUMLOCK}, | ||
95 | {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, | ||
53 | {KE_END, 0} | 96 | {KE_END, 0} |
54 | }; | 97 | }; |
55 | 98 | ||
@@ -122,15 +165,20 @@ static void dell_wmi_notify(u32 value, void *context) | |||
122 | 165 | ||
123 | if (obj && obj->type == ACPI_TYPE_BUFFER) { | 166 | if (obj && obj->type == ACPI_TYPE_BUFFER) { |
124 | int *buffer = (int *)obj->buffer.pointer; | 167 | int *buffer = (int *)obj->buffer.pointer; |
125 | key = dell_wmi_get_entry_by_scancode(buffer[1]); | 168 | /* |
169 | * The upper bytes of the event may contain | ||
170 | * additional information, so mask them off for the | ||
171 | * scancode lookup | ||
172 | */ | ||
173 | key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF); | ||
126 | if (key) { | 174 | if (key) { |
127 | input_report_key(dell_wmi_input_dev, key->keycode, 1); | 175 | input_report_key(dell_wmi_input_dev, key->keycode, 1); |
128 | input_sync(dell_wmi_input_dev); | 176 | input_sync(dell_wmi_input_dev); |
129 | input_report_key(dell_wmi_input_dev, key->keycode, 0); | 177 | input_report_key(dell_wmi_input_dev, key->keycode, 0); |
130 | input_sync(dell_wmi_input_dev); | 178 | input_sync(dell_wmi_input_dev); |
131 | } else | 179 | } else if (buffer[1] & 0xFFFF) |
132 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | 180 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", |
133 | buffer[1]); | 181 | buffer[1] & 0xFFFF); |
134 | } | 182 | } |
135 | } | 183 | } |
136 | 184 | ||
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 8153b3e59189..4207b26ff990 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -62,7 +62,10 @@ enum { | |||
62 | DISABLE_ASL_GPS = 0x0020, | 62 | DISABLE_ASL_GPS = 0x0020, |
63 | DISABLE_ASL_DISPLAYSWITCH = 0x0040, | 63 | DISABLE_ASL_DISPLAYSWITCH = 0x0040, |
64 | DISABLE_ASL_MODEM = 0x0080, | 64 | DISABLE_ASL_MODEM = 0x0080, |
65 | DISABLE_ASL_CARDREADER = 0x0100 | 65 | DISABLE_ASL_CARDREADER = 0x0100, |
66 | DISABLE_ASL_3G = 0x0200, | ||
67 | DISABLE_ASL_WIMAX = 0x0400, | ||
68 | DISABLE_ASL_HWCF = 0x0800 | ||
66 | }; | 69 | }; |
67 | 70 | ||
68 | enum { | 71 | enum { |
@@ -87,7 +90,13 @@ enum { | |||
87 | CM_ASL_USBPORT3, | 90 | CM_ASL_USBPORT3, |
88 | CM_ASL_MODEM, | 91 | CM_ASL_MODEM, |
89 | CM_ASL_CARDREADER, | 92 | CM_ASL_CARDREADER, |
90 | CM_ASL_LID | 93 | CM_ASL_3G, |
94 | CM_ASL_WIMAX, | ||
95 | CM_ASL_HWCF, | ||
96 | CM_ASL_LID, | ||
97 | CM_ASL_TYPE, | ||
98 | CM_ASL_PANELPOWER, /*P901*/ | ||
99 | CM_ASL_TPD | ||
91 | }; | 100 | }; |
92 | 101 | ||
93 | static const char *cm_getv[] = { | 102 | static const char *cm_getv[] = { |
@@ -96,7 +105,8 @@ static const char *cm_getv[] = { | |||
96 | NULL, "PBLG", NULL, NULL, | 105 | NULL, "PBLG", NULL, NULL, |
97 | "CFVG", NULL, NULL, NULL, | 106 | "CFVG", NULL, NULL, NULL, |
98 | "USBG", NULL, NULL, "MODG", | 107 | "USBG", NULL, NULL, "MODG", |
99 | "CRDG", "LIDG" | 108 | "CRDG", "M3GG", "WIMG", "HWCF", |
109 | "LIDG", "TYPE", "PBPG", "TPDG" | ||
100 | }; | 110 | }; |
101 | 111 | ||
102 | static const char *cm_setv[] = { | 112 | static const char *cm_setv[] = { |
@@ -105,7 +115,8 @@ static const char *cm_setv[] = { | |||
105 | "SDSP", "PBLS", "HDPS", NULL, | 115 | "SDSP", "PBLS", "HDPS", NULL, |
106 | "CFVS", NULL, NULL, NULL, | 116 | "CFVS", NULL, NULL, NULL, |
107 | "USBG", NULL, NULL, "MODS", | 117 | "USBG", NULL, NULL, "MODS", |
108 | "CRDS", NULL | 118 | "CRDS", "M3GS", "WIMS", NULL, |
119 | NULL, NULL, "PBPS", "TPDS" | ||
109 | }; | 120 | }; |
110 | 121 | ||
111 | #define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." | 122 | #define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." |
@@ -181,6 +192,7 @@ static struct key_entry eeepc_keymap[] = { | |||
181 | static int eeepc_hotk_add(struct acpi_device *device); | 192 | static int eeepc_hotk_add(struct acpi_device *device); |
182 | static int eeepc_hotk_remove(struct acpi_device *device, int type); | 193 | static int eeepc_hotk_remove(struct acpi_device *device, int type); |
183 | static int eeepc_hotk_resume(struct acpi_device *device); | 194 | static int eeepc_hotk_resume(struct acpi_device *device); |
195 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event); | ||
184 | 196 | ||
185 | static const struct acpi_device_id eeepc_device_ids[] = { | 197 | static const struct acpi_device_id eeepc_device_ids[] = { |
186 | {EEEPC_HOTK_HID, 0}, | 198 | {EEEPC_HOTK_HID, 0}, |
@@ -192,10 +204,12 @@ static struct acpi_driver eeepc_hotk_driver = { | |||
192 | .name = EEEPC_HOTK_NAME, | 204 | .name = EEEPC_HOTK_NAME, |
193 | .class = EEEPC_HOTK_CLASS, | 205 | .class = EEEPC_HOTK_CLASS, |
194 | .ids = eeepc_device_ids, | 206 | .ids = eeepc_device_ids, |
207 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
195 | .ops = { | 208 | .ops = { |
196 | .add = eeepc_hotk_add, | 209 | .add = eeepc_hotk_add, |
197 | .remove = eeepc_hotk_remove, | 210 | .remove = eeepc_hotk_remove, |
198 | .resume = eeepc_hotk_resume, | 211 | .resume = eeepc_hotk_resume, |
212 | .notify = eeepc_hotk_notify, | ||
199 | }, | 213 | }, |
200 | }; | 214 | }; |
201 | 215 | ||
@@ -318,6 +332,15 @@ static const struct rfkill_ops eeepc_rfkill_ops = { | |||
318 | .set_block = eeepc_rfkill_set, | 332 | .set_block = eeepc_rfkill_set, |
319 | }; | 333 | }; |
320 | 334 | ||
335 | static void __init eeepc_enable_camera(void) | ||
336 | { | ||
337 | /* | ||
338 | * If the following call to set_acpi() fails, it's because there's no | ||
339 | * camera so we can ignore the error. | ||
340 | */ | ||
341 | set_acpi(CM_ASL_CAMERA, 1); | ||
342 | } | ||
343 | |||
321 | /* | 344 | /* |
322 | * Sys helpers | 345 | * Sys helpers |
323 | */ | 346 | */ |
@@ -369,13 +392,88 @@ static ssize_t show_sys_acpi(int cm, char *buf) | |||
369 | EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); | 392 | EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); |
370 | EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); | 393 | EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); |
371 | EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); | 394 | EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); |
372 | EEEPC_CREATE_DEVICE_ATTR(cpufv, CM_ASL_CPUFV); | 395 | |
396 | struct eeepc_cpufv { | ||
397 | int num; | ||
398 | int cur; | ||
399 | }; | ||
400 | |||
401 | static int get_cpufv(struct eeepc_cpufv *c) | ||
402 | { | ||
403 | c->cur = get_acpi(CM_ASL_CPUFV); | ||
404 | c->num = (c->cur >> 8) & 0xff; | ||
405 | c->cur &= 0xff; | ||
406 | if (c->cur < 0 || c->num <= 0 || c->num > 12) | ||
407 | return -ENODEV; | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static ssize_t show_available_cpufv(struct device *dev, | ||
412 | struct device_attribute *attr, | ||
413 | char *buf) | ||
414 | { | ||
415 | struct eeepc_cpufv c; | ||
416 | int i; | ||
417 | ssize_t len = 0; | ||
418 | |||
419 | if (get_cpufv(&c)) | ||
420 | return -ENODEV; | ||
421 | for (i = 0; i < c.num; i++) | ||
422 | len += sprintf(buf + len, "%d ", i); | ||
423 | len += sprintf(buf + len, "\n"); | ||
424 | return len; | ||
425 | } | ||
426 | |||
427 | static ssize_t show_cpufv(struct device *dev, | ||
428 | struct device_attribute *attr, | ||
429 | char *buf) | ||
430 | { | ||
431 | struct eeepc_cpufv c; | ||
432 | |||
433 | if (get_cpufv(&c)) | ||
434 | return -ENODEV; | ||
435 | return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); | ||
436 | } | ||
437 | |||
438 | static ssize_t store_cpufv(struct device *dev, | ||
439 | struct device_attribute *attr, | ||
440 | const char *buf, size_t count) | ||
441 | { | ||
442 | struct eeepc_cpufv c; | ||
443 | int rv, value; | ||
444 | |||
445 | if (get_cpufv(&c)) | ||
446 | return -ENODEV; | ||
447 | rv = parse_arg(buf, count, &value); | ||
448 | if (rv < 0) | ||
449 | return rv; | ||
450 | if (!rv || value < 0 || value >= c.num) | ||
451 | return -EINVAL; | ||
452 | set_acpi(CM_ASL_CPUFV, value); | ||
453 | return rv; | ||
454 | } | ||
455 | |||
456 | static struct device_attribute dev_attr_cpufv = { | ||
457 | .attr = { | ||
458 | .name = "cpufv", | ||
459 | .mode = 0644 }, | ||
460 | .show = show_cpufv, | ||
461 | .store = store_cpufv | ||
462 | }; | ||
463 | |||
464 | static struct device_attribute dev_attr_available_cpufv = { | ||
465 | .attr = { | ||
466 | .name = "available_cpufv", | ||
467 | .mode = 0444 }, | ||
468 | .show = show_available_cpufv | ||
469 | }; | ||
373 | 470 | ||
374 | static struct attribute *platform_attributes[] = { | 471 | static struct attribute *platform_attributes[] = { |
375 | &dev_attr_camera.attr, | 472 | &dev_attr_camera.attr, |
376 | &dev_attr_cardr.attr, | 473 | &dev_attr_cardr.attr, |
377 | &dev_attr_disp.attr, | 474 | &dev_attr_disp.attr, |
378 | &dev_attr_cpufv.attr, | 475 | &dev_attr_cpufv.attr, |
476 | &dev_attr_available_cpufv.attr, | ||
379 | NULL | 477 | NULL |
380 | }; | 478 | }; |
381 | 479 | ||
@@ -558,7 +656,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |||
558 | eeepc_rfkill_hotplug(); | 656 | eeepc_rfkill_hotplug(); |
559 | } | 657 | } |
560 | 658 | ||
561 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | 659 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event) |
562 | { | 660 | { |
563 | static struct key_entry *key; | 661 | static struct key_entry *key; |
564 | u16 count; | 662 | u16 count; |
@@ -566,6 +664,8 @@ static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | |||
566 | 664 | ||
567 | if (!ehotk) | 665 | if (!ehotk) |
568 | return; | 666 | return; |
667 | if (event > ACPI_MAX_SYS_NOTIFY) | ||
668 | return; | ||
569 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) | 669 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) |
570 | brn = notify_brn(); | 670 | brn = notify_brn(); |
571 | count = ehotk->event_count[event % 128]++; | 671 | count = ehotk->event_count[event % 128]++; |
@@ -646,7 +746,6 @@ static void eeepc_unregister_rfkill_notifier(char *node) | |||
646 | 746 | ||
647 | static int eeepc_hotk_add(struct acpi_device *device) | 747 | static int eeepc_hotk_add(struct acpi_device *device) |
648 | { | 748 | { |
649 | acpi_status status = AE_OK; | ||
650 | int result; | 749 | int result; |
651 | 750 | ||
652 | if (!device) | 751 | if (!device) |
@@ -664,10 +763,6 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
664 | result = eeepc_hotk_check(); | 763 | result = eeepc_hotk_check(); |
665 | if (result) | 764 | if (result) |
666 | goto ehotk_fail; | 765 | goto ehotk_fail; |
667 | status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, | ||
668 | eeepc_hotk_notify, ehotk); | ||
669 | if (ACPI_FAILURE(status)) | ||
670 | printk(EEEPC_ERR "Error installing notify handler\n"); | ||
671 | 766 | ||
672 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | 767 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); |
673 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | 768 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); |
@@ -725,14 +820,8 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
725 | 820 | ||
726 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | 821 | static int eeepc_hotk_remove(struct acpi_device *device, int type) |
727 | { | 822 | { |
728 | acpi_status status = 0; | ||
729 | |||
730 | if (!device || !acpi_driver_data(device)) | 823 | if (!device || !acpi_driver_data(device)) |
731 | return -EINVAL; | 824 | return -EINVAL; |
732 | status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, | ||
733 | eeepc_hotk_notify); | ||
734 | if (ACPI_FAILURE(status)) | ||
735 | printk(EEEPC_ERR "Error removing notify handler\n"); | ||
736 | 825 | ||
737 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | 826 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); |
738 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | 827 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); |
@@ -989,6 +1078,9 @@ static int __init eeepc_laptop_init(void) | |||
989 | result = eeepc_hwmon_init(dev); | 1078 | result = eeepc_hwmon_init(dev); |
990 | if (result) | 1079 | if (result) |
991 | goto fail_hwmon; | 1080 | goto fail_hwmon; |
1081 | |||
1082 | eeepc_enable_camera(); | ||
1083 | |||
992 | /* Register platform stuff */ | 1084 | /* Register platform stuff */ |
993 | result = platform_driver_register(&platform_driver); | 1085 | result = platform_driver_register(&platform_driver); |
994 | if (result) | 1086 | if (result) |
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 16fffe44e333..4ac2311c00af 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -47,7 +47,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | |||
47 | #define HPWMI_DISPLAY_QUERY 0x1 | 47 | #define HPWMI_DISPLAY_QUERY 0x1 |
48 | #define HPWMI_HDDTEMP_QUERY 0x2 | 48 | #define HPWMI_HDDTEMP_QUERY 0x2 |
49 | #define HPWMI_ALS_QUERY 0x3 | 49 | #define HPWMI_ALS_QUERY 0x3 |
50 | #define HPWMI_DOCK_QUERY 0x4 | 50 | #define HPWMI_HARDWARE_QUERY 0x4 |
51 | #define HPWMI_WIRELESS_QUERY 0x5 | 51 | #define HPWMI_WIRELESS_QUERY 0x5 |
52 | #define HPWMI_HOTKEY_QUERY 0xc | 52 | #define HPWMI_HOTKEY_QUERY 0xc |
53 | 53 | ||
@@ -75,10 +75,9 @@ struct key_entry { | |||
75 | u16 keycode; | 75 | u16 keycode; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | enum { KE_KEY, KE_SW, KE_END }; | 78 | enum { KE_KEY, KE_END }; |
79 | 79 | ||
80 | static struct key_entry hp_wmi_keymap[] = { | 80 | static struct key_entry hp_wmi_keymap[] = { |
81 | {KE_SW, 0x01, SW_DOCK}, | ||
82 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | 81 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, |
83 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | 82 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, |
84 | {KE_KEY, 0x20e6, KEY_PROG1}, | 83 | {KE_KEY, 0x20e6, KEY_PROG1}, |
@@ -151,7 +150,22 @@ static int hp_wmi_als_state(void) | |||
151 | 150 | ||
152 | static int hp_wmi_dock_state(void) | 151 | static int hp_wmi_dock_state(void) |
153 | { | 152 | { |
154 | return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); | 153 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); |
154 | |||
155 | if (ret < 0) | ||
156 | return ret; | ||
157 | |||
158 | return ret & 0x1; | ||
159 | } | ||
160 | |||
161 | static int hp_wmi_tablet_state(void) | ||
162 | { | ||
163 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); | ||
164 | |||
165 | if (ret < 0) | ||
166 | return ret; | ||
167 | |||
168 | return (ret & 0x4) ? 1 : 0; | ||
155 | } | 169 | } |
156 | 170 | ||
157 | static int hp_wmi_set_block(void *data, bool blocked) | 171 | static int hp_wmi_set_block(void *data, bool blocked) |
@@ -232,6 +246,15 @@ static ssize_t show_dock(struct device *dev, struct device_attribute *attr, | |||
232 | return sprintf(buf, "%d\n", value); | 246 | return sprintf(buf, "%d\n", value); |
233 | } | 247 | } |
234 | 248 | ||
249 | static ssize_t show_tablet(struct device *dev, struct device_attribute *attr, | ||
250 | char *buf) | ||
251 | { | ||
252 | int value = hp_wmi_tablet_state(); | ||
253 | if (value < 0) | ||
254 | return -EINVAL; | ||
255 | return sprintf(buf, "%d\n", value); | ||
256 | } | ||
257 | |||
235 | static ssize_t set_als(struct device *dev, struct device_attribute *attr, | 258 | static ssize_t set_als(struct device *dev, struct device_attribute *attr, |
236 | const char *buf, size_t count) | 259 | const char *buf, size_t count) |
237 | { | 260 | { |
@@ -244,6 +267,7 @@ static DEVICE_ATTR(display, S_IRUGO, show_display, NULL); | |||
244 | static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); | 267 | static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); |
245 | static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | 268 | static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); |
246 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | 269 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); |
270 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); | ||
247 | 271 | ||
248 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) | 272 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) |
249 | { | 273 | { |
@@ -326,13 +350,13 @@ static void hp_wmi_notify(u32 value, void *context) | |||
326 | key->keycode, 0); | 350 | key->keycode, 0); |
327 | input_sync(hp_wmi_input_dev); | 351 | input_sync(hp_wmi_input_dev); |
328 | break; | 352 | break; |
329 | case KE_SW: | ||
330 | input_report_switch(hp_wmi_input_dev, | ||
331 | key->keycode, | ||
332 | hp_wmi_dock_state()); | ||
333 | input_sync(hp_wmi_input_dev); | ||
334 | break; | ||
335 | } | 353 | } |
354 | } else if (eventcode == 0x1) { | ||
355 | input_report_switch(hp_wmi_input_dev, SW_DOCK, | ||
356 | hp_wmi_dock_state()); | ||
357 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, | ||
358 | hp_wmi_tablet_state()); | ||
359 | input_sync(hp_wmi_input_dev); | ||
336 | } else if (eventcode == 0x5) { | 360 | } else if (eventcode == 0x5) { |
337 | if (wifi_rfkill) | 361 | if (wifi_rfkill) |
338 | rfkill_set_sw_state(wifi_rfkill, | 362 | rfkill_set_sw_state(wifi_rfkill, |
@@ -369,18 +393,19 @@ static int __init hp_wmi_input_setup(void) | |||
369 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | 393 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); |
370 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | 394 | set_bit(key->keycode, hp_wmi_input_dev->keybit); |
371 | break; | 395 | break; |
372 | case KE_SW: | ||
373 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | ||
374 | set_bit(key->keycode, hp_wmi_input_dev->swbit); | ||
375 | |||
376 | /* Set initial dock state */ | ||
377 | input_report_switch(hp_wmi_input_dev, key->keycode, | ||
378 | hp_wmi_dock_state()); | ||
379 | input_sync(hp_wmi_input_dev); | ||
380 | break; | ||
381 | } | 396 | } |
382 | } | 397 | } |
383 | 398 | ||
399 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | ||
400 | set_bit(SW_DOCK, hp_wmi_input_dev->swbit); | ||
401 | set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); | ||
402 | |||
403 | /* Set initial hardware state */ | ||
404 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); | ||
405 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, | ||
406 | hp_wmi_tablet_state()); | ||
407 | input_sync(hp_wmi_input_dev); | ||
408 | |||
384 | err = input_register_device(hp_wmi_input_dev); | 409 | err = input_register_device(hp_wmi_input_dev); |
385 | 410 | ||
386 | if (err) { | 411 | if (err) { |
@@ -397,6 +422,7 @@ static void cleanup_sysfs(struct platform_device *device) | |||
397 | device_remove_file(&device->dev, &dev_attr_hddtemp); | 422 | device_remove_file(&device->dev, &dev_attr_hddtemp); |
398 | device_remove_file(&device->dev, &dev_attr_als); | 423 | device_remove_file(&device->dev, &dev_attr_als); |
399 | device_remove_file(&device->dev, &dev_attr_dock); | 424 | device_remove_file(&device->dev, &dev_attr_dock); |
425 | device_remove_file(&device->dev, &dev_attr_tablet); | ||
400 | } | 426 | } |
401 | 427 | ||
402 | static int __init hp_wmi_bios_setup(struct platform_device *device) | 428 | static int __init hp_wmi_bios_setup(struct platform_device *device) |
@@ -416,6 +442,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) | |||
416 | err = device_create_file(&device->dev, &dev_attr_dock); | 442 | err = device_create_file(&device->dev, &dev_attr_dock); |
417 | if (err) | 443 | if (err) |
418 | goto add_sysfs_error; | 444 | goto add_sysfs_error; |
445 | err = device_create_file(&device->dev, &dev_attr_tablet); | ||
446 | if (err) | ||
447 | goto add_sysfs_error; | ||
419 | 448 | ||
420 | if (wireless & 0x1) { | 449 | if (wireless & 0x1) { |
421 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, | 450 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, |
@@ -485,23 +514,17 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) | |||
485 | 514 | ||
486 | static int hp_wmi_resume_handler(struct platform_device *device) | 515 | static int hp_wmi_resume_handler(struct platform_device *device) |
487 | { | 516 | { |
488 | struct key_entry *key; | ||
489 | |||
490 | /* | 517 | /* |
491 | * Docking state may have changed while suspended, so trigger | 518 | * Hardware state may have changed while suspended, so trigger |
492 | * an input event for the current state. As this is a switch, | 519 | * input events for the current state. As this is a switch, |
493 | * the input layer will only actually pass it on if the state | 520 | * the input layer will only actually pass it on if the state |
494 | * changed. | 521 | * changed. |
495 | */ | 522 | */ |
496 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | 523 | |
497 | switch (key->type) { | 524 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); |
498 | case KE_SW: | 525 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, |
499 | input_report_switch(hp_wmi_input_dev, key->keycode, | 526 | hp_wmi_tablet_state()); |
500 | hp_wmi_dock_state()); | 527 | input_sync(hp_wmi_input_dev); |
501 | input_sync(hp_wmi_input_dev); | ||
502 | break; | ||
503 | } | ||
504 | } | ||
505 | 528 | ||
506 | return 0; | 529 | return 0; |
507 | } | 530 | } |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 40d64c03278c..a463fd72c495 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -22,7 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.23" | 24 | #define TPACPI_VERSION "0.23" |
25 | #define TPACPI_SYSFS_VERSION 0x020300 | 25 | #define TPACPI_SYSFS_VERSION 0x020400 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -257,6 +257,8 @@ static struct { | |||
257 | u32 wan:1; | 257 | u32 wan:1; |
258 | u32 uwb:1; | 258 | u32 uwb:1; |
259 | u32 fan_ctrl_status_undef:1; | 259 | u32 fan_ctrl_status_undef:1; |
260 | u32 second_fan:1; | ||
261 | u32 beep_needs_two_args:1; | ||
260 | u32 input_device_registered:1; | 262 | u32 input_device_registered:1; |
261 | u32 platform_drv_registered:1; | 263 | u32 platform_drv_registered:1; |
262 | u32 platform_drv_attrs_registered:1; | 264 | u32 platform_drv_attrs_registered:1; |
@@ -277,8 +279,10 @@ struct thinkpad_id_data { | |||
277 | char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ | 279 | char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ |
278 | char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ | 280 | char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ |
279 | 281 | ||
280 | u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ | 282 | u16 bios_model; /* 1Y = 0x5931, 0 = unknown */ |
281 | u16 ec_model; | 283 | u16 ec_model; |
284 | u16 bios_release; /* 1ZETK1WW = 0x314b, 0 = unknown */ | ||
285 | u16 ec_release; | ||
282 | 286 | ||
283 | char *model_str; /* ThinkPad T43 */ | 287 | char *model_str; /* ThinkPad T43 */ |
284 | char *nummodel_str; /* 9384A9C for a 9384-A9C model */ | 288 | char *nummodel_str; /* 9384A9C for a 9384-A9C model */ |
@@ -355,6 +359,73 @@ static void tpacpi_log_usertask(const char * const what) | |||
355 | } \ | 359 | } \ |
356 | } while (0) | 360 | } while (0) |
357 | 361 | ||
362 | /* | ||
363 | * Quirk handling helpers | ||
364 | * | ||
365 | * ThinkPad IDs and versions seen in the field so far | ||
366 | * are two-characters from the set [0-9A-Z], i.e. base 36. | ||
367 | * | ||
368 | * We use values well outside that range as specials. | ||
369 | */ | ||
370 | |||
371 | #define TPACPI_MATCH_ANY 0xffffU | ||
372 | #define TPACPI_MATCH_UNKNOWN 0U | ||
373 | |||
374 | /* TPID('1', 'Y') == 0x5931 */ | ||
375 | #define TPID(__c1, __c2) (((__c2) << 8) | (__c1)) | ||
376 | |||
377 | #define TPACPI_Q_IBM(__id1, __id2, __quirk) \ | ||
378 | { .vendor = PCI_VENDOR_ID_IBM, \ | ||
379 | .bios = TPID(__id1, __id2), \ | ||
380 | .ec = TPACPI_MATCH_ANY, \ | ||
381 | .quirks = (__quirk) } | ||
382 | |||
383 | #define TPACPI_Q_LNV(__id1, __id2, __quirk) \ | ||
384 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
385 | .bios = TPID(__id1, __id2), \ | ||
386 | .ec = TPACPI_MATCH_ANY, \ | ||
387 | .quirks = (__quirk) } | ||
388 | |||
389 | struct tpacpi_quirk { | ||
390 | unsigned int vendor; | ||
391 | u16 bios; | ||
392 | u16 ec; | ||
393 | unsigned long quirks; | ||
394 | }; | ||
395 | |||
396 | /** | ||
397 | * tpacpi_check_quirks() - search BIOS/EC version on a list | ||
398 | * @qlist: array of &struct tpacpi_quirk | ||
399 | * @qlist_size: number of elements in @qlist | ||
400 | * | ||
401 | * Iterates over a quirks list until one is found that matches the | ||
402 | * ThinkPad's vendor, BIOS and EC model. | ||
403 | * | ||
404 | * Returns 0 if nothing matches, otherwise returns the quirks field of | ||
405 | * the matching &struct tpacpi_quirk entry. | ||
406 | * | ||
407 | * The match criteria is: vendor, ec and bios much match. | ||
408 | */ | ||
409 | static unsigned long __init tpacpi_check_quirks( | ||
410 | const struct tpacpi_quirk *qlist, | ||
411 | unsigned int qlist_size) | ||
412 | { | ||
413 | while (qlist_size) { | ||
414 | if ((qlist->vendor == thinkpad_id.vendor || | ||
415 | qlist->vendor == TPACPI_MATCH_ANY) && | ||
416 | (qlist->bios == thinkpad_id.bios_model || | ||
417 | qlist->bios == TPACPI_MATCH_ANY) && | ||
418 | (qlist->ec == thinkpad_id.ec_model || | ||
419 | qlist->ec == TPACPI_MATCH_ANY)) | ||
420 | return qlist->quirks; | ||
421 | |||
422 | qlist_size--; | ||
423 | qlist++; | ||
424 | } | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | |||
358 | /**************************************************************************** | 429 | /**************************************************************************** |
359 | **************************************************************************** | 430 | **************************************************************************** |
360 | * | 431 | * |
@@ -2880,7 +2951,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2880 | /* update bright_acpimode... */ | 2951 | /* update bright_acpimode... */ |
2881 | tpacpi_check_std_acpi_brightness_support(); | 2952 | tpacpi_check_std_acpi_brightness_support(); |
2882 | 2953 | ||
2883 | if (tp_features.bright_acpimode) { | 2954 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { |
2884 | printk(TPACPI_INFO | 2955 | printk(TPACPI_INFO |
2885 | "This ThinkPad has standard ACPI backlight " | 2956 | "This ThinkPad has standard ACPI backlight " |
2886 | "brightness control, supported by the ACPI " | 2957 | "brightness control, supported by the ACPI " |
@@ -4773,7 +4844,7 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | |||
4773 | "LED", /* all others */ | 4844 | "LED", /* all others */ |
4774 | ); /* R30, R31 */ | 4845 | ); /* R30, R31 */ |
4775 | 4846 | ||
4776 | #define TPACPI_LED_NUMLEDS 8 | 4847 | #define TPACPI_LED_NUMLEDS 16 |
4777 | static struct tpacpi_led_classdev *tpacpi_leds; | 4848 | static struct tpacpi_led_classdev *tpacpi_leds; |
4778 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; | 4849 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; |
4779 | static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | 4850 | static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { |
@@ -4786,15 +4857,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | |||
4786 | "tpacpi::dock_batt", | 4857 | "tpacpi::dock_batt", |
4787 | "tpacpi::unknown_led", | 4858 | "tpacpi::unknown_led", |
4788 | "tpacpi::standby", | 4859 | "tpacpi::standby", |
4860 | "tpacpi::dock_status1", | ||
4861 | "tpacpi::dock_status2", | ||
4862 | "tpacpi::unknown_led2", | ||
4863 | "tpacpi::unknown_led3", | ||
4864 | "tpacpi::thinkvantage", | ||
4789 | }; | 4865 | }; |
4790 | #define TPACPI_SAFE_LEDS 0x0081U | 4866 | #define TPACPI_SAFE_LEDS 0x1081U |
4791 | 4867 | ||
4792 | static inline bool tpacpi_is_led_restricted(const unsigned int led) | 4868 | static inline bool tpacpi_is_led_restricted(const unsigned int led) |
4793 | { | 4869 | { |
4794 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 4870 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
4795 | return false; | 4871 | return false; |
4796 | #else | 4872 | #else |
4797 | return (TPACPI_SAFE_LEDS & (1 << led)) == 0; | 4873 | return (1U & (TPACPI_SAFE_LEDS >> led)) == 0; |
4798 | #endif | 4874 | #endif |
4799 | } | 4875 | } |
4800 | 4876 | ||
@@ -4956,6 +5032,10 @@ static int __init tpacpi_init_led(unsigned int led) | |||
4956 | 5032 | ||
4957 | tpacpi_leds[led].led = led; | 5033 | tpacpi_leds[led].led = led; |
4958 | 5034 | ||
5035 | /* LEDs with no name don't get registered */ | ||
5036 | if (!tpacpi_led_names[led]) | ||
5037 | return 0; | ||
5038 | |||
4959 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; | 5039 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; |
4960 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; | 5040 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; |
4961 | if (led_supported == TPACPI_LED_570) | 5041 | if (led_supported == TPACPI_LED_570) |
@@ -4974,10 +5054,59 @@ static int __init tpacpi_init_led(unsigned int led) | |||
4974 | return rc; | 5054 | return rc; |
4975 | } | 5055 | } |
4976 | 5056 | ||
5057 | static const struct tpacpi_quirk led_useful_qtable[] __initconst = { | ||
5058 | TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */ | ||
5059 | TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */ | ||
5060 | TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */ | ||
5061 | |||
5062 | TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */ | ||
5063 | TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */ | ||
5064 | TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */ | ||
5065 | TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */ | ||
5066 | TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */ | ||
5067 | TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */ | ||
5068 | TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */ | ||
5069 | TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */ | ||
5070 | |||
5071 | TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */ | ||
5072 | TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */ | ||
5073 | TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */ | ||
5074 | TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */ | ||
5075 | TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */ | ||
5076 | |||
5077 | TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */ | ||
5078 | TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */ | ||
5079 | TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */ | ||
5080 | TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */ | ||
5081 | |||
5082 | /* (1) - may have excess leds enabled on MSB */ | ||
5083 | |||
5084 | /* Defaults (order matters, keep last, don't reorder!) */ | ||
5085 | { /* Lenovo */ | ||
5086 | .vendor = PCI_VENDOR_ID_LENOVO, | ||
5087 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, | ||
5088 | .quirks = 0x1fffU, | ||
5089 | }, | ||
5090 | { /* IBM ThinkPads with no EC version string */ | ||
5091 | .vendor = PCI_VENDOR_ID_IBM, | ||
5092 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN, | ||
5093 | .quirks = 0x00ffU, | ||
5094 | }, | ||
5095 | { /* IBM ThinkPads with EC version string */ | ||
5096 | .vendor = PCI_VENDOR_ID_IBM, | ||
5097 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, | ||
5098 | .quirks = 0x00bfU, | ||
5099 | }, | ||
5100 | }; | ||
5101 | |||
5102 | #undef TPACPI_LEDQ_IBM | ||
5103 | #undef TPACPI_LEDQ_LNV | ||
5104 | |||
4977 | static int __init led_init(struct ibm_init_struct *iibm) | 5105 | static int __init led_init(struct ibm_init_struct *iibm) |
4978 | { | 5106 | { |
4979 | unsigned int i; | 5107 | unsigned int i; |
4980 | int rc; | 5108 | int rc; |
5109 | unsigned long useful_leds; | ||
4981 | 5110 | ||
4982 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 5111 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
4983 | 5112 | ||
@@ -4999,6 +5128,9 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
4999 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 5128 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
5000 | str_supported(led_supported), led_supported); | 5129 | str_supported(led_supported), led_supported); |
5001 | 5130 | ||
5131 | if (led_supported == TPACPI_LED_NONE) | ||
5132 | return 1; | ||
5133 | |||
5002 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | 5134 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, |
5003 | GFP_KERNEL); | 5135 | GFP_KERNEL); |
5004 | if (!tpacpi_leds) { | 5136 | if (!tpacpi_leds) { |
@@ -5006,8 +5138,12 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5006 | return -ENOMEM; | 5138 | return -ENOMEM; |
5007 | } | 5139 | } |
5008 | 5140 | ||
5141 | useful_leds = tpacpi_check_quirks(led_useful_qtable, | ||
5142 | ARRAY_SIZE(led_useful_qtable)); | ||
5143 | |||
5009 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | 5144 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { |
5010 | if (!tpacpi_is_led_restricted(i)) { | 5145 | if (!tpacpi_is_led_restricted(i) && |
5146 | test_bit(i, &useful_leds)) { | ||
5011 | rc = tpacpi_init_led(i); | 5147 | rc = tpacpi_init_led(i); |
5012 | if (rc < 0) { | 5148 | if (rc < 0) { |
5013 | led_exit(); | 5149 | led_exit(); |
@@ -5017,12 +5153,11 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5017 | } | 5153 | } |
5018 | 5154 | ||
5019 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 5155 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
5020 | if (led_supported != TPACPI_LED_NONE) | 5156 | printk(TPACPI_NOTICE |
5021 | printk(TPACPI_NOTICE | 5157 | "warning: userspace override of important " |
5022 | "warning: userspace override of important " | 5158 | "firmware LEDs is enabled\n"); |
5023 | "firmware LEDs is enabled\n"); | ||
5024 | #endif | 5159 | #endif |
5025 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 5160 | return 0; |
5026 | } | 5161 | } |
5027 | 5162 | ||
5028 | #define str_led_status(s) \ | 5163 | #define str_led_status(s) \ |
@@ -5052,7 +5187,7 @@ static int led_read(char *p) | |||
5052 | } | 5187 | } |
5053 | 5188 | ||
5054 | len += sprintf(p + len, "commands:\t" | 5189 | len += sprintf(p + len, "commands:\t" |
5055 | "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); | 5190 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); |
5056 | 5191 | ||
5057 | return len; | 5192 | return len; |
5058 | } | 5193 | } |
@@ -5067,7 +5202,7 @@ static int led_write(char *buf) | |||
5067 | return -ENODEV; | 5202 | return -ENODEV; |
5068 | 5203 | ||
5069 | while ((cmd = next_cmd(&buf))) { | 5204 | while ((cmd = next_cmd(&buf))) { |
5070 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) | 5205 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15) |
5071 | return -EINVAL; | 5206 | return -EINVAL; |
5072 | 5207 | ||
5073 | if (strstr(cmd, "off")) { | 5208 | if (strstr(cmd, "off")) { |
@@ -5101,8 +5236,17 @@ static struct ibm_struct led_driver_data = { | |||
5101 | 5236 | ||
5102 | TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ | 5237 | TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ |
5103 | 5238 | ||
5239 | #define TPACPI_BEEP_Q1 0x0001 | ||
5240 | |||
5241 | static const struct tpacpi_quirk beep_quirk_table[] __initconst = { | ||
5242 | TPACPI_Q_IBM('I', 'M', TPACPI_BEEP_Q1), /* 570 */ | ||
5243 | TPACPI_Q_IBM('I', 'U', TPACPI_BEEP_Q1), /* 570E - unverified */ | ||
5244 | }; | ||
5245 | |||
5104 | static int __init beep_init(struct ibm_init_struct *iibm) | 5246 | static int __init beep_init(struct ibm_init_struct *iibm) |
5105 | { | 5247 | { |
5248 | unsigned long quirks; | ||
5249 | |||
5106 | vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); | 5250 | vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); |
5107 | 5251 | ||
5108 | TPACPI_ACPIHANDLE_INIT(beep); | 5252 | TPACPI_ACPIHANDLE_INIT(beep); |
@@ -5110,6 +5254,11 @@ static int __init beep_init(struct ibm_init_struct *iibm) | |||
5110 | vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", | 5254 | vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", |
5111 | str_supported(beep_handle != NULL)); | 5255 | str_supported(beep_handle != NULL)); |
5112 | 5256 | ||
5257 | quirks = tpacpi_check_quirks(beep_quirk_table, | ||
5258 | ARRAY_SIZE(beep_quirk_table)); | ||
5259 | |||
5260 | tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1); | ||
5261 | |||
5113 | return (beep_handle)? 0 : 1; | 5262 | return (beep_handle)? 0 : 1; |
5114 | } | 5263 | } |
5115 | 5264 | ||
@@ -5141,8 +5290,15 @@ static int beep_write(char *buf) | |||
5141 | /* beep_cmd set */ | 5290 | /* beep_cmd set */ |
5142 | } else | 5291 | } else |
5143 | return -EINVAL; | 5292 | return -EINVAL; |
5144 | if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) | 5293 | if (tp_features.beep_needs_two_args) { |
5145 | return -EIO; | 5294 | if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", |
5295 | beep_cmd, 0)) | ||
5296 | return -EIO; | ||
5297 | } else { | ||
5298 | if (!acpi_evalf(beep_handle, NULL, NULL, "vd", | ||
5299 | beep_cmd)) | ||
5300 | return -EIO; | ||
5301 | } | ||
5146 | } | 5302 | } |
5147 | 5303 | ||
5148 | return 0; | 5304 | return 0; |
@@ -5569,6 +5725,10 @@ static struct ibm_struct ecdump_driver_data = { | |||
5569 | * Bit 3-0: backlight brightness level | 5725 | * Bit 3-0: backlight brightness level |
5570 | * | 5726 | * |
5571 | * brightness_get_raw returns status data in the HBRV layout | 5727 | * brightness_get_raw returns status data in the HBRV layout |
5728 | * | ||
5729 | * WARNING: The X61 has been verified to use HBRV for something else, so | ||
5730 | * this should be used _only_ on IBM ThinkPads, and maybe with some careful | ||
5731 | * testing on the very early *60 Lenovo models... | ||
5572 | */ | 5732 | */ |
5573 | 5733 | ||
5574 | enum { | 5734 | enum { |
@@ -5869,6 +6029,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
5869 | brightness_mode); | 6029 | brightness_mode); |
5870 | } | 6030 | } |
5871 | 6031 | ||
6032 | /* Safety */ | ||
6033 | if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM && | ||
6034 | (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || | ||
6035 | brightness_mode == TPACPI_BRGHT_MODE_EC)) | ||
6036 | return -EINVAL; | ||
6037 | |||
5872 | if (tpacpi_brightness_get_raw(&b) < 0) | 6038 | if (tpacpi_brightness_get_raw(&b) < 0) |
5873 | return 1; | 6039 | return 1; |
5874 | 6040 | ||
@@ -6161,6 +6327,21 @@ static struct ibm_struct volume_driver_data = { | |||
6161 | * For firmware bugs, refer to: | 6327 | * For firmware bugs, refer to: |
6162 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues | 6328 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
6163 | * | 6329 | * |
6330 | * ---- | ||
6331 | * | ||
6332 | * ThinkPad EC register 0x31 bit 0 (only on select models) | ||
6333 | * | ||
6334 | * When bit 0 of EC register 0x31 is zero, the tachometer registers | ||
6335 | * show the speed of the main fan. When bit 0 of EC register 0x31 | ||
6336 | * is one, the tachometer registers show the speed of the auxiliary | ||
6337 | * fan. | ||
6338 | * | ||
6339 | * Fan control seems to affect both fans, regardless of the state | ||
6340 | * of this bit. | ||
6341 | * | ||
6342 | * So far, only the firmware for the X60/X61 non-tablet versions | ||
6343 | * seem to support this (firmware TP-7M). | ||
6344 | * | ||
6164 | * TPACPI_FAN_WR_ACPI_FANS: | 6345 | * TPACPI_FAN_WR_ACPI_FANS: |
6165 | * ThinkPad X31, X40, X41. Not available in the X60. | 6346 | * ThinkPad X31, X40, X41. Not available in the X60. |
6166 | * | 6347 | * |
@@ -6187,6 +6368,8 @@ enum { /* Fan control constants */ | |||
6187 | fan_status_offset = 0x2f, /* EC register 0x2f */ | 6368 | fan_status_offset = 0x2f, /* EC register 0x2f */ |
6188 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) | 6369 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) |
6189 | * 0x84 must be read before 0x85 */ | 6370 | * 0x84 must be read before 0x85 */ |
6371 | fan_select_offset = 0x31, /* EC register 0x31 (Firmware 7M) | ||
6372 | bit 0 selects which fan is active */ | ||
6190 | 6373 | ||
6191 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ | 6374 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ |
6192 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ | 6375 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ |
@@ -6249,30 +6432,18 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
6249 | * We assume 0x07 really means auto mode while this quirk is active, | 6432 | * We assume 0x07 really means auto mode while this quirk is active, |
6250 | * as this is far more likely than the ThinkPad being in level 7, | 6433 | * as this is far more likely than the ThinkPad being in level 7, |
6251 | * which is only used by the firmware during thermal emergencies. | 6434 | * which is only used by the firmware during thermal emergencies. |
6435 | * | ||
6436 | * Enable for TP-1Y (T43), TP-78 (R51e), TP-76 (R52), | ||
6437 | * TP-70 (T43, R52), which are known to be buggy. | ||
6252 | */ | 6438 | */ |
6253 | 6439 | ||
6254 | static void fan_quirk1_detect(void) | 6440 | static void fan_quirk1_setup(void) |
6255 | { | 6441 | { |
6256 | /* In some ThinkPads, neither the EC nor the ACPI | ||
6257 | * DSDT initialize the HFSP register, and it ends up | ||
6258 | * being initially set to 0x07 when it *could* be | ||
6259 | * either 0x07 or 0x80. | ||
6260 | * | ||
6261 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
6262 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
6263 | * to be buggy. */ | ||
6264 | if (fan_control_initial_status == 0x07) { | 6442 | if (fan_control_initial_status == 0x07) { |
6265 | switch (thinkpad_id.ec_model) { | 6443 | printk(TPACPI_NOTICE |
6266 | case 0x5931: /* TP-1Y */ | 6444 | "fan_init: initial fan status is unknown, " |
6267 | case 0x3837: /* TP-78 */ | 6445 | "assuming it is in auto mode\n"); |
6268 | case 0x3637: /* TP-76 */ | 6446 | tp_features.fan_ctrl_status_undef = 1; |
6269 | case 0x3037: /* TP-70 */ | ||
6270 | printk(TPACPI_NOTICE | ||
6271 | "fan_init: initial fan status is unknown, " | ||
6272 | "assuming it is in auto mode\n"); | ||
6273 | tp_features.fan_ctrl_status_undef = 1; | ||
6274 | ;; | ||
6275 | } | ||
6276 | } | 6447 | } |
6277 | } | 6448 | } |
6278 | 6449 | ||
@@ -6292,6 +6463,38 @@ static void fan_quirk1_handle(u8 *fan_status) | |||
6292 | } | 6463 | } |
6293 | } | 6464 | } |
6294 | 6465 | ||
6466 | /* Select main fan on X60/X61, NOOP on others */ | ||
6467 | static bool fan_select_fan1(void) | ||
6468 | { | ||
6469 | if (tp_features.second_fan) { | ||
6470 | u8 val; | ||
6471 | |||
6472 | if (ec_read(fan_select_offset, &val) < 0) | ||
6473 | return false; | ||
6474 | val &= 0xFEU; | ||
6475 | if (ec_write(fan_select_offset, val) < 0) | ||
6476 | return false; | ||
6477 | } | ||
6478 | return true; | ||
6479 | } | ||
6480 | |||
6481 | /* Select secondary fan on X60/X61 */ | ||
6482 | static bool fan_select_fan2(void) | ||
6483 | { | ||
6484 | u8 val; | ||
6485 | |||
6486 | if (!tp_features.second_fan) | ||
6487 | return false; | ||
6488 | |||
6489 | if (ec_read(fan_select_offset, &val) < 0) | ||
6490 | return false; | ||
6491 | val |= 0x01U; | ||
6492 | if (ec_write(fan_select_offset, val) < 0) | ||
6493 | return false; | ||
6494 | |||
6495 | return true; | ||
6496 | } | ||
6497 | |||
6295 | /* | 6498 | /* |
6296 | * Call with fan_mutex held | 6499 | * Call with fan_mutex held |
6297 | */ | 6500 | */ |
@@ -6369,6 +6572,8 @@ static int fan_get_speed(unsigned int *speed) | |||
6369 | switch (fan_status_access_mode) { | 6572 | switch (fan_status_access_mode) { |
6370 | case TPACPI_FAN_RD_TPEC: | 6573 | case TPACPI_FAN_RD_TPEC: |
6371 | /* all except 570, 600e/x, 770e, 770x */ | 6574 | /* all except 570, 600e/x, 770e, 770x */ |
6575 | if (unlikely(!fan_select_fan1())) | ||
6576 | return -EIO; | ||
6372 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || | 6577 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || |
6373 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) | 6578 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) |
6374 | return -EIO; | 6579 | return -EIO; |
@@ -6385,6 +6590,34 @@ static int fan_get_speed(unsigned int *speed) | |||
6385 | return 0; | 6590 | return 0; |
6386 | } | 6591 | } |
6387 | 6592 | ||
6593 | static int fan2_get_speed(unsigned int *speed) | ||
6594 | { | ||
6595 | u8 hi, lo; | ||
6596 | bool rc; | ||
6597 | |||
6598 | switch (fan_status_access_mode) { | ||
6599 | case TPACPI_FAN_RD_TPEC: | ||
6600 | /* all except 570, 600e/x, 770e, 770x */ | ||
6601 | if (unlikely(!fan_select_fan2())) | ||
6602 | return -EIO; | ||
6603 | rc = !acpi_ec_read(fan_rpm_offset, &lo) || | ||
6604 | !acpi_ec_read(fan_rpm_offset + 1, &hi); | ||
6605 | fan_select_fan1(); /* play it safe */ | ||
6606 | if (rc) | ||
6607 | return -EIO; | ||
6608 | |||
6609 | if (likely(speed)) | ||
6610 | *speed = (hi << 8) | lo; | ||
6611 | |||
6612 | break; | ||
6613 | |||
6614 | default: | ||
6615 | return -ENXIO; | ||
6616 | } | ||
6617 | |||
6618 | return 0; | ||
6619 | } | ||
6620 | |||
6388 | static int fan_set_level(int level) | 6621 | static int fan_set_level(int level) |
6389 | { | 6622 | { |
6390 | if (!fan_control_allowed) | 6623 | if (!fan_control_allowed) |
@@ -6790,6 +7023,25 @@ static struct device_attribute dev_attr_fan_fan1_input = | |||
6790 | __ATTR(fan1_input, S_IRUGO, | 7023 | __ATTR(fan1_input, S_IRUGO, |
6791 | fan_fan1_input_show, NULL); | 7024 | fan_fan1_input_show, NULL); |
6792 | 7025 | ||
7026 | /* sysfs fan fan2_input ------------------------------------------------ */ | ||
7027 | static ssize_t fan_fan2_input_show(struct device *dev, | ||
7028 | struct device_attribute *attr, | ||
7029 | char *buf) | ||
7030 | { | ||
7031 | int res; | ||
7032 | unsigned int speed; | ||
7033 | |||
7034 | res = fan2_get_speed(&speed); | ||
7035 | if (res < 0) | ||
7036 | return res; | ||
7037 | |||
7038 | return snprintf(buf, PAGE_SIZE, "%u\n", speed); | ||
7039 | } | ||
7040 | |||
7041 | static struct device_attribute dev_attr_fan_fan2_input = | ||
7042 | __ATTR(fan2_input, S_IRUGO, | ||
7043 | fan_fan2_input_show, NULL); | ||
7044 | |||
6793 | /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ | 7045 | /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ |
6794 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, | 7046 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, |
6795 | char *buf) | 7047 | char *buf) |
@@ -6823,6 +7075,7 @@ static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, | |||
6823 | static struct attribute *fan_attributes[] = { | 7075 | static struct attribute *fan_attributes[] = { |
6824 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, | 7076 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, |
6825 | &dev_attr_fan_fan1_input.attr, | 7077 | &dev_attr_fan_fan1_input.attr, |
7078 | NULL, /* for fan2_input */ | ||
6826 | NULL | 7079 | NULL |
6827 | }; | 7080 | }; |
6828 | 7081 | ||
@@ -6830,9 +7083,36 @@ static const struct attribute_group fan_attr_group = { | |||
6830 | .attrs = fan_attributes, | 7083 | .attrs = fan_attributes, |
6831 | }; | 7084 | }; |
6832 | 7085 | ||
7086 | #define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ | ||
7087 | #define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ | ||
7088 | |||
7089 | #define TPACPI_FAN_QI(__id1, __id2, __quirks) \ | ||
7090 | { .vendor = PCI_VENDOR_ID_IBM, \ | ||
7091 | .bios = TPACPI_MATCH_ANY, \ | ||
7092 | .ec = TPID(__id1, __id2), \ | ||
7093 | .quirks = __quirks } | ||
7094 | |||
7095 | #define TPACPI_FAN_QL(__id1, __id2, __quirks) \ | ||
7096 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
7097 | .bios = TPACPI_MATCH_ANY, \ | ||
7098 | .ec = TPID(__id1, __id2), \ | ||
7099 | .quirks = __quirks } | ||
7100 | |||
7101 | static const struct tpacpi_quirk fan_quirk_table[] __initconst = { | ||
7102 | TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), | ||
7103 | TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), | ||
7104 | TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), | ||
7105 | TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), | ||
7106 | TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), | ||
7107 | }; | ||
7108 | |||
7109 | #undef TPACPI_FAN_QL | ||
7110 | #undef TPACPI_FAN_QI | ||
7111 | |||
6833 | static int __init fan_init(struct ibm_init_struct *iibm) | 7112 | static int __init fan_init(struct ibm_init_struct *iibm) |
6834 | { | 7113 | { |
6835 | int rc; | 7114 | int rc; |
7115 | unsigned long quirks; | ||
6836 | 7116 | ||
6837 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, | 7117 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, |
6838 | "initializing fan subdriver\n"); | 7118 | "initializing fan subdriver\n"); |
@@ -6843,12 +7123,16 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
6843 | fan_control_commands = 0; | 7123 | fan_control_commands = 0; |
6844 | fan_watchdog_maxinterval = 0; | 7124 | fan_watchdog_maxinterval = 0; |
6845 | tp_features.fan_ctrl_status_undef = 0; | 7125 | tp_features.fan_ctrl_status_undef = 0; |
7126 | tp_features.second_fan = 0; | ||
6846 | fan_control_desired_level = 7; | 7127 | fan_control_desired_level = 7; |
6847 | 7128 | ||
6848 | TPACPI_ACPIHANDLE_INIT(fans); | 7129 | TPACPI_ACPIHANDLE_INIT(fans); |
6849 | TPACPI_ACPIHANDLE_INIT(gfan); | 7130 | TPACPI_ACPIHANDLE_INIT(gfan); |
6850 | TPACPI_ACPIHANDLE_INIT(sfan); | 7131 | TPACPI_ACPIHANDLE_INIT(sfan); |
6851 | 7132 | ||
7133 | quirks = tpacpi_check_quirks(fan_quirk_table, | ||
7134 | ARRAY_SIZE(fan_quirk_table)); | ||
7135 | |||
6852 | if (gfan_handle) { | 7136 | if (gfan_handle) { |
6853 | /* 570, 600e/x, 770e, 770x */ | 7137 | /* 570, 600e/x, 770e, 770x */ |
6854 | fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; | 7138 | fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; |
@@ -6858,7 +7142,13 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
6858 | if (likely(acpi_ec_read(fan_status_offset, | 7142 | if (likely(acpi_ec_read(fan_status_offset, |
6859 | &fan_control_initial_status))) { | 7143 | &fan_control_initial_status))) { |
6860 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; | 7144 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; |
6861 | fan_quirk1_detect(); | 7145 | if (quirks & TPACPI_FAN_Q1) |
7146 | fan_quirk1_setup(); | ||
7147 | if (quirks & TPACPI_FAN_2FAN) { | ||
7148 | tp_features.second_fan = 1; | ||
7149 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, | ||
7150 | "secondary fan support enabled\n"); | ||
7151 | } | ||
6862 | } else { | 7152 | } else { |
6863 | printk(TPACPI_ERR | 7153 | printk(TPACPI_ERR |
6864 | "ThinkPad ACPI EC access misbehaving, " | 7154 | "ThinkPad ACPI EC access misbehaving, " |
@@ -6914,6 +7204,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
6914 | 7204 | ||
6915 | if (fan_status_access_mode != TPACPI_FAN_NONE || | 7205 | if (fan_status_access_mode != TPACPI_FAN_NONE || |
6916 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { | 7206 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { |
7207 | if (tp_features.second_fan) { | ||
7208 | /* attach second fan tachometer */ | ||
7209 | fan_attributes[ARRAY_SIZE(fan_attributes)-2] = | ||
7210 | &dev_attr_fan_fan2_input.attr; | ||
7211 | } | ||
6917 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, | 7212 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, |
6918 | &fan_attr_group); | 7213 | &fan_attr_group); |
6919 | if (rc < 0) | 7214 | if (rc < 0) |
@@ -7385,6 +7680,24 @@ err_out: | |||
7385 | 7680 | ||
7386 | /* Probing */ | 7681 | /* Probing */ |
7387 | 7682 | ||
7683 | static bool __pure __init tpacpi_is_fw_digit(const char c) | ||
7684 | { | ||
7685 | return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); | ||
7686 | } | ||
7687 | |||
7688 | /* Most models: xxyTkkWW (#.##c); Ancient 570/600 and -SL lacks (#.##c) */ | ||
7689 | static bool __pure __init tpacpi_is_valid_fw_id(const char* const s, | ||
7690 | const char t) | ||
7691 | { | ||
7692 | return s && strlen(s) >= 8 && | ||
7693 | tpacpi_is_fw_digit(s[0]) && | ||
7694 | tpacpi_is_fw_digit(s[1]) && | ||
7695 | s[2] == t && s[3] == 'T' && | ||
7696 | tpacpi_is_fw_digit(s[4]) && | ||
7697 | tpacpi_is_fw_digit(s[5]) && | ||
7698 | s[6] == 'W' && s[7] == 'W'; | ||
7699 | } | ||
7700 | |||
7388 | /* returns 0 - probe ok, or < 0 - probe error. | 7701 | /* returns 0 - probe ok, or < 0 - probe error. |
7389 | * Probe ok doesn't mean thinkpad found. | 7702 | * Probe ok doesn't mean thinkpad found. |
7390 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ | 7703 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ |
@@ -7411,10 +7724,15 @@ static int __must_check __init get_thinkpad_model_data( | |||
7411 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); | 7724 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); |
7412 | if (s && !tp->bios_version_str) | 7725 | if (s && !tp->bios_version_str) |
7413 | return -ENOMEM; | 7726 | return -ENOMEM; |
7414 | if (!tp->bios_version_str) | 7727 | |
7728 | /* Really ancient ThinkPad 240X will fail this, which is fine */ | ||
7729 | if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E')) | ||
7415 | return 0; | 7730 | return 0; |
7731 | |||
7416 | tp->bios_model = tp->bios_version_str[0] | 7732 | tp->bios_model = tp->bios_version_str[0] |
7417 | | (tp->bios_version_str[1] << 8); | 7733 | | (tp->bios_version_str[1] << 8); |
7734 | tp->bios_release = (tp->bios_version_str[4] << 8) | ||
7735 | | tp->bios_version_str[5]; | ||
7418 | 7736 | ||
7419 | /* | 7737 | /* |
7420 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, | 7738 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, |
@@ -7433,8 +7751,21 @@ static int __must_check __init get_thinkpad_model_data( | |||
7433 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); | 7751 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
7434 | if (!tp->ec_version_str) | 7752 | if (!tp->ec_version_str) |
7435 | return -ENOMEM; | 7753 | return -ENOMEM; |
7436 | tp->ec_model = ec_fw_string[0] | 7754 | |
7437 | | (ec_fw_string[1] << 8); | 7755 | if (tpacpi_is_valid_fw_id(ec_fw_string, 'H')) { |
7756 | tp->ec_model = ec_fw_string[0] | ||
7757 | | (ec_fw_string[1] << 8); | ||
7758 | tp->ec_release = (ec_fw_string[4] << 8) | ||
7759 | | ec_fw_string[5]; | ||
7760 | } else { | ||
7761 | printk(TPACPI_NOTICE | ||
7762 | "ThinkPad firmware release %s " | ||
7763 | "doesn't match the known patterns\n", | ||
7764 | ec_fw_string); | ||
7765 | printk(TPACPI_NOTICE | ||
7766 | "please report this to %s\n", | ||
7767 | TPACPI_MAIL); | ||
7768 | } | ||
7438 | break; | 7769 | break; |
7439 | } | 7770 | } |
7440 | } | 7771 | } |