blob: 91304342f8b6985ae940ddc07d18f6d7d6bfb46b [file] [log] [blame]
Eric Coopere59f8792008-03-13 12:55:46 +01001/*
2 * eepc-laptop.c - Asus Eee PC extras
3 *
4 * Based on asus_acpi.c as patched for the Eee PC by Asus:
5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
6 * Based on eee.c from eeepc-linux
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
Joe Perches19b53282009-06-25 13:25:37 +020019#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
Eric Coopere59f8792008-03-13 12:55:46 +010021#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/types.h>
25#include <linux/platform_device.h>
Corentin Charya5fa4292008-03-13 12:56:37 +010026#include <linux/backlight.h>
27#include <linux/fb.h>
Corentin Charye1faa9d2008-03-13 12:57:18 +010028#include <linux/hwmon.h>
29#include <linux/hwmon-sysfs.h>
Eric Coopere59f8792008-03-13 12:55:46 +010030#include <acpi/acpi_drivers.h>
31#include <acpi/acpi_bus.h>
32#include <linux/uaccess.h>
Matthew Garretta195dcd2008-08-19 12:13:20 +010033#include <linux/input.h>
34#include <linux/rfkill.h>
Matthew Garrett57402942009-01-20 16:17:48 +010035#include <linux/pci.h>
Corentin Chary2b121bc2009-06-25 13:25:36 +020036#include <linux/pci_hotplug.h>
Corentin Chary3c0eb512009-12-03 07:44:52 +000037#include <linux/leds.h>
Eric Coopere59f8792008-03-13 12:55:46 +010038
39#define EEEPC_LAPTOP_VERSION "0.1"
40
41#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
42#define EEEPC_HOTK_FILE "eeepc"
43#define EEEPC_HOTK_CLASS "hotkey"
44#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
45#define EEEPC_HOTK_HID "ASUS010"
46
Eric Coopere59f8792008-03-13 12:55:46 +010047
48/*
49 * Definitions for Asus EeePC
50 */
51#define NOTIFY_WLAN_ON 0x10
Corentin Charya5fa4292008-03-13 12:56:37 +010052#define NOTIFY_BRN_MIN 0x20
53#define NOTIFY_BRN_MAX 0x2f
Eric Coopere59f8792008-03-13 12:55:46 +010054
55enum {
56 DISABLE_ASL_WLAN = 0x0001,
57 DISABLE_ASL_BLUETOOTH = 0x0002,
58 DISABLE_ASL_IRDA = 0x0004,
59 DISABLE_ASL_CAMERA = 0x0008,
60 DISABLE_ASL_TV = 0x0010,
61 DISABLE_ASL_GPS = 0x0020,
62 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
63 DISABLE_ASL_MODEM = 0x0080,
Corentin Charyb7b700d2009-06-16 19:28:52 +000064 DISABLE_ASL_CARDREADER = 0x0100,
65 DISABLE_ASL_3G = 0x0200,
66 DISABLE_ASL_WIMAX = 0x0400,
67 DISABLE_ASL_HWCF = 0x0800
Eric Coopere59f8792008-03-13 12:55:46 +010068};
69
70enum {
71 CM_ASL_WLAN = 0,
72 CM_ASL_BLUETOOTH,
73 CM_ASL_IRDA,
74 CM_ASL_1394,
75 CM_ASL_CAMERA,
76 CM_ASL_TV,
77 CM_ASL_GPS,
78 CM_ASL_DVDROM,
79 CM_ASL_DISPLAYSWITCH,
80 CM_ASL_PANELBRIGHT,
81 CM_ASL_BIOSFLASH,
82 CM_ASL_ACPIFLASH,
83 CM_ASL_CPUFV,
84 CM_ASL_CPUTEMPERATURE,
85 CM_ASL_FANCPU,
86 CM_ASL_FANCHASSIS,
87 CM_ASL_USBPORT1,
88 CM_ASL_USBPORT2,
89 CM_ASL_USBPORT3,
90 CM_ASL_MODEM,
91 CM_ASL_CARDREADER,
Corentin Charyb7b700d2009-06-16 19:28:52 +000092 CM_ASL_3G,
93 CM_ASL_WIMAX,
94 CM_ASL_HWCF,
95 CM_ASL_LID,
96 CM_ASL_TYPE,
97 CM_ASL_PANELPOWER, /*P901*/
98 CM_ASL_TPD
Eric Coopere59f8792008-03-13 12:55:46 +010099};
100
Adrian Bunk14109462008-06-25 19:25:47 +0300101static const char *cm_getv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000102 "WLDG", "BTHG", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100103 "CAMG", NULL, NULL, NULL,
104 NULL, "PBLG", NULL, NULL,
105 "CFVG", NULL, NULL, NULL,
106 "USBG", NULL, NULL, "MODG",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000107 "CRDG", "M3GG", "WIMG", "HWCF",
108 "LIDG", "TYPE", "PBPG", "TPDG"
Eric Coopere59f8792008-03-13 12:55:46 +0100109};
110
Adrian Bunk14109462008-06-25 19:25:47 +0300111static const char *cm_setv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000112 "WLDS", "BTHS", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100113 "CAMS", NULL, NULL, NULL,
114 "SDSP", "PBLS", "HDPS", NULL,
115 "CFVS", NULL, NULL, NULL,
116 "USBG", NULL, NULL, "MODS",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000117 "CRDS", "M3GS", "WIMS", NULL,
118 NULL, NULL, "PBPS", "TPDS"
Eric Coopere59f8792008-03-13 12:55:46 +0100119};
120
Corentin Charye1faa9d2008-03-13 12:57:18 +0100121#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
122
123#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
124#define EEEPC_EC_SC02 0x63
125#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
126#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
127#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
128#define EEEPC_EC_SFB3 0xD3
129
Eric Coopere59f8792008-03-13 12:55:46 +0100130/*
131 * This is the main structure, we can use it to store useful information
132 * about the hotk device
133 */
134struct eeepc_hotk {
135 struct acpi_device *device; /* the device we are in */
136 acpi_handle handle; /* the handle of the hotk device */
137 u32 cm_supported; /* the control methods supported
138 by this BIOS */
139 uint init_flag; /* Init flags */
140 u16 event_count[128]; /* count for each event */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100141 struct input_dev *inputdev;
142 u16 *keycode_map;
Corentin Chary7de39382009-06-25 13:25:38 +0200143 struct rfkill *wlan_rfkill;
144 struct rfkill *bluetooth_rfkill;
Corentin Chary3cd530b2009-06-25 13:25:42 +0200145 struct rfkill *wwan3g_rfkill;
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000146 struct rfkill *wimax_rfkill;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200147 struct hotplug_slot *hotplug_slot;
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000148 struct mutex hotplug_lock;
Eric Coopere59f8792008-03-13 12:55:46 +0100149};
150
151/* The actual device the driver binds to */
152static struct eeepc_hotk *ehotk;
153
154/* Platform device/driver */
Alan Jenkinsc200da52009-08-28 12:56:40 +0000155static int eeepc_hotk_thaw(struct device *device);
156static int eeepc_hotk_restore(struct device *device);
157
158static struct dev_pm_ops eeepc_pm_ops = {
159 .thaw = eeepc_hotk_thaw,
160 .restore = eeepc_hotk_restore,
161};
162
Eric Coopere59f8792008-03-13 12:55:46 +0100163static struct platform_driver platform_driver = {
164 .driver = {
165 .name = EEEPC_HOTK_FILE,
166 .owner = THIS_MODULE,
Alan Jenkinsc200da52009-08-28 12:56:40 +0000167 .pm = &eeepc_pm_ops,
Eric Coopere59f8792008-03-13 12:55:46 +0100168 }
169};
170
171static struct platform_device *platform_device;
172
Matthew Garretta195dcd2008-08-19 12:13:20 +0100173struct key_entry {
174 char type;
175 u8 code;
176 u16 keycode;
177};
178
179enum { KE_KEY, KE_END };
180
181static struct key_entry eeepc_keymap[] = {
182 /* Sleep already handled via generic ACPI code */
183 {KE_KEY, 0x10, KEY_WLAN },
Alan Jenkins978605c2009-04-27 09:23:39 +0200184 {KE_KEY, 0x11, KEY_WLAN },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100185 {KE_KEY, 0x12, KEY_PROG1 },
186 {KE_KEY, 0x13, KEY_MUTE },
187 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
188 {KE_KEY, 0x15, KEY_VOLUMEUP },
Matthew Garrettb5f6f262009-01-20 16:17:46 +0100189 {KE_KEY, 0x1a, KEY_COFFEE },
190 {KE_KEY, 0x1b, KEY_ZOOM },
191 {KE_KEY, 0x1c, KEY_PROG2 },
192 {KE_KEY, 0x1d, KEY_PROG3 },
Darren Salt64b86b62009-04-27 09:23:38 +0200193 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
194 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100195 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
196 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
197 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
198 {KE_END, 0},
199};
200
Eric Coopere59f8792008-03-13 12:55:46 +0100201/*
202 * The hotkey driver declaration
203 */
204static int eeepc_hotk_add(struct acpi_device *device);
205static int eeepc_hotk_remove(struct acpi_device *device, int type);
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600206static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
Eric Coopere59f8792008-03-13 12:55:46 +0100207
208static const struct acpi_device_id eeepc_device_ids[] = {
209 {EEEPC_HOTK_HID, 0},
210 {"", 0},
211};
212MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
213
214static struct acpi_driver eeepc_hotk_driver = {
215 .name = EEEPC_HOTK_NAME,
216 .class = EEEPC_HOTK_CLASS,
217 .ids = eeepc_device_ids,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600218 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Eric Coopere59f8792008-03-13 12:55:46 +0100219 .ops = {
220 .add = eeepc_hotk_add,
221 .remove = eeepc_hotk_remove,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600222 .notify = eeepc_hotk_notify,
Eric Coopere59f8792008-03-13 12:55:46 +0100223 },
224};
225
Corentin Chary2b121bc2009-06-25 13:25:36 +0200226/* PCI hotplug ops */
227static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
228
229static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
230 .owner = THIS_MODULE,
231 .get_adapter_status = eeepc_get_adapter_status,
232 .get_power_status = eeepc_get_adapter_status,
233};
234
Corentin Charya5fa4292008-03-13 12:56:37 +0100235/* The backlight device /sys/class/backlight */
236static struct backlight_device *eeepc_backlight_device;
237
Corentin Charye1faa9d2008-03-13 12:57:18 +0100238/* The hwmon device */
239static struct device *eeepc_hwmon_device;
240
Corentin Charya5fa4292008-03-13 12:56:37 +0100241/*
242 * The backlight class declaration
243 */
244static int read_brightness(struct backlight_device *bd);
245static int update_bl_status(struct backlight_device *bd);
246static struct backlight_ops eeepcbl_ops = {
247 .get_brightness = read_brightness,
248 .update_status = update_bl_status,
249};
250
Eric Coopere59f8792008-03-13 12:55:46 +0100251MODULE_AUTHOR("Corentin Chary, Eric Cooper");
252MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
253MODULE_LICENSE("GPL");
254
255/*
256 * ACPI Helpers
257 */
258static int write_acpi_int(acpi_handle handle, const char *method, int val,
259 struct acpi_buffer *output)
260{
261 struct acpi_object_list params;
262 union acpi_object in_obj;
263 acpi_status status;
264
265 params.count = 1;
266 params.pointer = &in_obj;
267 in_obj.type = ACPI_TYPE_INTEGER;
268 in_obj.integer.value = val;
269
270 status = acpi_evaluate_object(handle, (char *)method, &params, output);
271 return (status == AE_OK ? 0 : -1);
272}
273
274static int read_acpi_int(acpi_handle handle, const char *method, int *val)
275{
276 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400277 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100278
279 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
280 if (ACPI_FAILURE(status)) {
281 *val = -1;
282 return -1;
283 } else {
284 *val = result;
285 return 0;
286 }
287}
288
289static int set_acpi(int cm, int value)
290{
291 if (ehotk->cm_supported & (0x1 << cm)) {
292 const char *method = cm_setv[cm];
293 if (method == NULL)
294 return -ENODEV;
295 if (write_acpi_int(ehotk->handle, method, value, NULL))
Joe Perches19b53282009-06-25 13:25:37 +0200296 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100297 }
298 return 0;
299}
300
301static int get_acpi(int cm)
302{
Corentin Charyf36509e2009-06-25 13:25:40 +0200303 int value = -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100304 if ((ehotk->cm_supported & (0x1 << cm))) {
305 const char *method = cm_getv[cm];
306 if (method == NULL)
307 return -ENODEV;
308 if (read_acpi_int(ehotk->handle, method, &value))
Joe Perches19b53282009-06-25 13:25:37 +0200309 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100310 }
311 return value;
312}
313
314/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100315 * Backlight
316 */
317static int read_brightness(struct backlight_device *bd)
318{
319 return get_acpi(CM_ASL_PANELBRIGHT);
320}
321
322static int set_brightness(struct backlight_device *bd, int value)
323{
324 value = max(0, min(15, value));
325 return set_acpi(CM_ASL_PANELBRIGHT, value);
326}
327
328static int update_bl_status(struct backlight_device *bd)
329{
330 return set_brightness(bd, bd->props.brightness);
331}
332
333/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100334 * Rfkill helpers
335 */
336
Johannes Berg19d337d2009-06-02 13:01:37 +0200337static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100338{
339 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200340 return false;
341 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100342}
343
Johannes Berg19d337d2009-06-02 13:01:37 +0200344static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100345{
Johannes Berg19d337d2009-06-02 13:01:37 +0200346 unsigned long asl = (unsigned long)data;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200347 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100348}
349
Johannes Berg19d337d2009-06-02 13:01:37 +0200350static const struct rfkill_ops eeepc_rfkill_ops = {
351 .set_block = eeepc_rfkill_set,
352};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100353
Rakib Mullickdcb73ee2009-10-13 00:13:32 +0200354static void __devinit eeepc_enable_camera(void)
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000355{
356 /*
357 * If the following call to set_acpi() fails, it's because there's no
358 * camera so we can ignore the error.
359 */
Luca Niccoli80f0c892009-10-16 22:22:47 +0200360 if (get_acpi(CM_ASL_CAMERA) == 0)
361 set_acpi(CM_ASL_CAMERA, 1);
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000362}
363
Matthew Garretta195dcd2008-08-19 12:13:20 +0100364/*
Eric Coopere59f8792008-03-13 12:55:46 +0100365 * Sys helpers
366 */
367static int parse_arg(const char *buf, unsigned long count, int *val)
368{
369 if (!count)
370 return 0;
371 if (sscanf(buf, "%i", val) != 1)
372 return -EINVAL;
373 return count;
374}
375
376static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
377{
378 int rv, value;
379
380 rv = parse_arg(buf, count, &value);
381 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200382 value = set_acpi(cm, value);
383 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000384 return -EIO;
Eric Coopere59f8792008-03-13 12:55:46 +0100385 return rv;
386}
387
388static ssize_t show_sys_acpi(int cm, char *buf)
389{
Corentin Charyf36509e2009-06-25 13:25:40 +0200390 int value = get_acpi(cm);
391
392 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000393 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200394 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100395}
396
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000397#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Eric Coopere59f8792008-03-13 12:55:46 +0100398 static ssize_t show_##_name(struct device *dev, \
399 struct device_attribute *attr, \
400 char *buf) \
401 { \
402 return show_sys_acpi(_cm, buf); \
403 } \
404 static ssize_t store_##_name(struct device *dev, \
405 struct device_attribute *attr, \
406 const char *buf, size_t count) \
407 { \
408 return store_sys_acpi(_cm, buf, count); \
409 } \
410 static struct device_attribute dev_attr_##_name = { \
411 .attr = { \
412 .name = __stringify(_name), \
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000413 .mode = _mode }, \
Eric Coopere59f8792008-03-13 12:55:46 +0100414 .show = show_##_name, \
415 .store = store_##_name, \
416 }
417
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000418EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
419EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
420EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000421
422struct eeepc_cpufv {
423 int num;
424 int cur;
425};
426
427static int get_cpufv(struct eeepc_cpufv *c)
428{
429 c->cur = get_acpi(CM_ASL_CPUFV);
430 c->num = (c->cur >> 8) & 0xff;
431 c->cur &= 0xff;
432 if (c->cur < 0 || c->num <= 0 || c->num > 12)
433 return -ENODEV;
434 return 0;
435}
436
437static ssize_t show_available_cpufv(struct device *dev,
438 struct device_attribute *attr,
439 char *buf)
440{
441 struct eeepc_cpufv c;
442 int i;
443 ssize_t len = 0;
444
445 if (get_cpufv(&c))
446 return -ENODEV;
447 for (i = 0; i < c.num; i++)
448 len += sprintf(buf + len, "%d ", i);
449 len += sprintf(buf + len, "\n");
450 return len;
451}
452
453static ssize_t show_cpufv(struct device *dev,
454 struct device_attribute *attr,
455 char *buf)
456{
457 struct eeepc_cpufv c;
458
459 if (get_cpufv(&c))
460 return -ENODEV;
461 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
462}
463
464static ssize_t store_cpufv(struct device *dev,
465 struct device_attribute *attr,
466 const char *buf, size_t count)
467{
468 struct eeepc_cpufv c;
469 int rv, value;
470
471 if (get_cpufv(&c))
472 return -ENODEV;
473 rv = parse_arg(buf, count, &value);
474 if (rv < 0)
475 return rv;
476 if (!rv || value < 0 || value >= c.num)
477 return -EINVAL;
478 set_acpi(CM_ASL_CPUFV, value);
479 return rv;
480}
481
482static struct device_attribute dev_attr_cpufv = {
483 .attr = {
484 .name = "cpufv",
485 .mode = 0644 },
486 .show = show_cpufv,
487 .store = store_cpufv
488};
489
490static struct device_attribute dev_attr_available_cpufv = {
491 .attr = {
492 .name = "available_cpufv",
493 .mode = 0444 },
494 .show = show_available_cpufv
495};
Eric Coopere59f8792008-03-13 12:55:46 +0100496
497static struct attribute *platform_attributes[] = {
498 &dev_attr_camera.attr,
499 &dev_attr_cardr.attr,
500 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200501 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000502 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100503 NULL
504};
505
506static struct attribute_group platform_attribute_group = {
507 .attrs = platform_attributes
508};
509
510/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000511 * LEDs
512 */
513/*
514 * These functions actually update the LED's, and are called from a
515 * workqueue. By doing this as separate work rather than when the LED
516 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
517 * potentially bad time, such as a timer interrupt.
518 */
519static int tpd_led_wk;
520
521static void tpd_led_update(struct work_struct *ignored)
522{
523 int value = tpd_led_wk;
524 set_acpi(CM_ASL_TPD, value);
525}
526
527static struct workqueue_struct *led_workqueue;
528static DECLARE_WORK(tpd_led_work, tpd_led_update);
529
530static void tpd_led_set(struct led_classdev *led_cdev,
531 enum led_brightness value)
532{
533 tpd_led_wk = (value > 0) ? 1 : 0;
534 queue_work(led_workqueue, &tpd_led_work);
535}
536
537static struct led_classdev tpd_led = {
538 .name = "eeepc::touchpad",
539 .brightness_set = tpd_led_set,
540 .max_brightness = 1
541};
542
543/*
Eric Coopere59f8792008-03-13 12:55:46 +0100544 * Hotkey functions
545 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100546static struct key_entry *eepc_get_entry_by_scancode(int code)
547{
548 struct key_entry *key;
549
550 for (key = eeepc_keymap; key->type != KE_END; key++)
551 if (code == key->code)
552 return key;
553
554 return NULL;
555}
556
557static struct key_entry *eepc_get_entry_by_keycode(int code)
558{
559 struct key_entry *key;
560
561 for (key = eeepc_keymap; key->type != KE_END; key++)
562 if (code == key->keycode && key->type == KE_KEY)
563 return key;
564
565 return NULL;
566}
567
568static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
569{
570 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
571
572 if (key && key->type == KE_KEY) {
573 *keycode = key->keycode;
574 return 0;
575 }
576
577 return -EINVAL;
578}
579
580static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
581{
582 struct key_entry *key;
583 int old_keycode;
584
585 if (keycode < 0 || keycode > KEY_MAX)
586 return -EINVAL;
587
588 key = eepc_get_entry_by_scancode(scancode);
589 if (key && key->type == KE_KEY) {
590 old_keycode = key->keycode;
591 key->keycode = keycode;
592 set_bit(keycode, dev->keybit);
593 if (!eepc_get_entry_by_keycode(old_keycode))
594 clear_bit(old_keycode, dev->keybit);
595 return 0;
596 }
597
598 return -EINVAL;
599}
600
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200601static void cmsg_quirk(int cm, const char *name)
602{
603 int dummy;
604
605 /* Some BIOSes do not report cm although it is avaliable.
606 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
607 if (!(ehotk->cm_supported & (1 << cm))
608 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
609 pr_info("%s (%x) not reported by BIOS,"
610 " enabling anyway\n", name, 1 << cm);
611 ehotk->cm_supported |= 1 << cm;
612 }
613}
614
615static void cmsg_quirks(void)
616{
617 cmsg_quirk(CM_ASL_LID, "LID");
618 cmsg_quirk(CM_ASL_TYPE, "TYPE");
619 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
620 cmsg_quirk(CM_ASL_TPD, "TPD");
621}
622
Eric Coopere59f8792008-03-13 12:55:46 +0100623static int eeepc_hotk_check(void)
624{
625 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
626 int result;
627
628 result = acpi_bus_get_status(ehotk->device);
629 if (result)
630 return result;
631 if (ehotk->device->status.present) {
632 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
633 &buffer)) {
Joe Perches19b53282009-06-25 13:25:37 +0200634 pr_err("Hotkey initialization failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100635 return -ENODEV;
636 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200637 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
Eric Coopere59f8792008-03-13 12:55:46 +0100638 }
639 /* get control methods supported */
640 if (read_acpi_int(ehotk->handle, "CMSG"
641 , &ehotk->cm_supported)) {
Joe Perches19b53282009-06-25 13:25:37 +0200642 pr_err("Get control methods supported failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100643 return -ENODEV;
644 } else {
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200645 cmsg_quirks();
Joe Perches19b53282009-06-25 13:25:37 +0200646 pr_info("Get control methods supported: 0x%x\n",
647 ehotk->cm_supported);
Eric Coopere59f8792008-03-13 12:55:46 +0100648 }
649 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200650 pr_err("Hotkey device not present, aborting\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100651 return -EINVAL;
652 }
653 return 0;
654}
655
Darren Salt64b86b62009-04-27 09:23:38 +0200656static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100657{
Darren Salt64b86b62009-04-27 09:23:38 +0200658 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100659 struct backlight_device *bd = eeepc_backlight_device;
Darren Salt64b86b62009-04-27 09:23:38 +0200660 if (bd) {
661 int old = bd->props.brightness;
Matthew Garrettd822d5c2009-07-14 17:06:04 +0100662 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
Darren Salt64b86b62009-04-27 09:23:38 +0200663 return old;
664 }
665 return -1;
Corentin Charya5fa4292008-03-13 12:56:37 +0100666}
667
Corentin Chary2b121bc2009-06-25 13:25:36 +0200668static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
669 u8 *value)
670{
671 int val = get_acpi(CM_ASL_WLAN);
672
673 if (val == 1 || val == 0)
674 *value = val;
675 else
676 return -EINVAL;
677
678 return 0;
679}
680
Corentin Chary58ce48a2009-10-16 22:22:46 +0200681static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100682{
683 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000684 struct pci_bus *bus;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200685 bool blocked = eeepc_wlan_rfkill_blocked();
Matthew Garrett57402942009-01-20 16:17:48 +0100686
Corentin Chary58ce48a2009-10-16 22:22:46 +0200687 if (ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000688 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000689
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000690 mutex_lock(&ehotk->hotplug_lock);
691
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000692 if (ehotk->hotplug_slot) {
693 bus = pci_find_bus(0, 1);
694 if (!bus) {
695 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000696 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100697 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000698
699 if (!blocked) {
700 dev = pci_get_slot(bus, 0);
701 if (dev) {
702 /* Device already present */
703 pci_dev_put(dev);
704 goto out_unlock;
705 }
706 dev = pci_scan_single_device(bus, 0);
707 if (dev) {
708 pci_bus_assign_resources(bus);
709 if (pci_bus_add_device(dev))
710 pr_err("Unable to hotplug wifi\n");
711 }
712 } else {
713 dev = pci_get_slot(bus, 0);
714 if (dev) {
715 pci_remove_bus_device(dev);
716 pci_dev_put(dev);
717 }
Matthew Garrett57402942009-01-20 16:17:48 +0100718 }
719 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000720
721out_unlock:
722 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100723}
724
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100725static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
726{
727 if (event != ACPI_NOTIFY_BUS_CHECK)
728 return;
729
Corentin Chary58ce48a2009-10-16 22:22:46 +0200730 eeepc_rfkill_hotplug();
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100731}
732
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600733static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100734{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100735 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100736 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200737 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100738
Eric Coopere59f8792008-03-13 12:55:46 +0100739 if (!ehotk)
740 return;
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600741 if (event > ACPI_MAX_SYS_NOTIFY)
742 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100743 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200744 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100745 count = ehotk->event_count[event % 128]++;
746 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100747 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
748 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100749 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100750 if (ehotk->inputdev) {
Darren Salt64b86b62009-04-27 09:23:38 +0200751 if (brn != -ENODEV) {
752 /* brightness-change events need special
753 * handling for conversion to key events
754 */
755 if (brn < 0)
756 brn = event;
757 else
758 brn += NOTIFY_BRN_MIN;
759 if (event < brn)
760 event = NOTIFY_BRN_MIN; /* brightness down */
761 else if (event > brn)
762 event = NOTIFY_BRN_MIN + 2; /* ... up */
763 else
764 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
765 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100766 key = eepc_get_entry_by_scancode(event);
767 if (key) {
768 switch (key->type) {
769 case KE_KEY:
770 input_report_key(ehotk->inputdev, key->keycode,
771 1);
772 input_sync(ehotk->inputdev);
773 input_report_key(ehotk->inputdev, key->keycode,
774 0);
775 input_sync(ehotk->inputdev);
776 break;
777 }
778 }
779 }
Eric Coopere59f8792008-03-13 12:55:46 +0100780}
781
Matthew Garrett57402942009-01-20 16:17:48 +0100782static int eeepc_register_rfkill_notifier(char *node)
783{
784 acpi_status status = AE_OK;
785 acpi_handle handle;
786
787 status = acpi_get_handle(NULL, node, &handle);
788
789 if (ACPI_SUCCESS(status)) {
790 status = acpi_install_notify_handler(handle,
791 ACPI_SYSTEM_NOTIFY,
792 eeepc_rfkill_notify,
793 NULL);
794 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200795 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100796 } else
797 return -ENODEV;
798
799 return 0;
800}
801
802static void eeepc_unregister_rfkill_notifier(char *node)
803{
804 acpi_status status = AE_OK;
805 acpi_handle handle;
806
807 status = acpi_get_handle(NULL, node, &handle);
808
809 if (ACPI_SUCCESS(status)) {
810 status = acpi_remove_notify_handler(handle,
811 ACPI_SYSTEM_NOTIFY,
812 eeepc_rfkill_notify);
813 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200814 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100815 node);
816 }
817}
818
Corentin Chary2b121bc2009-06-25 13:25:36 +0200819static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
820{
821 kfree(hotplug_slot->info);
822 kfree(hotplug_slot);
823}
824
825static int eeepc_setup_pci_hotplug(void)
826{
827 int ret = -ENOMEM;
828 struct pci_bus *bus = pci_find_bus(0, 1);
829
830 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200831 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200832 return -ENODEV;
833 }
834
835 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
836 if (!ehotk->hotplug_slot)
837 goto error_slot;
838
839 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
840 GFP_KERNEL);
841 if (!ehotk->hotplug_slot->info)
842 goto error_info;
843
844 ehotk->hotplug_slot->private = ehotk;
845 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
846 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
847 eeepc_get_adapter_status(ehotk->hotplug_slot,
848 &ehotk->hotplug_slot->info->adapter_status);
849
850 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
851 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200852 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200853 goto error_register;
854 }
855
856 return 0;
857
858error_register:
859 kfree(ehotk->hotplug_slot->info);
860error_info:
861 kfree(ehotk->hotplug_slot);
862 ehotk->hotplug_slot = NULL;
863error_slot:
864 return ret;
865}
866
Alan Jenkinsc200da52009-08-28 12:56:40 +0000867static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100868{
Corentin Chary7de39382009-06-25 13:25:38 +0200869 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100870 bool wlan;
871
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000872 /*
873 * Work around bios bug - acpi _PTS turns off the wireless led
874 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000875 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100876 */
877 wlan = get_acpi(CM_ASL_WLAN);
878 set_acpi(CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100879 }
880
Alan Jenkinsc200da52009-08-28 12:56:40 +0000881 return 0;
882}
883
884static int eeepc_hotk_restore(struct device *device)
885{
886 /* Refresh both wlan rfkill state and pci hotplug */
887 if (ehotk->wlan_rfkill)
Corentin Chary58ce48a2009-10-16 22:22:46 +0200888 eeepc_rfkill_hotplug();
Alan Jenkinsc200da52009-08-28 12:56:40 +0000889
Corentin Chary7de39382009-06-25 13:25:38 +0200890 if (ehotk->bluetooth_rfkill)
891 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100892 get_acpi(CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa4746102009-08-28 12:56:38 +0000893 if (ehotk->wwan3g_rfkill)
894 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
895 get_acpi(CM_ASL_3G) != 1);
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000896 if (ehotk->wimax_rfkill)
897 rfkill_set_sw_state(ehotk->wimax_rfkill,
898 get_acpi(CM_ASL_WIMAX) != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100899
900 return 0;
901}
902
Eric Coopere59f8792008-03-13 12:55:46 +0100903/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100904 * Hwmon
905 */
906static int eeepc_get_fan_pwm(void)
907{
908 int value = 0;
909
910 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200911 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100912 return (value);
913}
914
915static void eeepc_set_fan_pwm(int value)
916{
Corentin Chary04dcd842008-10-09 15:33:57 +0200917 value = SENSORS_LIMIT(value, 0, 255);
918 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100919 ec_write(EEEPC_EC_SC02, value);
920}
921
922static int eeepc_get_fan_rpm(void)
923{
924 int high = 0;
925 int low = 0;
926
927 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
928 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
929 return (high << 8 | low);
930}
931
932static int eeepc_get_fan_ctrl(void)
933{
934 int value = 0;
935
936 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
937 return ((value & 0x02 ? 1 : 0));
938}
939
940static void eeepc_set_fan_ctrl(int manual)
941{
942 int value = 0;
943
944 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
945 if (manual)
946 value |= 0x02;
947 else
948 value &= ~0x02;
949 ec_write(EEEPC_EC_SFB3, value);
950}
951
952static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
953{
954 int rv, value;
955
956 rv = parse_arg(buf, count, &value);
957 if (rv > 0)
958 set(value);
959 return rv;
960}
961
962static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
963{
964 return sprintf(buf, "%d\n", get());
965}
966
967#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
968 static ssize_t show_##_name(struct device *dev, \
969 struct device_attribute *attr, \
970 char *buf) \
971 { \
972 return show_sys_hwmon(_set, buf); \
973 } \
974 static ssize_t store_##_name(struct device *dev, \
975 struct device_attribute *attr, \
976 const char *buf, size_t count) \
977 { \
978 return store_sys_hwmon(_get, buf, count); \
979 } \
980 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
981
982EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200983EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100984 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
985EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
986 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
987
Corentin Chary04dcd842008-10-09 15:33:57 +0200988static ssize_t
989show_name(struct device *dev, struct device_attribute *attr, char *buf)
990{
991 return sprintf(buf, "eeepc\n");
992}
993static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
994
Corentin Charye1faa9d2008-03-13 12:57:18 +0100995static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +0200996 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100997 &sensor_dev_attr_fan1_input.dev_attr.attr,
998 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +0200999 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001000 NULL
1001};
1002
1003static struct attribute_group hwmon_attribute_group = {
1004 .attrs = hwmon_attributes
1005};
1006
1007/*
Eric Coopere59f8792008-03-13 12:55:46 +01001008 * exit/init
1009 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001010static void eeepc_backlight_exit(void)
1011{
1012 if (eeepc_backlight_device)
1013 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001014 eeepc_backlight_device = NULL;
1015}
1016
1017static void eeepc_rfkill_exit(void)
1018{
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001019 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +02001020 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1021 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001022 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001023 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001024 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001025 ehotk->wlan_rfkill = NULL;
1026 }
1027 /*
1028 * Refresh pci hotplug in case the rfkill state was changed after
1029 * eeepc_unregister_rfkill_notifier()
1030 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001031 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001032 if (ehotk->hotplug_slot)
1033 pci_hp_deregister(ehotk->hotplug_slot);
1034
Alan Jenkinsa82580692009-08-29 10:28:30 +02001035 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001036 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001037 rfkill_destroy(ehotk->bluetooth_rfkill);
1038 ehotk->bluetooth_rfkill = NULL;
1039 }
1040 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001041 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001042 rfkill_destroy(ehotk->wwan3g_rfkill);
1043 ehotk->wwan3g_rfkill = NULL;
1044 }
1045 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001046 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001047 rfkill_destroy(ehotk->wimax_rfkill);
1048 ehotk->wimax_rfkill = NULL;
1049 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001050}
1051
1052static void eeepc_input_exit(void)
1053{
1054 if (ehotk->inputdev)
1055 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001056}
1057
Corentin Charye1faa9d2008-03-13 12:57:18 +01001058static void eeepc_hwmon_exit(void)
1059{
1060 struct device *hwmon;
1061
1062 hwmon = eeepc_hwmon_device;
1063 if (!hwmon)
1064 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001065 sysfs_remove_group(&hwmon->kobj,
1066 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001067 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001068 eeepc_hwmon_device = NULL;
1069}
1070
Corentin Chary3c0eb512009-12-03 07:44:52 +00001071static void eeepc_led_exit(void)
1072{
1073 if (led_workqueue)
1074 destroy_workqueue(led_workqueue);
1075 if (tpd_led.dev)
1076 led_classdev_unregister(&tpd_led);
1077}
1078
Corentin Chary7de39382009-06-25 13:25:38 +02001079static int eeepc_new_rfkill(struct rfkill **rfkill,
1080 const char *name, struct device *dev,
1081 enum rfkill_type type, int cm)
1082{
1083 int result;
1084
Corentin Charyf36509e2009-06-25 13:25:40 +02001085 result = get_acpi(cm);
1086 if (result < 0)
1087 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001088
1089 *rfkill = rfkill_alloc(name, dev, type,
1090 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1091
1092 if (!*rfkill)
1093 return -EINVAL;
1094
1095 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1096 result = rfkill_register(*rfkill);
1097 if (result) {
1098 rfkill_destroy(*rfkill);
1099 *rfkill = NULL;
1100 return result;
1101 }
1102 return 0;
1103}
1104
1105
1106static int eeepc_rfkill_init(struct device *dev)
1107{
1108 int result = 0;
1109
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001110 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001111
Corentin Chary7de39382009-06-25 13:25:38 +02001112 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1113 "eeepc-wlan", dev,
1114 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1115
1116 if (result && result != -ENODEV)
1117 goto exit;
1118
1119 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1120 "eeepc-bluetooth", dev,
1121 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1122
1123 if (result && result != -ENODEV)
1124 goto exit;
1125
Corentin Chary3cd530b2009-06-25 13:25:42 +02001126 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1127 "eeepc-wwan3g", dev,
1128 RFKILL_TYPE_WWAN, CM_ASL_3G);
1129
1130 if (result && result != -ENODEV)
1131 goto exit;
1132
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001133 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1134 "eeepc-wimax", dev,
1135 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1136
1137 if (result && result != -ENODEV)
1138 goto exit;
1139
Corentin Chary7de39382009-06-25 13:25:38 +02001140 result = eeepc_setup_pci_hotplug();
1141 /*
1142 * If we get -EBUSY then something else is handling the PCI hotplug -
1143 * don't fail in this case
1144 */
1145 if (result == -EBUSY)
1146 result = 0;
1147
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001148 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001149 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1150 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1151 /*
1152 * Refresh pci hotplug in case the rfkill state was changed during
1153 * setup.
1154 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001155 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001156
Corentin Chary7de39382009-06-25 13:25:38 +02001157exit:
1158 if (result && result != -ENODEV)
1159 eeepc_rfkill_exit();
1160 return result;
1161}
1162
Corentin Charya5fa4292008-03-13 12:56:37 +01001163static int eeepc_backlight_init(struct device *dev)
1164{
1165 struct backlight_device *bd;
1166
1167 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1168 NULL, &eeepcbl_ops);
1169 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001170 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001171 eeepc_backlight_device = NULL;
1172 return PTR_ERR(bd);
1173 }
1174 eeepc_backlight_device = bd;
1175 bd->props.max_brightness = 15;
1176 bd->props.brightness = read_brightness(NULL);
1177 bd->props.power = FB_BLANK_UNBLANK;
1178 backlight_update_status(bd);
1179 return 0;
1180}
1181
Corentin Charye1faa9d2008-03-13 12:57:18 +01001182static int eeepc_hwmon_init(struct device *dev)
1183{
1184 struct device *hwmon;
1185 int result;
1186
1187 hwmon = hwmon_device_register(dev);
1188 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001189 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001190 eeepc_hwmon_device = NULL;
1191 return PTR_ERR(hwmon);
1192 }
1193 eeepc_hwmon_device = hwmon;
1194 result = sysfs_create_group(&hwmon->kobj,
1195 &hwmon_attribute_group);
1196 if (result)
1197 eeepc_hwmon_exit();
1198 return result;
1199}
1200
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001201static int eeepc_input_init(struct device *dev)
1202{
1203 const struct key_entry *key;
1204 int result;
1205
1206 ehotk->inputdev = input_allocate_device();
1207 if (!ehotk->inputdev) {
1208 pr_info("Unable to allocate input device\n");
1209 return -ENOMEM;
1210 }
1211 ehotk->inputdev->name = "Asus EeePC extra buttons";
1212 ehotk->inputdev->dev.parent = dev;
1213 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1214 ehotk->inputdev->id.bustype = BUS_HOST;
1215 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1216 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1217
1218 for (key = eeepc_keymap; key->type != KE_END; key++) {
1219 switch (key->type) {
1220 case KE_KEY:
1221 set_bit(EV_KEY, ehotk->inputdev->evbit);
1222 set_bit(key->keycode, ehotk->inputdev->keybit);
1223 break;
1224 }
1225 }
1226 result = input_register_device(ehotk->inputdev);
1227 if (result) {
1228 pr_info("Unable to register input device\n");
1229 input_free_device(ehotk->inputdev);
1230 return result;
1231 }
1232 return 0;
1233}
1234
Corentin Chary3c0eb512009-12-03 07:44:52 +00001235static int eeepc_led_init(struct device *dev)
1236{
1237 int rv;
1238
1239 if (get_acpi(CM_ASL_TPD) == -ENODEV)
1240 return 0;
1241
1242 rv = led_classdev_register(dev, &tpd_led);
1243 if (rv)
1244 return rv;
1245
1246 led_workqueue = create_singlethread_workqueue("led_workqueue");
1247 if (!led_workqueue)
1248 return -ENOMEM;
1249
1250 return 0;
1251}
1252
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001253static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001254{
1255 struct device *dev;
1256 int result;
1257
Alan Jenkins1e779852009-08-28 12:56:35 +00001258 if (!device)
Len Brownaeb41b82009-08-28 19:03:11 -04001259 return -EINVAL;
Alan Jenkins1e779852009-08-28 12:56:35 +00001260 pr_notice(EEEPC_HOTK_NAME "\n");
1261 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1262 if (!ehotk)
1263 return -ENOMEM;
1264 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1265 ehotk->handle = device->handle;
1266 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1267 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1268 device->driver_data = ehotk;
1269 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001270
Alan Jenkins1e779852009-08-28 12:56:35 +00001271 result = eeepc_hotk_check();
1272 if (result)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001273 goto fail_platform_driver;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001274 eeepc_enable_camera();
1275
Eric Coopere59f8792008-03-13 12:55:46 +01001276 /* Register platform stuff */
1277 result = platform_driver_register(&platform_driver);
1278 if (result)
1279 goto fail_platform_driver;
1280 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1281 if (!platform_device) {
1282 result = -ENOMEM;
1283 goto fail_platform_device1;
1284 }
1285 result = platform_device_add(platform_device);
1286 if (result)
1287 goto fail_platform_device2;
1288 result = sysfs_create_group(&platform_device->dev.kobj,
1289 &platform_attribute_group);
1290 if (result)
1291 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001292
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001293 dev = &platform_device->dev;
1294
1295 if (!acpi_video_backlight_support()) {
1296 result = eeepc_backlight_init(dev);
1297 if (result)
1298 goto fail_backlight;
1299 } else
1300 pr_info("Backlight controlled by ACPI video "
1301 "driver\n");
1302
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001303 result = eeepc_input_init(dev);
1304 if (result)
1305 goto fail_input;
1306
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001307 result = eeepc_hwmon_init(dev);
1308 if (result)
1309 goto fail_hwmon;
1310
Corentin Chary3c0eb512009-12-03 07:44:52 +00001311 result = eeepc_led_init(dev);
1312 if (result)
1313 goto fail_led;
1314
Corentin Chary7de39382009-06-25 13:25:38 +02001315 result = eeepc_rfkill_init(dev);
1316 if (result)
1317 goto fail_rfkill;
1318
Eric Coopere59f8792008-03-13 12:55:46 +01001319 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001320
Corentin Chary7de39382009-06-25 13:25:38 +02001321fail_rfkill:
Corentin Chary3c0eb512009-12-03 07:44:52 +00001322 eeepc_led_exit();
1323fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001324 eeepc_hwmon_exit();
1325fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001326 eeepc_input_exit();
1327fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001328 eeepc_backlight_exit();
1329fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001330 sysfs_remove_group(&platform_device->dev.kobj,
1331 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001332fail_sysfs:
1333 platform_device_del(platform_device);
1334fail_platform_device2:
1335 platform_device_put(platform_device);
1336fail_platform_device1:
1337 platform_driver_unregister(&platform_driver);
1338fail_platform_driver:
Alan Jenkins1e779852009-08-28 12:56:35 +00001339 kfree(ehotk);
1340
Eric Coopere59f8792008-03-13 12:55:46 +01001341 return result;
1342}
1343
Alan Jenkins1e779852009-08-28 12:56:35 +00001344static int eeepc_hotk_remove(struct acpi_device *device, int type)
1345{
1346 if (!device || !acpi_driver_data(device))
Len Brownaeb41b82009-08-28 19:03:11 -04001347 return -EINVAL;
Alan Jenkins1e779852009-08-28 12:56:35 +00001348
1349 eeepc_backlight_exit();
1350 eeepc_rfkill_exit();
1351 eeepc_input_exit();
1352 eeepc_hwmon_exit();
Corentin Chary3c0eb512009-12-03 07:44:52 +00001353 eeepc_led_exit();
Alan Jenkins1e779852009-08-28 12:56:35 +00001354 sysfs_remove_group(&platform_device->dev.kobj,
1355 &platform_attribute_group);
1356 platform_device_unregister(platform_device);
1357 platform_driver_unregister(&platform_driver);
1358
1359 kfree(ehotk);
1360 return 0;
1361}
1362
1363static int __init eeepc_laptop_init(void)
1364{
1365 int result;
1366
1367 if (acpi_disabled)
1368 return -ENODEV;
1369 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1370 if (result < 0)
1371 return result;
1372 if (!ehotk) {
1373 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1374 return -ENODEV;
1375 }
1376 return 0;
1377}
1378
1379static void __exit eeepc_laptop_exit(void)
1380{
1381 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1382}
1383
Eric Coopere59f8792008-03-13 12:55:46 +01001384module_init(eeepc_laptop_init);
1385module_exit(eeepc_laptop_exit);