Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * eeepc-laptop.c - Asus Eee PC extras
4 : : *
5 : : * Based on asus_acpi.c as patched for the Eee PC by Asus:
6 : : * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
7 : : * Based on eee.c from eeepc-linux
8 : : */
9 : :
10 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11 : :
12 : : #include <linux/kernel.h>
13 : : #include <linux/module.h>
14 : : #include <linux/init.h>
15 : : #include <linux/types.h>
16 : : #include <linux/platform_device.h>
17 : : #include <linux/backlight.h>
18 : : #include <linux/fb.h>
19 : : #include <linux/hwmon.h>
20 : : #include <linux/hwmon-sysfs.h>
21 : : #include <linux/slab.h>
22 : : #include <linux/acpi.h>
23 : : #include <linux/uaccess.h>
24 : : #include <linux/input.h>
25 : : #include <linux/input/sparse-keymap.h>
26 : : #include <linux/rfkill.h>
27 : : #include <linux/pci.h>
28 : : #include <linux/pci_hotplug.h>
29 : : #include <linux/leds.h>
30 : : #include <linux/dmi.h>
31 : : #include <acpi/video.h>
32 : :
33 : : #define EEEPC_LAPTOP_VERSION "0.1"
34 : : #define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
35 : : #define EEEPC_LAPTOP_FILE "eeepc"
36 : :
37 : : #define EEEPC_ACPI_CLASS "hotkey"
38 : : #define EEEPC_ACPI_DEVICE_NAME "Hotkey"
39 : : #define EEEPC_ACPI_HID "ASUS010"
40 : :
41 : : MODULE_AUTHOR("Corentin Chary, Eric Cooper");
42 : : MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
43 : : MODULE_LICENSE("GPL");
44 : :
45 : : static bool hotplug_disabled;
46 : :
47 : : module_param(hotplug_disabled, bool, 0444);
48 : : MODULE_PARM_DESC(hotplug_disabled,
49 : : "Disable hotplug for wireless device. "
50 : : "If your laptop need that, please report to "
51 : : "acpi4asus-user@lists.sourceforge.net.");
52 : :
53 : : /*
54 : : * Definitions for Asus EeePC
55 : : */
56 : : #define NOTIFY_BRN_MIN 0x20
57 : : #define NOTIFY_BRN_MAX 0x2f
58 : :
59 : : enum {
60 : : DISABLE_ASL_WLAN = 0x0001,
61 : : DISABLE_ASL_BLUETOOTH = 0x0002,
62 : : DISABLE_ASL_IRDA = 0x0004,
63 : : DISABLE_ASL_CAMERA = 0x0008,
64 : : DISABLE_ASL_TV = 0x0010,
65 : : DISABLE_ASL_GPS = 0x0020,
66 : : DISABLE_ASL_DISPLAYSWITCH = 0x0040,
67 : : DISABLE_ASL_MODEM = 0x0080,
68 : : DISABLE_ASL_CARDREADER = 0x0100,
69 : : DISABLE_ASL_3G = 0x0200,
70 : : DISABLE_ASL_WIMAX = 0x0400,
71 : : DISABLE_ASL_HWCF = 0x0800
72 : : };
73 : :
74 : : enum {
75 : : CM_ASL_WLAN = 0,
76 : : CM_ASL_BLUETOOTH,
77 : : CM_ASL_IRDA,
78 : : CM_ASL_1394,
79 : : CM_ASL_CAMERA,
80 : : CM_ASL_TV,
81 : : CM_ASL_GPS,
82 : : CM_ASL_DVDROM,
83 : : CM_ASL_DISPLAYSWITCH,
84 : : CM_ASL_PANELBRIGHT,
85 : : CM_ASL_BIOSFLASH,
86 : : CM_ASL_ACPIFLASH,
87 : : CM_ASL_CPUFV,
88 : : CM_ASL_CPUTEMPERATURE,
89 : : CM_ASL_FANCPU,
90 : : CM_ASL_FANCHASSIS,
91 : : CM_ASL_USBPORT1,
92 : : CM_ASL_USBPORT2,
93 : : CM_ASL_USBPORT3,
94 : : CM_ASL_MODEM,
95 : : CM_ASL_CARDREADER,
96 : : CM_ASL_3G,
97 : : CM_ASL_WIMAX,
98 : : CM_ASL_HWCF,
99 : : CM_ASL_LID,
100 : : CM_ASL_TYPE,
101 : : CM_ASL_PANELPOWER, /*P901*/
102 : : CM_ASL_TPD
103 : : };
104 : :
105 : : static const char *cm_getv[] = {
106 : : "WLDG", "BTHG", NULL, NULL,
107 : : "CAMG", NULL, NULL, NULL,
108 : : NULL, "PBLG", NULL, NULL,
109 : : "CFVG", NULL, NULL, NULL,
110 : : "USBG", NULL, NULL, "MODG",
111 : : "CRDG", "M3GG", "WIMG", "HWCF",
112 : : "LIDG", "TYPE", "PBPG", "TPDG"
113 : : };
114 : :
115 : : static const char *cm_setv[] = {
116 : : "WLDS", "BTHS", NULL, NULL,
117 : : "CAMS", NULL, NULL, NULL,
118 : : "SDSP", "PBLS", "HDPS", NULL,
119 : : "CFVS", NULL, NULL, NULL,
120 : : "USBG", NULL, NULL, "MODS",
121 : : "CRDS", "M3GS", "WIMS", NULL,
122 : : NULL, NULL, "PBPS", "TPDS"
123 : : };
124 : :
125 : : static const struct key_entry eeepc_keymap[] = {
126 : : { KE_KEY, 0x10, { KEY_WLAN } },
127 : : { KE_KEY, 0x11, { KEY_WLAN } },
128 : : { KE_KEY, 0x12, { KEY_PROG1 } },
129 : : { KE_KEY, 0x13, { KEY_MUTE } },
130 : : { KE_KEY, 0x14, { KEY_VOLUMEDOWN } },
131 : : { KE_KEY, 0x15, { KEY_VOLUMEUP } },
132 : : { KE_KEY, 0x16, { KEY_DISPLAY_OFF } },
133 : : { KE_KEY, 0x1a, { KEY_COFFEE } },
134 : : { KE_KEY, 0x1b, { KEY_ZOOM } },
135 : : { KE_KEY, 0x1c, { KEY_PROG2 } },
136 : : { KE_KEY, 0x1d, { KEY_PROG3 } },
137 : : { KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } },
138 : : { KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } },
139 : : { KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } },
140 : : { KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } },
141 : : { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } },
142 : : { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */
143 : : { KE_KEY, 0x38, { KEY_F14 } },
144 : : { KE_IGNORE, 0x50, { KEY_RESERVED } }, /* AC plugged */
145 : : { KE_IGNORE, 0x51, { KEY_RESERVED } }, /* AC unplugged */
146 : : { KE_END, 0 },
147 : : };
148 : :
149 : : /*
150 : : * This is the main structure, we can use it to store useful information
151 : : */
152 : : struct eeepc_laptop {
153 : : acpi_handle handle; /* the handle of the acpi device */
154 : : u32 cm_supported; /* the control methods supported
155 : : by this BIOS */
156 : : bool cpufv_disabled;
157 : : bool hotplug_disabled;
158 : : u16 event_count[128]; /* count for each event */
159 : :
160 : : struct platform_device *platform_device;
161 : : struct acpi_device *device; /* the device we are in */
162 : : struct backlight_device *backlight_device;
163 : :
164 : : struct input_dev *inputdev;
165 : :
166 : : struct rfkill *wlan_rfkill;
167 : : struct rfkill *bluetooth_rfkill;
168 : : struct rfkill *wwan3g_rfkill;
169 : : struct rfkill *wimax_rfkill;
170 : :
171 : : struct hotplug_slot hotplug_slot;
172 : : struct mutex hotplug_lock;
173 : :
174 : : struct led_classdev tpd_led;
175 : : int tpd_led_wk;
176 : : struct workqueue_struct *led_workqueue;
177 : : struct work_struct tpd_led_work;
178 : : };
179 : :
180 : : /*
181 : : * ACPI Helpers
182 : : */
183 : 0 : static int write_acpi_int(acpi_handle handle, const char *method, int val)
184 : : {
185 : 0 : acpi_status status;
186 : :
187 : 0 : status = acpi_execute_simple_method(handle, (char *)method, val);
188 : :
189 [ # # # # ]: 0 : return (status == AE_OK ? 0 : -1);
190 : : }
191 : :
192 : 0 : static int read_acpi_int(acpi_handle handle, const char *method, int *val)
193 : : {
194 : 0 : acpi_status status;
195 : 0 : unsigned long long result;
196 : :
197 : 0 : status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
198 [ # # ]: 0 : if (ACPI_FAILURE(status)) {
199 : 0 : *val = -1;
200 : 0 : return -1;
201 : : } else {
202 : 0 : *val = result;
203 : 0 : return 0;
204 : : }
205 : : }
206 : :
207 : : static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value)
208 : : {
209 : : const char *method = cm_setv[cm];
210 : :
211 : : if (method == NULL)
212 : : return -ENODEV;
213 : : if ((eeepc->cm_supported & (0x1 << cm)) == 0)
214 : : return -ENODEV;
215 : :
216 : : if (write_acpi_int(eeepc->handle, method, value))
217 : : pr_warn("Error writing %s\n", method);
218 : : return 0;
219 : : }
220 : :
221 : : static int get_acpi(struct eeepc_laptop *eeepc, int cm)
222 : : {
223 : : const char *method = cm_getv[cm];
224 : : int value;
225 : :
226 : : if (method == NULL)
227 : : return -ENODEV;
228 : : if ((eeepc->cm_supported & (0x1 << cm)) == 0)
229 : : return -ENODEV;
230 : :
231 : : if (read_acpi_int(eeepc->handle, method, &value))
232 : : pr_warn("Error reading %s\n", method);
233 : : return value;
234 : : }
235 : :
236 : : static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,
237 : : acpi_handle *handle)
238 : : {
239 : : const char *method = cm_setv[cm];
240 : : acpi_status status;
241 : :
242 : : if (method == NULL)
243 : : return -ENODEV;
244 : : if ((eeepc->cm_supported & (0x1 << cm)) == 0)
245 : : return -ENODEV;
246 : :
247 : : status = acpi_get_handle(eeepc->handle, (char *)method,
248 : : handle);
249 : : if (status != AE_OK) {
250 : : pr_warn("Error finding %s\n", method);
251 : : return -ENODEV;
252 : : }
253 : : return 0;
254 : : }
255 : :
256 : :
257 : : /*
258 : : * Sys helpers
259 : : */
260 : 0 : static int parse_arg(const char *buf, int *val)
261 : : {
262 : 0 : if (sscanf(buf, "%i", val) != 1)
263 : : return -EINVAL;
264 : : return 0;
265 : : }
266 : :
267 : 0 : static ssize_t store_sys_acpi(struct device *dev, int cm,
268 : : const char *buf, size_t count)
269 : : {
270 [ # # ]: 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
271 : 0 : int rv, value;
272 : :
273 [ # # ]: 0 : rv = parse_arg(buf, &value);
274 : 0 : if (rv < 0)
275 : : return rv;
276 : 0 : rv = set_acpi(eeepc, cm, value);
277 [ # # ]: 0 : if (rv < 0)
278 : : return -EIO;
279 : 0 : return count;
280 : : }
281 : :
282 : 0 : static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
283 : : {
284 : 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
285 : 0 : int value = get_acpi(eeepc, cm);
286 : :
287 [ # # ]: 0 : if (value < 0)
288 : : return -EIO;
289 : 0 : return sprintf(buf, "%d\n", value);
290 : : }
291 : :
292 : : #define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \
293 : : static ssize_t _name##_show(struct device *dev, \
294 : : struct device_attribute *attr, \
295 : : char *buf) \
296 : : { \
297 : : return show_sys_acpi(dev, _cm, buf); \
298 : : }
299 : :
300 : : #define EEEPC_ACPI_STORE_FUNC(_name, _cm) \
301 : : static ssize_t _name##_store(struct device *dev, \
302 : : struct device_attribute *attr, \
303 : : const char *buf, size_t count) \
304 : : { \
305 : : return store_sys_acpi(dev, _cm, buf, count); \
306 : : }
307 : :
308 : : #define EEEPC_CREATE_DEVICE_ATTR_RW(_name, _cm) \
309 : : EEEPC_ACPI_SHOW_FUNC(_name, _cm) \
310 : : EEEPC_ACPI_STORE_FUNC(_name, _cm) \
311 : : static DEVICE_ATTR_RW(_name)
312 : :
313 : : #define EEEPC_CREATE_DEVICE_ATTR_WO(_name, _cm) \
314 : : EEEPC_ACPI_STORE_FUNC(_name, _cm) \
315 : : static DEVICE_ATTR_WO(_name)
316 : :
317 : 0 : EEEPC_CREATE_DEVICE_ATTR_RW(camera, CM_ASL_CAMERA);
318 : 0 : EEEPC_CREATE_DEVICE_ATTR_RW(cardr, CM_ASL_CARDREADER);
319 : 0 : EEEPC_CREATE_DEVICE_ATTR_WO(disp, CM_ASL_DISPLAYSWITCH);
320 : :
321 : : struct eeepc_cpufv {
322 : : int num;
323 : : int cur;
324 : : };
325 : :
326 : : static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)
327 : : {
328 : : c->cur = get_acpi(eeepc, CM_ASL_CPUFV);
329 : : if (c->cur < 0)
330 : : return -ENODEV;
331 : :
332 : : c->num = (c->cur >> 8) & 0xff;
333 : : c->cur &= 0xff;
334 : : if (c->num == 0 || c->num > 12)
335 : : return -ENODEV;
336 : : return 0;
337 : : }
338 : :
339 : 0 : static ssize_t available_cpufv_show(struct device *dev,
340 : : struct device_attribute *attr,
341 : : char *buf)
342 : : {
343 : 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
344 : 0 : struct eeepc_cpufv c;
345 : 0 : int i;
346 : 0 : ssize_t len = 0;
347 : :
348 [ # # ]: 0 : if (get_cpufv(eeepc, &c))
349 : : return -ENODEV;
350 [ # # ]: 0 : for (i = 0; i < c.num; i++)
351 : 0 : len += sprintf(buf + len, "%d ", i);
352 : 0 : len += sprintf(buf + len, "\n");
353 : 0 : return len;
354 : : }
355 : :
356 : 0 : static ssize_t cpufv_show(struct device *dev,
357 : : struct device_attribute *attr,
358 : : char *buf)
359 : : {
360 : 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
361 : 0 : struct eeepc_cpufv c;
362 : :
363 [ # # ]: 0 : if (get_cpufv(eeepc, &c))
364 : : return -ENODEV;
365 : 0 : return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
366 : : }
367 : :
368 : 0 : static ssize_t cpufv_store(struct device *dev,
369 : : struct device_attribute *attr,
370 : : const char *buf, size_t count)
371 : : {
372 [ # # ]: 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
373 : 0 : struct eeepc_cpufv c;
374 : 0 : int rv, value;
375 : :
376 [ # # ]: 0 : if (eeepc->cpufv_disabled)
377 : : return -EPERM;
378 [ # # ]: 0 : if (get_cpufv(eeepc, &c))
379 : : return -ENODEV;
380 [ # # ]: 0 : rv = parse_arg(buf, &value);
381 : 0 : if (rv < 0)
382 : : return rv;
383 [ # # # # ]: 0 : if (value < 0 || value >= c.num)
384 : : return -EINVAL;
385 : 0 : rv = set_acpi(eeepc, CM_ASL_CPUFV, value);
386 [ # # ]: 0 : if (rv)
387 : 0 : return rv;
388 : 0 : return count;
389 : : }
390 : :
391 : 0 : static ssize_t cpufv_disabled_show(struct device *dev,
392 : : struct device_attribute *attr,
393 : : char *buf)
394 : : {
395 : 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
396 : :
397 : 0 : return sprintf(buf, "%d\n", eeepc->cpufv_disabled);
398 : : }
399 : :
400 : 0 : static ssize_t cpufv_disabled_store(struct device *dev,
401 : : struct device_attribute *attr,
402 : : const char *buf, size_t count)
403 : : {
404 [ # # ]: 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
405 : 0 : int rv, value;
406 : :
407 [ # # ]: 0 : rv = parse_arg(buf, &value);
408 : 0 : if (rv < 0)
409 : : return rv;
410 : :
411 [ # # # ]: 0 : switch (value) {
412 : 0 : case 0:
413 [ # # ]: 0 : if (eeepc->cpufv_disabled)
414 : 0 : pr_warn("cpufv enabled (not officially supported on this model)\n");
415 : 0 : eeepc->cpufv_disabled = false;
416 : 0 : return count;
417 : : case 1:
418 : : return -EPERM;
419 : 0 : default:
420 : 0 : return -EINVAL;
421 : : }
422 : : }
423 : :
424 : :
425 : : static DEVICE_ATTR_RW(cpufv);
426 : : static DEVICE_ATTR_RO(available_cpufv);
427 : : static DEVICE_ATTR_RW(cpufv_disabled);
428 : :
429 : : static struct attribute *platform_attributes[] = {
430 : : &dev_attr_camera.attr,
431 : : &dev_attr_cardr.attr,
432 : : &dev_attr_disp.attr,
433 : : &dev_attr_cpufv.attr,
434 : : &dev_attr_available_cpufv.attr,
435 : : &dev_attr_cpufv_disabled.attr,
436 : : NULL
437 : : };
438 : :
439 : : static const struct attribute_group platform_attribute_group = {
440 : : .attrs = platform_attributes
441 : : };
442 : :
443 : 0 : static int eeepc_platform_init(struct eeepc_laptop *eeepc)
444 : : {
445 : 0 : int result;
446 : :
447 : 0 : eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
448 [ # # ]: 0 : if (!eeepc->platform_device)
449 : : return -ENOMEM;
450 : 0 : platform_set_drvdata(eeepc->platform_device, eeepc);
451 : :
452 : 0 : result = platform_device_add(eeepc->platform_device);
453 [ # # ]: 0 : if (result)
454 : 0 : goto fail_platform_device;
455 : :
456 : 0 : result = sysfs_create_group(&eeepc->platform_device->dev.kobj,
457 : : &platform_attribute_group);
458 [ # # ]: 0 : if (result)
459 : 0 : goto fail_sysfs;
460 : : return 0;
461 : :
462 : : fail_sysfs:
463 : 0 : platform_device_del(eeepc->platform_device);
464 : 0 : fail_platform_device:
465 : 0 : platform_device_put(eeepc->platform_device);
466 : 0 : return result;
467 : : }
468 : :
469 : 0 : static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
470 : : {
471 : 0 : sysfs_remove_group(&eeepc->platform_device->dev.kobj,
472 : : &platform_attribute_group);
473 : 0 : platform_device_unregister(eeepc->platform_device);
474 : 0 : }
475 : :
476 : : /*
477 : : * LEDs
478 : : */
479 : : /*
480 : : * These functions actually update the LED's, and are called from a
481 : : * workqueue. By doing this as separate work rather than when the LED
482 : : * subsystem asks, we avoid messing with the Asus ACPI stuff during a
483 : : * potentially bad time, such as a timer interrupt.
484 : : */
485 : 0 : static void tpd_led_update(struct work_struct *work)
486 : : {
487 : 0 : struct eeepc_laptop *eeepc;
488 : :
489 : 0 : eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
490 : :
491 : 0 : set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);
492 : 0 : }
493 : :
494 : 0 : static void tpd_led_set(struct led_classdev *led_cdev,
495 : : enum led_brightness value)
496 : : {
497 : 0 : struct eeepc_laptop *eeepc;
498 : :
499 : 0 : eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
500 : :
501 : 0 : eeepc->tpd_led_wk = (value > 0) ? 1 : 0;
502 : 0 : queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
503 : 0 : }
504 : :
505 : 0 : static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
506 : : {
507 : 0 : struct eeepc_laptop *eeepc;
508 : :
509 : 0 : eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
510 : :
511 : 0 : return get_acpi(eeepc, CM_ASL_TPD);
512 : : }
513 : :
514 : 0 : static int eeepc_led_init(struct eeepc_laptop *eeepc)
515 : : {
516 : 0 : int rv;
517 : :
518 [ # # ]: 0 : if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
519 : : return 0;
520 : :
521 : 0 : eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
522 [ # # ]: 0 : if (!eeepc->led_workqueue)
523 : : return -ENOMEM;
524 : 0 : INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
525 : :
526 : 0 : eeepc->tpd_led.name = "eeepc::touchpad";
527 : 0 : eeepc->tpd_led.brightness_set = tpd_led_set;
528 [ # # ]: 0 : if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */
529 : 0 : eeepc->tpd_led.brightness_get = tpd_led_get;
530 : 0 : eeepc->tpd_led.max_brightness = 1;
531 : :
532 : 0 : rv = led_classdev_register(&eeepc->platform_device->dev,
533 : : &eeepc->tpd_led);
534 [ # # ]: 0 : if (rv) {
535 : 0 : destroy_workqueue(eeepc->led_workqueue);
536 : 0 : return rv;
537 : : }
538 : :
539 : : return 0;
540 : : }
541 : :
542 : 0 : static void eeepc_led_exit(struct eeepc_laptop *eeepc)
543 : : {
544 [ # # # # ]: 0 : if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
545 : 0 : led_classdev_unregister(&eeepc->tpd_led);
546 [ # # ]: 0 : if (eeepc->led_workqueue)
547 : 0 : destroy_workqueue(eeepc->led_workqueue);
548 : 0 : }
549 : :
550 : :
551 : : /*
552 : : * PCI hotplug (for wlan rfkill)
553 : : */
554 : 0 : static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
555 : : {
556 [ # # ]: 0 : if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
557 : 0 : return false;
558 : : return true;
559 : : }
560 : :
561 : 0 : static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
562 : : {
563 : 0 : struct pci_dev *port;
564 : 0 : struct pci_dev *dev;
565 : 0 : struct pci_bus *bus;
566 : 0 : bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
567 : 0 : bool absent;
568 : 0 : u32 l;
569 : :
570 [ # # ]: 0 : if (eeepc->wlan_rfkill)
571 : 0 : rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
572 : :
573 : 0 : mutex_lock(&eeepc->hotplug_lock);
574 : 0 : pci_lock_rescan_remove();
575 : :
576 [ # # ]: 0 : if (!eeepc->hotplug_slot.ops)
577 : 0 : goto out_unlock;
578 : :
579 : 0 : port = acpi_get_pci_dev(handle);
580 [ # # ]: 0 : if (!port) {
581 : 0 : pr_warn("Unable to find port\n");
582 : 0 : goto out_unlock;
583 : : }
584 : :
585 : 0 : bus = port->subordinate;
586 : :
587 [ # # ]: 0 : if (!bus) {
588 : 0 : pr_warn("Unable to find PCI bus 1?\n");
589 : 0 : goto out_put_dev;
590 : : }
591 : :
592 [ # # ]: 0 : if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
593 : 0 : pr_err("Unable to read PCI config space?\n");
594 : 0 : goto out_put_dev;
595 : : }
596 : :
597 : 0 : absent = (l == 0xffffffff);
598 : :
599 [ # # ]: 0 : if (blocked != absent) {
600 [ # # # # ]: 0 : pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
601 : : blocked ? "blocked" : "unblocked",
602 : : absent ? "absent" : "present");
603 : 0 : pr_warn("skipped wireless hotplug as probably inappropriate for this model\n");
604 : 0 : goto out_put_dev;
605 : : }
606 : :
607 [ # # ]: 0 : if (!blocked) {
608 : 0 : dev = pci_get_slot(bus, 0);
609 [ # # ]: 0 : if (dev) {
610 : : /* Device already present */
611 : 0 : pci_dev_put(dev);
612 : 0 : goto out_put_dev;
613 : : }
614 : 0 : dev = pci_scan_single_device(bus, 0);
615 [ # # ]: 0 : if (dev) {
616 : 0 : pci_bus_assign_resources(bus);
617 : 0 : pci_bus_add_device(dev);
618 : : }
619 : : } else {
620 : 0 : dev = pci_get_slot(bus, 0);
621 [ # # ]: 0 : if (dev) {
622 : 0 : pci_stop_and_remove_bus_device(dev);
623 : 0 : pci_dev_put(dev);
624 : : }
625 : : }
626 : 0 : out_put_dev:
627 : 0 : pci_dev_put(port);
628 : :
629 : 0 : out_unlock:
630 : 0 : pci_unlock_rescan_remove();
631 : 0 : mutex_unlock(&eeepc->hotplug_lock);
632 : 0 : }
633 : :
634 : 0 : static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node)
635 : : {
636 : 0 : acpi_status status = AE_OK;
637 : 0 : acpi_handle handle;
638 : :
639 : 0 : status = acpi_get_handle(NULL, node, &handle);
640 : :
641 [ # # ]: 0 : if (ACPI_SUCCESS(status))
642 : 0 : eeepc_rfkill_hotplug(eeepc, handle);
643 : 0 : }
644 : :
645 : 0 : static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
646 : : {
647 : 0 : struct eeepc_laptop *eeepc = data;
648 : :
649 [ # # ]: 0 : if (event != ACPI_NOTIFY_BUS_CHECK)
650 : : return;
651 : :
652 : 0 : eeepc_rfkill_hotplug(eeepc, handle);
653 : : }
654 : :
655 : 0 : static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
656 : : char *node)
657 : : {
658 : 0 : acpi_status status;
659 : 0 : acpi_handle handle;
660 : :
661 : 0 : status = acpi_get_handle(NULL, node, &handle);
662 : :
663 [ # # ]: 0 : if (ACPI_FAILURE(status))
664 : : return -ENODEV;
665 : :
666 : 0 : status = acpi_install_notify_handler(handle,
667 : : ACPI_SYSTEM_NOTIFY,
668 : : eeepc_rfkill_notify,
669 : : eeepc);
670 [ # # ]: 0 : if (ACPI_FAILURE(status))
671 : 0 : pr_warn("Failed to register notify on %s\n", node);
672 : :
673 : : /*
674 : : * Refresh pci hotplug in case the rfkill state was
675 : : * changed during setup.
676 : : */
677 : 0 : eeepc_rfkill_hotplug(eeepc, handle);
678 : 0 : return 0;
679 : : }
680 : :
681 : 0 : static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
682 : : char *node)
683 : : {
684 : 0 : acpi_status status = AE_OK;
685 : 0 : acpi_handle handle;
686 : :
687 : 0 : status = acpi_get_handle(NULL, node, &handle);
688 : :
689 [ # # ]: 0 : if (ACPI_FAILURE(status))
690 : 0 : return;
691 : :
692 : 0 : status = acpi_remove_notify_handler(handle,
693 : : ACPI_SYSTEM_NOTIFY,
694 : : eeepc_rfkill_notify);
695 [ # # ]: 0 : if (ACPI_FAILURE(status))
696 : 0 : pr_err("Error removing rfkill notify handler %s\n",
697 : : node);
698 : : /*
699 : : * Refresh pci hotplug in case the rfkill
700 : : * state was changed after
701 : : * eeepc_unregister_rfkill_notifier()
702 : : */
703 : 0 : eeepc_rfkill_hotplug(eeepc, handle);
704 : : }
705 : :
706 : 0 : static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
707 : : u8 *value)
708 : : {
709 : 0 : struct eeepc_laptop *eeepc;
710 : 0 : int val;
711 : :
712 : 0 : eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot);
713 : 0 : val = get_acpi(eeepc, CM_ASL_WLAN);
714 : :
715 [ # # ]: 0 : if (val == 1 || val == 0)
716 : 0 : *value = val;
717 : : else
718 : : return -EINVAL;
719 : :
720 : 0 : return 0;
721 : : }
722 : :
723 : : static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
724 : : .get_adapter_status = eeepc_get_adapter_status,
725 : : .get_power_status = eeepc_get_adapter_status,
726 : : };
727 : :
728 : 0 : static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
729 : : {
730 : 0 : int ret = -ENOMEM;
731 : 0 : struct pci_bus *bus = pci_find_bus(0, 1);
732 : :
733 [ # # ]: 0 : if (!bus) {
734 : 0 : pr_err("Unable to find wifi PCI bus\n");
735 : 0 : return -ENODEV;
736 : : }
737 : :
738 : 0 : eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops;
739 : :
740 : 0 : ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
741 [ # # ]: 0 : if (ret) {
742 : 0 : pr_err("Unable to register hotplug slot - %d\n", ret);
743 : 0 : goto error_register;
744 : : }
745 : :
746 : : return 0;
747 : :
748 : : error_register:
749 : 0 : eeepc->hotplug_slot.ops = NULL;
750 : 0 : return ret;
751 : : }
752 : :
753 : : /*
754 : : * Rfkill devices
755 : : */
756 : 0 : static int eeepc_rfkill_set(void *data, bool blocked)
757 : : {
758 : 0 : acpi_handle handle = data;
759 : :
760 : 0 : return write_acpi_int(handle, NULL, !blocked);
761 : : }
762 : :
763 : : static const struct rfkill_ops eeepc_rfkill_ops = {
764 : : .set_block = eeepc_rfkill_set,
765 : : };
766 : :
767 : 0 : static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
768 : : struct rfkill **rfkill,
769 : : const char *name,
770 : : enum rfkill_type type, int cm)
771 : : {
772 : 0 : acpi_handle handle;
773 : 0 : int result;
774 : :
775 : 0 : result = acpi_setter_handle(eeepc, cm, &handle);
776 [ # # ]: 0 : if (result < 0)
777 : : return result;
778 : :
779 : 0 : *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
780 : : &eeepc_rfkill_ops, handle);
781 : :
782 [ # # ]: 0 : if (!*rfkill)
783 : : return -EINVAL;
784 : :
785 : 0 : rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
786 : 0 : result = rfkill_register(*rfkill);
787 [ # # ]: 0 : if (result) {
788 : 0 : rfkill_destroy(*rfkill);
789 : 0 : *rfkill = NULL;
790 : 0 : return result;
791 : : }
792 : : return 0;
793 : : }
794 : :
795 : : static char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5";
796 : : static char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6";
797 : : static char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7";
798 : :
799 : 0 : static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
800 : : {
801 : 0 : eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
802 : 0 : eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
803 : 0 : eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
804 [ # # ]: 0 : if (eeepc->wlan_rfkill) {
805 : 0 : rfkill_unregister(eeepc->wlan_rfkill);
806 : 0 : rfkill_destroy(eeepc->wlan_rfkill);
807 : 0 : eeepc->wlan_rfkill = NULL;
808 : : }
809 : :
810 [ # # ]: 0 : if (eeepc->hotplug_slot.ops)
811 : 0 : pci_hp_deregister(&eeepc->hotplug_slot);
812 : :
813 [ # # ]: 0 : if (eeepc->bluetooth_rfkill) {
814 : 0 : rfkill_unregister(eeepc->bluetooth_rfkill);
815 : 0 : rfkill_destroy(eeepc->bluetooth_rfkill);
816 : 0 : eeepc->bluetooth_rfkill = NULL;
817 : : }
818 [ # # ]: 0 : if (eeepc->wwan3g_rfkill) {
819 : 0 : rfkill_unregister(eeepc->wwan3g_rfkill);
820 : 0 : rfkill_destroy(eeepc->wwan3g_rfkill);
821 : 0 : eeepc->wwan3g_rfkill = NULL;
822 : : }
823 [ # # ]: 0 : if (eeepc->wimax_rfkill) {
824 : 0 : rfkill_unregister(eeepc->wimax_rfkill);
825 : 0 : rfkill_destroy(eeepc->wimax_rfkill);
826 : 0 : eeepc->wimax_rfkill = NULL;
827 : : }
828 : 0 : }
829 : :
830 : 0 : static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
831 : : {
832 : 0 : int result = 0;
833 : :
834 : 0 : mutex_init(&eeepc->hotplug_lock);
835 : :
836 : 0 : result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
837 : : "eeepc-wlan", RFKILL_TYPE_WLAN,
838 : : CM_ASL_WLAN);
839 : :
840 [ # # ]: 0 : if (result && result != -ENODEV)
841 : 0 : goto exit;
842 : :
843 : 0 : result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
844 : : "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
845 : : CM_ASL_BLUETOOTH);
846 : :
847 [ # # ]: 0 : if (result && result != -ENODEV)
848 : 0 : goto exit;
849 : :
850 : 0 : result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
851 : : "eeepc-wwan3g", RFKILL_TYPE_WWAN,
852 : : CM_ASL_3G);
853 : :
854 [ # # ]: 0 : if (result && result != -ENODEV)
855 : 0 : goto exit;
856 : :
857 : 0 : result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
858 : : "eeepc-wimax", RFKILL_TYPE_WIMAX,
859 : : CM_ASL_WIMAX);
860 : :
861 [ # # ]: 0 : if (result && result != -ENODEV)
862 : 0 : goto exit;
863 : :
864 [ # # ]: 0 : if (eeepc->hotplug_disabled)
865 : : return 0;
866 : :
867 : 0 : result = eeepc_setup_pci_hotplug(eeepc);
868 : : /*
869 : : * If we get -EBUSY then something else is handling the PCI hotplug -
870 : : * don't fail in this case
871 : : */
872 [ # # ]: 0 : if (result == -EBUSY)
873 : 0 : result = 0;
874 : :
875 : 0 : eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
876 : 0 : eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
877 : 0 : eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
878 : :
879 : 0 : exit:
880 [ # # ]: 0 : if (result && result != -ENODEV)
881 : 0 : eeepc_rfkill_exit(eeepc);
882 : : return result;
883 : : }
884 : :
885 : : /*
886 : : * Platform driver - hibernate/resume callbacks
887 : : */
888 : 0 : static int eeepc_hotk_thaw(struct device *device)
889 : : {
890 [ # # ]: 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(device);
891 : :
892 [ # # ]: 0 : if (eeepc->wlan_rfkill) {
893 : 0 : int wlan;
894 : :
895 : : /*
896 : : * Work around bios bug - acpi _PTS turns off the wireless led
897 : : * during suspend. Normally it restores it on resume, but
898 : : * we should kick it ourselves in case hibernation is aborted.
899 : : */
900 : 0 : wlan = get_acpi(eeepc, CM_ASL_WLAN);
901 [ # # ]: 0 : if (wlan >= 0)
902 : 0 : set_acpi(eeepc, CM_ASL_WLAN, wlan);
903 : : }
904 : :
905 : 0 : return 0;
906 : : }
907 : :
908 : 0 : static int eeepc_hotk_restore(struct device *device)
909 : : {
910 [ # # ]: 0 : struct eeepc_laptop *eeepc = dev_get_drvdata(device);
911 : :
912 : : /* Refresh both wlan rfkill state and pci hotplug */
913 [ # # ]: 0 : if (eeepc->wlan_rfkill) {
914 : 0 : eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1);
915 : 0 : eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2);
916 : 0 : eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3);
917 : : }
918 : :
919 [ # # ]: 0 : if (eeepc->bluetooth_rfkill)
920 : 0 : rfkill_set_sw_state(eeepc->bluetooth_rfkill,
921 : 0 : get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);
922 [ # # ]: 0 : if (eeepc->wwan3g_rfkill)
923 : 0 : rfkill_set_sw_state(eeepc->wwan3g_rfkill,
924 : 0 : get_acpi(eeepc, CM_ASL_3G) != 1);
925 [ # # ]: 0 : if (eeepc->wimax_rfkill)
926 : 0 : rfkill_set_sw_state(eeepc->wimax_rfkill,
927 : 0 : get_acpi(eeepc, CM_ASL_WIMAX) != 1);
928 : :
929 : 0 : return 0;
930 : : }
931 : :
932 : : static const struct dev_pm_ops eeepc_pm_ops = {
933 : : .thaw = eeepc_hotk_thaw,
934 : : .restore = eeepc_hotk_restore,
935 : : };
936 : :
937 : : static struct platform_driver platform_driver = {
938 : : .driver = {
939 : : .name = EEEPC_LAPTOP_FILE,
940 : : .pm = &eeepc_pm_ops,
941 : : }
942 : : };
943 : :
944 : : /*
945 : : * Hwmon device
946 : : */
947 : :
948 : : #define EEEPC_EC_SC00 0x61
949 : : #define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
950 : : #define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
951 : : #define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
952 : :
953 : : #define EEEPC_EC_SFB0 0xD0
954 : : #define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */
955 : :
956 : 0 : static inline int eeepc_pwm_to_lmsensors(int value)
957 : : {
958 : 0 : return value * 255 / 100;
959 : : }
960 : :
961 : 0 : static inline int eeepc_lmsensors_to_pwm(int value)
962 : : {
963 : 0 : value = clamp_val(value, 0, 255);
964 : 0 : return value * 100 / 255;
965 : : }
966 : :
967 : 0 : static int eeepc_get_fan_pwm(void)
968 : : {
969 : 0 : u8 value = 0;
970 : :
971 : 0 : ec_read(EEEPC_EC_FAN_PWM, &value);
972 : 0 : return eeepc_pwm_to_lmsensors(value);
973 : : }
974 : :
975 : 0 : static void eeepc_set_fan_pwm(int value)
976 : : {
977 : 0 : value = eeepc_lmsensors_to_pwm(value);
978 : 0 : ec_write(EEEPC_EC_FAN_PWM, value);
979 : 0 : }
980 : :
981 : 0 : static int eeepc_get_fan_rpm(void)
982 : : {
983 : 0 : u8 high = 0;
984 : 0 : u8 low = 0;
985 : :
986 : 0 : ec_read(EEEPC_EC_FAN_HRPM, &high);
987 : 0 : ec_read(EEEPC_EC_FAN_LRPM, &low);
988 : 0 : return high << 8 | low;
989 : : }
990 : :
991 : : #define EEEPC_EC_FAN_CTRL_BIT 0x02
992 : : #define EEEPC_FAN_CTRL_MANUAL 1
993 : : #define EEEPC_FAN_CTRL_AUTO 2
994 : :
995 : 0 : static int eeepc_get_fan_ctrl(void)
996 : : {
997 : 0 : u8 value = 0;
998 : :
999 : 0 : ec_read(EEEPC_EC_FAN_CTRL, &value);
1000 [ # # ]: 0 : if (value & EEEPC_EC_FAN_CTRL_BIT)
1001 : : return EEEPC_FAN_CTRL_MANUAL;
1002 : : else
1003 : 0 : return EEEPC_FAN_CTRL_AUTO;
1004 : : }
1005 : :
1006 : 0 : static void eeepc_set_fan_ctrl(int manual)
1007 : : {
1008 : 0 : u8 value = 0;
1009 : :
1010 : 0 : ec_read(EEEPC_EC_FAN_CTRL, &value);
1011 [ # # ]: 0 : if (manual == EEEPC_FAN_CTRL_MANUAL)
1012 : 0 : value |= EEEPC_EC_FAN_CTRL_BIT;
1013 : : else
1014 : 0 : value &= ~EEEPC_EC_FAN_CTRL_BIT;
1015 : 0 : ec_write(EEEPC_EC_FAN_CTRL, value);
1016 : 0 : }
1017 : :
1018 : 0 : static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
1019 : : {
1020 : 0 : int rv, value;
1021 : :
1022 : 0 : rv = parse_arg(buf, &value);
1023 : 0 : if (rv < 0)
1024 : : return rv;
1025 : 0 : set(value);
1026 : 0 : return count;
1027 : : }
1028 : :
1029 : 0 : static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
1030 : : {
1031 : 0 : return sprintf(buf, "%d\n", get());
1032 : : }
1033 : :
1034 : : #define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \
1035 : : static ssize_t _name##_show(struct device *dev, \
1036 : : struct device_attribute *attr, \
1037 : : char *buf) \
1038 : : { \
1039 : : return show_sys_hwmon(_get, buf); \
1040 : : }
1041 : :
1042 : : #define EEEPC_SENSOR_STORE_FUNC(_name, _set) \
1043 : : static ssize_t _name##_store(struct device *dev, \
1044 : : struct device_attribute *attr, \
1045 : : const char *buf, size_t count) \
1046 : : { \
1047 : : return store_sys_hwmon(_set, buf, count); \
1048 : : }
1049 : :
1050 : : #define EEEPC_CREATE_SENSOR_ATTR_RW(_name, _get, _set) \
1051 : : EEEPC_SENSOR_SHOW_FUNC(_name, _get) \
1052 : : EEEPC_SENSOR_STORE_FUNC(_name, _set) \
1053 : : static DEVICE_ATTR_RW(_name)
1054 : :
1055 : : #define EEEPC_CREATE_SENSOR_ATTR_RO(_name, _get) \
1056 : : EEEPC_SENSOR_SHOW_FUNC(_name, _get) \
1057 : : static DEVICE_ATTR_RO(_name)
1058 : :
1059 : 0 : EEEPC_CREATE_SENSOR_ATTR_RO(fan1_input, eeepc_get_fan_rpm);
1060 [ # # ]: 0 : EEEPC_CREATE_SENSOR_ATTR_RW(pwm1, eeepc_get_fan_pwm,
1061 : : eeepc_set_fan_pwm);
1062 [ # # ]: 0 : EEEPC_CREATE_SENSOR_ATTR_RW(pwm1_enable, eeepc_get_fan_ctrl,
1063 : : eeepc_set_fan_ctrl);
1064 : :
1065 : : static struct attribute *hwmon_attrs[] = {
1066 : : &dev_attr_pwm1.attr,
1067 : : &dev_attr_fan1_input.attr,
1068 : : &dev_attr_pwm1_enable.attr,
1069 : : NULL
1070 : : };
1071 : : ATTRIBUTE_GROUPS(hwmon);
1072 : :
1073 : : static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
1074 : : {
1075 : : struct device *dev = &eeepc->platform_device->dev;
1076 : : struct device *hwmon;
1077 : :
1078 : : hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL,
1079 : : hwmon_groups);
1080 : : if (IS_ERR(hwmon)) {
1081 : : pr_err("Could not register eeepc hwmon device\n");
1082 : : return PTR_ERR(hwmon);
1083 : : }
1084 : : return 0;
1085 : : }
1086 : :
1087 : : /*
1088 : : * Backlight device
1089 : : */
1090 : 0 : static int read_brightness(struct backlight_device *bd)
1091 : : {
1092 : 0 : struct eeepc_laptop *eeepc = bl_get_data(bd);
1093 : :
1094 : 0 : return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
1095 : : }
1096 : :
1097 : 0 : static int set_brightness(struct backlight_device *bd, int value)
1098 : : {
1099 : 0 : struct eeepc_laptop *eeepc = bl_get_data(bd);
1100 : :
1101 : 0 : return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
1102 : : }
1103 : :
1104 : 0 : static int update_bl_status(struct backlight_device *bd)
1105 : : {
1106 : 0 : return set_brightness(bd, bd->props.brightness);
1107 : : }
1108 : :
1109 : : static const struct backlight_ops eeepcbl_ops = {
1110 : : .get_brightness = read_brightness,
1111 : : .update_status = update_bl_status,
1112 : : };
1113 : :
1114 : 0 : static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
1115 : : {
1116 : 0 : struct backlight_device *bd = eeepc->backlight_device;
1117 : 0 : int old = bd->props.brightness;
1118 : :
1119 : 0 : backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1120 : :
1121 : 0 : return old;
1122 : : }
1123 : :
1124 : 0 : static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
1125 : : {
1126 : 0 : struct backlight_properties props;
1127 : 0 : struct backlight_device *bd;
1128 : :
1129 : 0 : memset(&props, 0, sizeof(struct backlight_properties));
1130 : 0 : props.type = BACKLIGHT_PLATFORM;
1131 : 0 : props.max_brightness = 15;
1132 : 0 : bd = backlight_device_register(EEEPC_LAPTOP_FILE,
1133 : 0 : &eeepc->platform_device->dev, eeepc,
1134 : : &eeepcbl_ops, &props);
1135 [ # # ]: 0 : if (IS_ERR(bd)) {
1136 : 0 : pr_err("Could not register eeepc backlight device\n");
1137 : 0 : eeepc->backlight_device = NULL;
1138 : 0 : return PTR_ERR(bd);
1139 : : }
1140 : 0 : eeepc->backlight_device = bd;
1141 : 0 : bd->props.brightness = read_brightness(bd);
1142 : 0 : bd->props.power = FB_BLANK_UNBLANK;
1143 : 0 : backlight_update_status(bd);
1144 : 0 : return 0;
1145 : : }
1146 : :
1147 : 0 : static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
1148 : : {
1149 : 0 : backlight_device_unregister(eeepc->backlight_device);
1150 : 0 : eeepc->backlight_device = NULL;
1151 : 0 : }
1152 : :
1153 : :
1154 : : /*
1155 : : * Input device (i.e. hotkeys)
1156 : : */
1157 : : static int eeepc_input_init(struct eeepc_laptop *eeepc)
1158 : : {
1159 : : struct input_dev *input;
1160 : : int error;
1161 : :
1162 : : input = input_allocate_device();
1163 : : if (!input)
1164 : : return -ENOMEM;
1165 : :
1166 : : input->name = "Asus EeePC extra buttons";
1167 : : input->phys = EEEPC_LAPTOP_FILE "/input0";
1168 : : input->id.bustype = BUS_HOST;
1169 : : input->dev.parent = &eeepc->platform_device->dev;
1170 : :
1171 : : error = sparse_keymap_setup(input, eeepc_keymap, NULL);
1172 : : if (error) {
1173 : : pr_err("Unable to setup input device keymap\n");
1174 : : goto err_free_dev;
1175 : : }
1176 : :
1177 : : error = input_register_device(input);
1178 : : if (error) {
1179 : : pr_err("Unable to register input device\n");
1180 : : goto err_free_dev;
1181 : : }
1182 : :
1183 : : eeepc->inputdev = input;
1184 : : return 0;
1185 : :
1186 : : err_free_dev:
1187 : : input_free_device(input);
1188 : : return error;
1189 : : }
1190 : :
1191 : 0 : static void eeepc_input_exit(struct eeepc_laptop *eeepc)
1192 : : {
1193 : 0 : if (eeepc->inputdev)
1194 : 0 : input_unregister_device(eeepc->inputdev);
1195 : 0 : eeepc->inputdev = NULL;
1196 : 0 : }
1197 : :
1198 : : /*
1199 : : * ACPI driver
1200 : : */
1201 : : static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
1202 : : {
1203 : : if (!eeepc->inputdev)
1204 : : return;
1205 : : if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))
1206 : : pr_info("Unknown key %x pressed\n", event);
1207 : : }
1208 : :
1209 : 0 : static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
1210 : : {
1211 [ # # ]: 0 : struct eeepc_laptop *eeepc = acpi_driver_data(device);
1212 : 0 : int old_brightness, new_brightness;
1213 : 0 : u16 count;
1214 : :
1215 [ # # ]: 0 : if (event > ACPI_MAX_SYS_NOTIFY)
1216 : : return;
1217 : 0 : count = eeepc->event_count[event % 128]++;
1218 [ # # ]: 0 : acpi_bus_generate_netlink_event(device->pnp.device_class,
1219 : : dev_name(&device->dev), event,
1220 : : count);
1221 : :
1222 : : /* Brightness events are special */
1223 [ # # ]: 0 : if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) {
1224 : 0 : eeepc_input_notify(eeepc, event);
1225 : 0 : return;
1226 : : }
1227 : :
1228 : : /* Ignore them completely if the acpi video driver is used */
1229 [ # # ]: 0 : if (!eeepc->backlight_device)
1230 : : return;
1231 : :
1232 : : /* Update the backlight device. */
1233 : 0 : old_brightness = eeepc_backlight_notify(eeepc);
1234 : :
1235 : : /* Convert event to keypress (obsolescent hack) */
1236 : 0 : new_brightness = event - NOTIFY_BRN_MIN;
1237 : :
1238 [ # # ]: 0 : if (new_brightness < old_brightness) {
1239 : : event = NOTIFY_BRN_MIN; /* brightness down */
1240 [ # # ]: 0 : } else if (new_brightness > old_brightness) {
1241 : 0 : event = NOTIFY_BRN_MAX; /* brightness up */
1242 : : } else {
1243 : : /*
1244 : : * no change in brightness - already at min/max,
1245 : : * event will be desired value (or else ignored)
1246 : : */
1247 : 0 : }
1248 : 0 : eeepc_input_notify(eeepc, event);
1249 : : }
1250 : :
1251 : : static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
1252 : : {
1253 : : const char *model;
1254 : :
1255 : : model = dmi_get_system_info(DMI_PRODUCT_NAME);
1256 : : if (!model)
1257 : : return;
1258 : :
1259 : : /*
1260 : : * Blacklist for setting cpufv (cpu speed).
1261 : : *
1262 : : * EeePC 4G ("701") implements CFVS, but it is not supported
1263 : : * by the pre-installed OS, and the original option to change it
1264 : : * in the BIOS setup screen was removed in later versions.
1265 : : *
1266 : : * Judging by the lack of "Super Hybrid Engine" on Asus product pages,
1267 : : * this applies to all "701" models (4G/4G Surf/2G Surf).
1268 : : *
1269 : : * So Asus made a deliberate decision not to support it on this model.
1270 : : * We have several reports that using it can cause the system to hang
1271 : : *
1272 : : * The hang has also been reported on a "702" (Model name "8G"?).
1273 : : *
1274 : : * We avoid dmi_check_system() / dmi_match(), because they use
1275 : : * substring matching. We don't want to affect the "701SD"
1276 : : * and "701SDX" models, because they do support S.H.E.
1277 : : */
1278 : : if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
1279 : : eeepc->cpufv_disabled = true;
1280 : : pr_info("model %s does not officially support setting cpu speed\n",
1281 : : model);
1282 : : pr_info("cpufv disabled to avoid instability\n");
1283 : : }
1284 : :
1285 : : /*
1286 : : * Blacklist for wlan hotplug
1287 : : *
1288 : : * Eeepc 1005HA doesn't work like others models and don't need the
1289 : : * hotplug code. In fact, current hotplug code seems to unplug another
1290 : : * device...
1291 : : */
1292 : : if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||
1293 : : strcmp(model, "1005PE") == 0) {
1294 : : eeepc->hotplug_disabled = true;
1295 : : pr_info("wlan hotplug disabled\n");
1296 : : }
1297 : : }
1298 : :
1299 : : static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
1300 : : {
1301 : : int dummy;
1302 : :
1303 : : /* Some BIOSes do not report cm although it is available.
1304 : : Check if cm_getv[cm] works and, if yes, assume cm should be set. */
1305 : : if (!(eeepc->cm_supported & (1 << cm))
1306 : : && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
1307 : : pr_info("%s (%x) not reported by BIOS, enabling anyway\n",
1308 : : name, 1 << cm);
1309 : : eeepc->cm_supported |= 1 << cm;
1310 : : }
1311 : : }
1312 : :
1313 : 0 : static void cmsg_quirks(struct eeepc_laptop *eeepc)
1314 : : {
1315 : 0 : cmsg_quirk(eeepc, CM_ASL_LID, "LID");
1316 : 0 : cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");
1317 : 0 : cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");
1318 : 0 : cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
1319 : 0 : }
1320 : :
1321 : 0 : static int eeepc_acpi_init(struct eeepc_laptop *eeepc)
1322 : : {
1323 : 0 : unsigned int init_flags;
1324 : 0 : int result;
1325 : :
1326 : 0 : result = acpi_bus_get_status(eeepc->device);
1327 [ # # ]: 0 : if (result)
1328 : : return result;
1329 [ # # ]: 0 : if (!eeepc->device->status.present) {
1330 : 0 : pr_err("Hotkey device not present, aborting\n");
1331 : 0 : return -ENODEV;
1332 : : }
1333 : :
1334 : 0 : init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1335 : 0 : pr_notice("Hotkey init flags 0x%x\n", init_flags);
1336 : :
1337 : 0 : if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
1338 : 0 : pr_err("Hotkey initialization failed\n");
1339 : 0 : return -ENODEV;
1340 : : }
1341 : :
1342 : : /* get control methods supported */
1343 : 0 : if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
1344 : 0 : pr_err("Get control methods supported failed\n");
1345 : 0 : return -ENODEV;
1346 : : }
1347 : 0 : cmsg_quirks(eeepc);
1348 : 0 : pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
1349 : :
1350 : 0 : return 0;
1351 : : }
1352 : :
1353 : 0 : static void eeepc_enable_camera(struct eeepc_laptop *eeepc)
1354 : : {
1355 : : /*
1356 : : * If the following call to set_acpi() fails, it's because there's no
1357 : : * camera so we can ignore the error.
1358 : : */
1359 [ # # ]: 0 : if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
1360 : 0 : set_acpi(eeepc, CM_ASL_CAMERA, 1);
1361 : 0 : }
1362 : :
1363 : : static bool eeepc_device_present;
1364 : :
1365 : 0 : static int eeepc_acpi_add(struct acpi_device *device)
1366 : : {
1367 : 0 : struct eeepc_laptop *eeepc;
1368 : 0 : int result;
1369 : :
1370 : 0 : pr_notice(EEEPC_LAPTOP_NAME "\n");
1371 : 0 : eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
1372 [ # # ]: 0 : if (!eeepc)
1373 : : return -ENOMEM;
1374 : 0 : eeepc->handle = device->handle;
1375 : 0 : strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
1376 : 0 : strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
1377 : 0 : device->driver_data = eeepc;
1378 : 0 : eeepc->device = device;
1379 : :
1380 : 0 : eeepc->hotplug_disabled = hotplug_disabled;
1381 : :
1382 : 0 : eeepc_dmi_check(eeepc);
1383 : :
1384 : 0 : result = eeepc_acpi_init(eeepc);
1385 [ # # ]: 0 : if (result)
1386 : 0 : goto fail_platform;
1387 : 0 : eeepc_enable_camera(eeepc);
1388 : :
1389 : : /*
1390 : : * Register the platform device first. It is used as a parent for the
1391 : : * sub-devices below.
1392 : : *
1393 : : * Note that if there are multiple instances of this ACPI device it
1394 : : * will bail out, because the platform device is registered with a
1395 : : * fixed name. Of course it doesn't make sense to have more than one,
1396 : : * and machine-specific scripts find the fixed name convenient. But
1397 : : * It's also good for us to exclude multiple instances because both
1398 : : * our hwmon and our wlan rfkill subdevice use global ACPI objects
1399 : : * (the EC and the wlan PCI slot respectively).
1400 : : */
1401 : 0 : result = eeepc_platform_init(eeepc);
1402 [ # # ]: 0 : if (result)
1403 : 0 : goto fail_platform;
1404 : :
1405 [ # # ]: 0 : if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1406 : 0 : result = eeepc_backlight_init(eeepc);
1407 [ # # ]: 0 : if (result)
1408 : 0 : goto fail_backlight;
1409 : : }
1410 : :
1411 : 0 : result = eeepc_input_init(eeepc);
1412 [ # # ]: 0 : if (result)
1413 : 0 : goto fail_input;
1414 : :
1415 : 0 : result = eeepc_hwmon_init(eeepc);
1416 [ # # ]: 0 : if (result)
1417 : 0 : goto fail_hwmon;
1418 : :
1419 : 0 : result = eeepc_led_init(eeepc);
1420 [ # # ]: 0 : if (result)
1421 : 0 : goto fail_led;
1422 : :
1423 : 0 : result = eeepc_rfkill_init(eeepc);
1424 [ # # ]: 0 : if (result)
1425 : 0 : goto fail_rfkill;
1426 : :
1427 : 0 : eeepc_device_present = true;
1428 : 0 : return 0;
1429 : :
1430 : : fail_rfkill:
1431 : 0 : eeepc_led_exit(eeepc);
1432 : 0 : fail_led:
1433 : 0 : fail_hwmon:
1434 [ # # ]: 0 : eeepc_input_exit(eeepc);
1435 : 0 : fail_input:
1436 : 0 : eeepc_backlight_exit(eeepc);
1437 : 0 : fail_backlight:
1438 : 0 : eeepc_platform_exit(eeepc);
1439 : 0 : fail_platform:
1440 : 0 : kfree(eeepc);
1441 : :
1442 : 0 : return result;
1443 : : }
1444 : :
1445 : 0 : static int eeepc_acpi_remove(struct acpi_device *device)
1446 : : {
1447 : 0 : struct eeepc_laptop *eeepc = acpi_driver_data(device);
1448 : :
1449 : 0 : eeepc_backlight_exit(eeepc);
1450 : 0 : eeepc_rfkill_exit(eeepc);
1451 [ # # ]: 0 : eeepc_input_exit(eeepc);
1452 : 0 : eeepc_led_exit(eeepc);
1453 : 0 : eeepc_platform_exit(eeepc);
1454 : :
1455 : 0 : kfree(eeepc);
1456 : 0 : return 0;
1457 : : }
1458 : :
1459 : :
1460 : : static const struct acpi_device_id eeepc_device_ids[] = {
1461 : : {EEEPC_ACPI_HID, 0},
1462 : : {"", 0},
1463 : : };
1464 : : MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
1465 : :
1466 : : static struct acpi_driver eeepc_acpi_driver = {
1467 : : .name = EEEPC_LAPTOP_NAME,
1468 : : .class = EEEPC_ACPI_CLASS,
1469 : : .owner = THIS_MODULE,
1470 : : .ids = eeepc_device_ids,
1471 : : .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1472 : : .ops = {
1473 : : .add = eeepc_acpi_add,
1474 : : .remove = eeepc_acpi_remove,
1475 : : .notify = eeepc_acpi_notify,
1476 : : },
1477 : : };
1478 : :
1479 : :
1480 : 28 : static int __init eeepc_laptop_init(void)
1481 : : {
1482 : 28 : int result;
1483 : :
1484 : 28 : result = platform_driver_register(&platform_driver);
1485 [ + - ]: 28 : if (result < 0)
1486 : : return result;
1487 : :
1488 : 28 : result = acpi_bus_register_driver(&eeepc_acpi_driver);
1489 [ - + ]: 28 : if (result < 0)
1490 : 0 : goto fail_acpi_driver;
1491 : :
1492 [ + - ]: 28 : if (!eeepc_device_present) {
1493 : 28 : result = -ENODEV;
1494 : 28 : goto fail_no_device;
1495 : : }
1496 : :
1497 : : return 0;
1498 : :
1499 : : fail_no_device:
1500 : 28 : acpi_bus_unregister_driver(&eeepc_acpi_driver);
1501 : 28 : fail_acpi_driver:
1502 : 28 : platform_driver_unregister(&platform_driver);
1503 : 28 : return result;
1504 : : }
1505 : :
1506 : 0 : static void __exit eeepc_laptop_exit(void)
1507 : : {
1508 : 0 : acpi_bus_unregister_driver(&eeepc_acpi_driver);
1509 : 0 : platform_driver_unregister(&platform_driver);
1510 : 0 : }
1511 : :
1512 : : module_init(eeepc_laptop_init);
1513 : : module_exit(eeepc_laptop_exit);
|