blob: 8b686b563ec0d0925e47b4535cc704179b5ae45b [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,
Alan Jenkinseacec302009-12-03 07:44:55 +0000217 .owner = THIS_MODULE,
Eric Coopere59f8792008-03-13 12:55:46 +0100218 .ids = eeepc_device_ids,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600219 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Eric Coopere59f8792008-03-13 12:55:46 +0100220 .ops = {
221 .add = eeepc_hotk_add,
222 .remove = eeepc_hotk_remove,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600223 .notify = eeepc_hotk_notify,
Eric Coopere59f8792008-03-13 12:55:46 +0100224 },
225};
226
Corentin Chary2b121bc2009-06-25 13:25:36 +0200227/* PCI hotplug ops */
228static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
229
230static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
231 .owner = THIS_MODULE,
232 .get_adapter_status = eeepc_get_adapter_status,
233 .get_power_status = eeepc_get_adapter_status,
234};
235
Corentin Charya5fa4292008-03-13 12:56:37 +0100236/* The backlight device /sys/class/backlight */
237static struct backlight_device *eeepc_backlight_device;
238
Corentin Charye1faa9d2008-03-13 12:57:18 +0100239/* The hwmon device */
240static struct device *eeepc_hwmon_device;
241
Corentin Charya5fa4292008-03-13 12:56:37 +0100242/*
243 * The backlight class declaration
244 */
245static int read_brightness(struct backlight_device *bd);
246static int update_bl_status(struct backlight_device *bd);
247static struct backlight_ops eeepcbl_ops = {
248 .get_brightness = read_brightness,
249 .update_status = update_bl_status,
250};
251
Eric Coopere59f8792008-03-13 12:55:46 +0100252MODULE_AUTHOR("Corentin Chary, Eric Cooper");
253MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
254MODULE_LICENSE("GPL");
255
256/*
257 * ACPI Helpers
258 */
259static int write_acpi_int(acpi_handle handle, const char *method, int val,
260 struct acpi_buffer *output)
261{
262 struct acpi_object_list params;
263 union acpi_object in_obj;
264 acpi_status status;
265
266 params.count = 1;
267 params.pointer = &in_obj;
268 in_obj.type = ACPI_TYPE_INTEGER;
269 in_obj.integer.value = val;
270
271 status = acpi_evaluate_object(handle, (char *)method, &params, output);
272 return (status == AE_OK ? 0 : -1);
273}
274
275static int read_acpi_int(acpi_handle handle, const char *method, int *val)
276{
277 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400278 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100279
280 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
281 if (ACPI_FAILURE(status)) {
282 *val = -1;
283 return -1;
284 } else {
285 *val = result;
286 return 0;
287 }
288}
289
290static int set_acpi(int cm, int value)
291{
292 if (ehotk->cm_supported & (0x1 << cm)) {
293 const char *method = cm_setv[cm];
294 if (method == NULL)
295 return -ENODEV;
296 if (write_acpi_int(ehotk->handle, method, value, NULL))
Joe Perches19b53282009-06-25 13:25:37 +0200297 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100298 }
299 return 0;
300}
301
302static int get_acpi(int cm)
303{
Corentin Charyf36509e2009-06-25 13:25:40 +0200304 int value = -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100305 if ((ehotk->cm_supported & (0x1 << cm))) {
306 const char *method = cm_getv[cm];
307 if (method == NULL)
308 return -ENODEV;
309 if (read_acpi_int(ehotk->handle, method, &value))
Joe Perches19b53282009-06-25 13:25:37 +0200310 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100311 }
312 return value;
313}
314
315/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100316 * Backlight
317 */
318static int read_brightness(struct backlight_device *bd)
319{
320 return get_acpi(CM_ASL_PANELBRIGHT);
321}
322
323static int set_brightness(struct backlight_device *bd, int value)
324{
325 value = max(0, min(15, value));
326 return set_acpi(CM_ASL_PANELBRIGHT, value);
327}
328
329static int update_bl_status(struct backlight_device *bd)
330{
331 return set_brightness(bd, bd->props.brightness);
332}
333
334/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100335 * Rfkill helpers
336 */
337
Johannes Berg19d337d2009-06-02 13:01:37 +0200338static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100339{
340 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200341 return false;
342 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100343}
344
Johannes Berg19d337d2009-06-02 13:01:37 +0200345static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100346{
Johannes Berg19d337d2009-06-02 13:01:37 +0200347 unsigned long asl = (unsigned long)data;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200348 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100349}
350
Johannes Berg19d337d2009-06-02 13:01:37 +0200351static const struct rfkill_ops eeepc_rfkill_ops = {
352 .set_block = eeepc_rfkill_set,
353};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100354
Rakib Mullickdcb73ee2009-10-13 00:13:32 +0200355static void __devinit eeepc_enable_camera(void)
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000356{
357 /*
358 * If the following call to set_acpi() fails, it's because there's no
359 * camera so we can ignore the error.
360 */
Luca Niccoli80f0c892009-10-16 22:22:47 +0200361 if (get_acpi(CM_ASL_CAMERA) == 0)
362 set_acpi(CM_ASL_CAMERA, 1);
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000363}
364
Matthew Garretta195dcd2008-08-19 12:13:20 +0100365/*
Eric Coopere59f8792008-03-13 12:55:46 +0100366 * Sys helpers
367 */
368static int parse_arg(const char *buf, unsigned long count, int *val)
369{
370 if (!count)
371 return 0;
372 if (sscanf(buf, "%i", val) != 1)
373 return -EINVAL;
374 return count;
375}
376
377static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
378{
379 int rv, value;
380
381 rv = parse_arg(buf, count, &value);
382 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200383 value = set_acpi(cm, value);
384 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000385 return -EIO;
Eric Coopere59f8792008-03-13 12:55:46 +0100386 return rv;
387}
388
389static ssize_t show_sys_acpi(int cm, char *buf)
390{
Corentin Charyf36509e2009-06-25 13:25:40 +0200391 int value = get_acpi(cm);
392
393 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000394 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200395 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100396}
397
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000398#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Eric Coopere59f8792008-03-13 12:55:46 +0100399 static ssize_t show_##_name(struct device *dev, \
400 struct device_attribute *attr, \
401 char *buf) \
402 { \
403 return show_sys_acpi(_cm, buf); \
404 } \
405 static ssize_t store_##_name(struct device *dev, \
406 struct device_attribute *attr, \
407 const char *buf, size_t count) \
408 { \
409 return store_sys_acpi(_cm, buf, count); \
410 } \
411 static struct device_attribute dev_attr_##_name = { \
412 .attr = { \
413 .name = __stringify(_name), \
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000414 .mode = _mode }, \
Eric Coopere59f8792008-03-13 12:55:46 +0100415 .show = show_##_name, \
416 .store = store_##_name, \
417 }
418
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000419EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
420EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
421EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000422
423struct eeepc_cpufv {
424 int num;
425 int cur;
426};
427
428static int get_cpufv(struct eeepc_cpufv *c)
429{
430 c->cur = get_acpi(CM_ASL_CPUFV);
431 c->num = (c->cur >> 8) & 0xff;
432 c->cur &= 0xff;
433 if (c->cur < 0 || c->num <= 0 || c->num > 12)
434 return -ENODEV;
435 return 0;
436}
437
438static ssize_t show_available_cpufv(struct device *dev,
439 struct device_attribute *attr,
440 char *buf)
441{
442 struct eeepc_cpufv c;
443 int i;
444 ssize_t len = 0;
445
446 if (get_cpufv(&c))
447 return -ENODEV;
448 for (i = 0; i < c.num; i++)
449 len += sprintf(buf + len, "%d ", i);
450 len += sprintf(buf + len, "\n");
451 return len;
452}
453
454static ssize_t show_cpufv(struct device *dev,
455 struct device_attribute *attr,
456 char *buf)
457{
458 struct eeepc_cpufv c;
459
460 if (get_cpufv(&c))
461 return -ENODEV;
462 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
463}
464
465static ssize_t store_cpufv(struct device *dev,
466 struct device_attribute *attr,
467 const char *buf, size_t count)
468{
469 struct eeepc_cpufv c;
470 int rv, value;
471
472 if (get_cpufv(&c))
473 return -ENODEV;
474 rv = parse_arg(buf, count, &value);
475 if (rv < 0)
476 return rv;
477 if (!rv || value < 0 || value >= c.num)
478 return -EINVAL;
479 set_acpi(CM_ASL_CPUFV, value);
480 return rv;
481}
482
483static struct device_attribute dev_attr_cpufv = {
484 .attr = {
485 .name = "cpufv",
486 .mode = 0644 },
487 .show = show_cpufv,
488 .store = store_cpufv
489};
490
491static struct device_attribute dev_attr_available_cpufv = {
492 .attr = {
493 .name = "available_cpufv",
494 .mode = 0444 },
495 .show = show_available_cpufv
496};
Eric Coopere59f8792008-03-13 12:55:46 +0100497
498static struct attribute *platform_attributes[] = {
499 &dev_attr_camera.attr,
500 &dev_attr_cardr.attr,
501 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200502 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000503 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100504 NULL
505};
506
507static struct attribute_group platform_attribute_group = {
508 .attrs = platform_attributes
509};
510
511/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000512 * LEDs
513 */
514/*
515 * These functions actually update the LED's, and are called from a
516 * workqueue. By doing this as separate work rather than when the LED
517 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
518 * potentially bad time, such as a timer interrupt.
519 */
520static int tpd_led_wk;
521
522static void tpd_led_update(struct work_struct *ignored)
523{
524 int value = tpd_led_wk;
525 set_acpi(CM_ASL_TPD, value);
526}
527
528static struct workqueue_struct *led_workqueue;
529static DECLARE_WORK(tpd_led_work, tpd_led_update);
530
531static void tpd_led_set(struct led_classdev *led_cdev,
532 enum led_brightness value)
533{
534 tpd_led_wk = (value > 0) ? 1 : 0;
535 queue_work(led_workqueue, &tpd_led_work);
536}
537
538static struct led_classdev tpd_led = {
539 .name = "eeepc::touchpad",
540 .brightness_set = tpd_led_set,
541 .max_brightness = 1
542};
543
544/*
Eric Coopere59f8792008-03-13 12:55:46 +0100545 * Hotkey functions
546 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100547static struct key_entry *eepc_get_entry_by_scancode(int code)
548{
549 struct key_entry *key;
550
551 for (key = eeepc_keymap; key->type != KE_END; key++)
552 if (code == key->code)
553 return key;
554
555 return NULL;
556}
557
558static struct key_entry *eepc_get_entry_by_keycode(int code)
559{
560 struct key_entry *key;
561
562 for (key = eeepc_keymap; key->type != KE_END; key++)
563 if (code == key->keycode && key->type == KE_KEY)
564 return key;
565
566 return NULL;
567}
568
569static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
570{
571 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
572
573 if (key && key->type == KE_KEY) {
574 *keycode = key->keycode;
575 return 0;
576 }
577
578 return -EINVAL;
579}
580
581static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
582{
583 struct key_entry *key;
584 int old_keycode;
585
586 if (keycode < 0 || keycode > KEY_MAX)
587 return -EINVAL;
588
589 key = eepc_get_entry_by_scancode(scancode);
590 if (key && key->type == KE_KEY) {
591 old_keycode = key->keycode;
592 key->keycode = keycode;
593 set_bit(keycode, dev->keybit);
594 if (!eepc_get_entry_by_keycode(old_keycode))
595 clear_bit(old_keycode, dev->keybit);
596 return 0;
597 }
598
599 return -EINVAL;
600}
601
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200602static void cmsg_quirk(int cm, const char *name)
603{
604 int dummy;
605
606 /* Some BIOSes do not report cm although it is avaliable.
607 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
608 if (!(ehotk->cm_supported & (1 << cm))
609 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
610 pr_info("%s (%x) not reported by BIOS,"
611 " enabling anyway\n", name, 1 << cm);
612 ehotk->cm_supported |= 1 << cm;
613 }
614}
615
616static void cmsg_quirks(void)
617{
618 cmsg_quirk(CM_ASL_LID, "LID");
619 cmsg_quirk(CM_ASL_TYPE, "TYPE");
620 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
621 cmsg_quirk(CM_ASL_TPD, "TPD");
622}
623
Eric Coopere59f8792008-03-13 12:55:46 +0100624static int eeepc_hotk_check(void)
625{
626 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
627 int result;
628
629 result = acpi_bus_get_status(ehotk->device);
630 if (result)
631 return result;
632 if (ehotk->device->status.present) {
633 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
634 &buffer)) {
Joe Perches19b53282009-06-25 13:25:37 +0200635 pr_err("Hotkey initialization failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100636 return -ENODEV;
637 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200638 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
Eric Coopere59f8792008-03-13 12:55:46 +0100639 }
640 /* get control methods supported */
641 if (read_acpi_int(ehotk->handle, "CMSG"
642 , &ehotk->cm_supported)) {
Joe Perches19b53282009-06-25 13:25:37 +0200643 pr_err("Get control methods supported failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100644 return -ENODEV;
645 } else {
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200646 cmsg_quirks();
Joe Perches19b53282009-06-25 13:25:37 +0200647 pr_info("Get control methods supported: 0x%x\n",
648 ehotk->cm_supported);
Eric Coopere59f8792008-03-13 12:55:46 +0100649 }
650 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200651 pr_err("Hotkey device not present, aborting\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100652 return -EINVAL;
653 }
654 return 0;
655}
656
Darren Salt64b86b62009-04-27 09:23:38 +0200657static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100658{
Darren Salt64b86b62009-04-27 09:23:38 +0200659 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100660 struct backlight_device *bd = eeepc_backlight_device;
Darren Salt64b86b62009-04-27 09:23:38 +0200661 if (bd) {
662 int old = bd->props.brightness;
Matthew Garrettd822d5c2009-07-14 17:06:04 +0100663 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
Darren Salt64b86b62009-04-27 09:23:38 +0200664 return old;
665 }
666 return -1;
Corentin Charya5fa4292008-03-13 12:56:37 +0100667}
668
Corentin Chary2b121bc2009-06-25 13:25:36 +0200669static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
670 u8 *value)
671{
672 int val = get_acpi(CM_ASL_WLAN);
673
674 if (val == 1 || val == 0)
675 *value = val;
676 else
677 return -EINVAL;
678
679 return 0;
680}
681
Corentin Chary58ce48a2009-10-16 22:22:46 +0200682static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100683{
684 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000685 struct pci_bus *bus;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200686 bool blocked = eeepc_wlan_rfkill_blocked();
Matthew Garrett57402942009-01-20 16:17:48 +0100687
Corentin Chary58ce48a2009-10-16 22:22:46 +0200688 if (ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000689 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000690
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000691 mutex_lock(&ehotk->hotplug_lock);
692
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000693 if (ehotk->hotplug_slot) {
694 bus = pci_find_bus(0, 1);
695 if (!bus) {
696 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000697 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100698 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000699
700 if (!blocked) {
701 dev = pci_get_slot(bus, 0);
702 if (dev) {
703 /* Device already present */
704 pci_dev_put(dev);
705 goto out_unlock;
706 }
707 dev = pci_scan_single_device(bus, 0);
708 if (dev) {
709 pci_bus_assign_resources(bus);
710 if (pci_bus_add_device(dev))
711 pr_err("Unable to hotplug wifi\n");
712 }
713 } else {
714 dev = pci_get_slot(bus, 0);
715 if (dev) {
716 pci_remove_bus_device(dev);
717 pci_dev_put(dev);
718 }
Matthew Garrett57402942009-01-20 16:17:48 +0100719 }
720 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000721
722out_unlock:
723 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100724}
725
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100726static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
727{
728 if (event != ACPI_NOTIFY_BUS_CHECK)
729 return;
730
Corentin Chary58ce48a2009-10-16 22:22:46 +0200731 eeepc_rfkill_hotplug();
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100732}
733
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600734static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100735{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100736 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100737 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200738 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100739
Eric Coopere59f8792008-03-13 12:55:46 +0100740 if (!ehotk)
741 return;
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600742 if (event > ACPI_MAX_SYS_NOTIFY)
743 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100744 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200745 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100746 count = ehotk->event_count[event % 128]++;
747 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100748 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
749 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100750 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100751 if (ehotk->inputdev) {
Darren Salt64b86b62009-04-27 09:23:38 +0200752 if (brn != -ENODEV) {
753 /* brightness-change events need special
754 * handling for conversion to key events
755 */
756 if (brn < 0)
757 brn = event;
758 else
759 brn += NOTIFY_BRN_MIN;
760 if (event < brn)
761 event = NOTIFY_BRN_MIN; /* brightness down */
762 else if (event > brn)
763 event = NOTIFY_BRN_MIN + 2; /* ... up */
764 else
765 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
766 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100767 key = eepc_get_entry_by_scancode(event);
768 if (key) {
769 switch (key->type) {
770 case KE_KEY:
771 input_report_key(ehotk->inputdev, key->keycode,
772 1);
773 input_sync(ehotk->inputdev);
774 input_report_key(ehotk->inputdev, key->keycode,
775 0);
776 input_sync(ehotk->inputdev);
777 break;
778 }
779 }
780 }
Eric Coopere59f8792008-03-13 12:55:46 +0100781}
782
Matthew Garrett57402942009-01-20 16:17:48 +0100783static int eeepc_register_rfkill_notifier(char *node)
784{
785 acpi_status status = AE_OK;
786 acpi_handle handle;
787
788 status = acpi_get_handle(NULL, node, &handle);
789
790 if (ACPI_SUCCESS(status)) {
791 status = acpi_install_notify_handler(handle,
792 ACPI_SYSTEM_NOTIFY,
793 eeepc_rfkill_notify,
794 NULL);
795 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200796 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100797 } else
798 return -ENODEV;
799
800 return 0;
801}
802
803static void eeepc_unregister_rfkill_notifier(char *node)
804{
805 acpi_status status = AE_OK;
806 acpi_handle handle;
807
808 status = acpi_get_handle(NULL, node, &handle);
809
810 if (ACPI_SUCCESS(status)) {
811 status = acpi_remove_notify_handler(handle,
812 ACPI_SYSTEM_NOTIFY,
813 eeepc_rfkill_notify);
814 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200815 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100816 node);
817 }
818}
819
Corentin Chary2b121bc2009-06-25 13:25:36 +0200820static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
821{
822 kfree(hotplug_slot->info);
823 kfree(hotplug_slot);
824}
825
826static int eeepc_setup_pci_hotplug(void)
827{
828 int ret = -ENOMEM;
829 struct pci_bus *bus = pci_find_bus(0, 1);
830
831 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200832 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200833 return -ENODEV;
834 }
835
836 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
837 if (!ehotk->hotplug_slot)
838 goto error_slot;
839
840 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
841 GFP_KERNEL);
842 if (!ehotk->hotplug_slot->info)
843 goto error_info;
844
845 ehotk->hotplug_slot->private = ehotk;
846 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
847 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
848 eeepc_get_adapter_status(ehotk->hotplug_slot,
849 &ehotk->hotplug_slot->info->adapter_status);
850
851 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
852 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200853 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200854 goto error_register;
855 }
856
857 return 0;
858
859error_register:
860 kfree(ehotk->hotplug_slot->info);
861error_info:
862 kfree(ehotk->hotplug_slot);
863 ehotk->hotplug_slot = NULL;
864error_slot:
865 return ret;
866}
867
Alan Jenkinsc200da52009-08-28 12:56:40 +0000868static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100869{
Corentin Chary7de39382009-06-25 13:25:38 +0200870 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100871 bool wlan;
872
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000873 /*
874 * Work around bios bug - acpi _PTS turns off the wireless led
875 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000876 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100877 */
878 wlan = get_acpi(CM_ASL_WLAN);
879 set_acpi(CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100880 }
881
Alan Jenkinsc200da52009-08-28 12:56:40 +0000882 return 0;
883}
884
885static int eeepc_hotk_restore(struct device *device)
886{
887 /* Refresh both wlan rfkill state and pci hotplug */
888 if (ehotk->wlan_rfkill)
Corentin Chary58ce48a2009-10-16 22:22:46 +0200889 eeepc_rfkill_hotplug();
Alan Jenkinsc200da52009-08-28 12:56:40 +0000890
Corentin Chary7de39382009-06-25 13:25:38 +0200891 if (ehotk->bluetooth_rfkill)
892 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100893 get_acpi(CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa4746102009-08-28 12:56:38 +0000894 if (ehotk->wwan3g_rfkill)
895 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
896 get_acpi(CM_ASL_3G) != 1);
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000897 if (ehotk->wimax_rfkill)
898 rfkill_set_sw_state(ehotk->wimax_rfkill,
899 get_acpi(CM_ASL_WIMAX) != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100900
901 return 0;
902}
903
Eric Coopere59f8792008-03-13 12:55:46 +0100904/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100905 * Hwmon
906 */
907static int eeepc_get_fan_pwm(void)
908{
909 int value = 0;
910
911 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200912 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100913 return (value);
914}
915
916static void eeepc_set_fan_pwm(int value)
917{
Corentin Chary04dcd842008-10-09 15:33:57 +0200918 value = SENSORS_LIMIT(value, 0, 255);
919 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100920 ec_write(EEEPC_EC_SC02, value);
921}
922
923static int eeepc_get_fan_rpm(void)
924{
925 int high = 0;
926 int low = 0;
927
928 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
929 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
930 return (high << 8 | low);
931}
932
933static int eeepc_get_fan_ctrl(void)
934{
935 int value = 0;
936
937 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000938 if (value & 0x02)
939 return 1; /* manual */
940 else
941 return 2; /* automatic */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100942}
943
944static void eeepc_set_fan_ctrl(int manual)
945{
946 int value = 0;
947
948 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000949 if (manual == 1)
Corentin Charye1faa9d2008-03-13 12:57:18 +0100950 value |= 0x02;
951 else
952 value &= ~0x02;
953 ec_write(EEEPC_EC_SFB3, value);
954}
955
956static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
957{
958 int rv, value;
959
960 rv = parse_arg(buf, count, &value);
961 if (rv > 0)
962 set(value);
963 return rv;
964}
965
966static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
967{
968 return sprintf(buf, "%d\n", get());
969}
970
971#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
972 static ssize_t show_##_name(struct device *dev, \
973 struct device_attribute *attr, \
974 char *buf) \
975 { \
976 return show_sys_hwmon(_set, buf); \
977 } \
978 static ssize_t store_##_name(struct device *dev, \
979 struct device_attribute *attr, \
980 const char *buf, size_t count) \
981 { \
982 return store_sys_hwmon(_get, buf, count); \
983 } \
984 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
985
986EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200987EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100988 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
989EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
990 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
991
Corentin Chary04dcd842008-10-09 15:33:57 +0200992static ssize_t
993show_name(struct device *dev, struct device_attribute *attr, char *buf)
994{
995 return sprintf(buf, "eeepc\n");
996}
997static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
998
Corentin Charye1faa9d2008-03-13 12:57:18 +0100999static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +02001000 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001001 &sensor_dev_attr_fan1_input.dev_attr.attr,
1002 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001003 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001004 NULL
1005};
1006
1007static struct attribute_group hwmon_attribute_group = {
1008 .attrs = hwmon_attributes
1009};
1010
1011/*
Eric Coopere59f8792008-03-13 12:55:46 +01001012 * exit/init
1013 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001014static void eeepc_backlight_exit(void)
1015{
1016 if (eeepc_backlight_device)
1017 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001018 eeepc_backlight_device = NULL;
1019}
1020
1021static void eeepc_rfkill_exit(void)
1022{
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001023 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +02001024 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1025 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001026 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001027 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001028 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001029 ehotk->wlan_rfkill = NULL;
1030 }
1031 /*
1032 * Refresh pci hotplug in case the rfkill state was changed after
1033 * eeepc_unregister_rfkill_notifier()
1034 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001035 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001036 if (ehotk->hotplug_slot)
1037 pci_hp_deregister(ehotk->hotplug_slot);
1038
Alan Jenkinsa82580692009-08-29 10:28:30 +02001039 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001040 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001041 rfkill_destroy(ehotk->bluetooth_rfkill);
1042 ehotk->bluetooth_rfkill = NULL;
1043 }
1044 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001045 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001046 rfkill_destroy(ehotk->wwan3g_rfkill);
1047 ehotk->wwan3g_rfkill = NULL;
1048 }
1049 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001050 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001051 rfkill_destroy(ehotk->wimax_rfkill);
1052 ehotk->wimax_rfkill = NULL;
1053 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001054}
1055
1056static void eeepc_input_exit(void)
1057{
1058 if (ehotk->inputdev)
1059 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001060}
1061
Corentin Charye1faa9d2008-03-13 12:57:18 +01001062static void eeepc_hwmon_exit(void)
1063{
1064 struct device *hwmon;
1065
1066 hwmon = eeepc_hwmon_device;
1067 if (!hwmon)
1068 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001069 sysfs_remove_group(&hwmon->kobj,
1070 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001071 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001072 eeepc_hwmon_device = NULL;
1073}
1074
Corentin Chary3c0eb512009-12-03 07:44:52 +00001075static void eeepc_led_exit(void)
1076{
Corentin Chary3c0eb512009-12-03 07:44:52 +00001077 if (tpd_led.dev)
1078 led_classdev_unregister(&tpd_led);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001079 if (led_workqueue)
1080 destroy_workqueue(led_workqueue);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001081}
1082
Corentin Chary7de39382009-06-25 13:25:38 +02001083static int eeepc_new_rfkill(struct rfkill **rfkill,
1084 const char *name, struct device *dev,
1085 enum rfkill_type type, int cm)
1086{
1087 int result;
1088
Corentin Charyf36509e2009-06-25 13:25:40 +02001089 result = get_acpi(cm);
1090 if (result < 0)
1091 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001092
1093 *rfkill = rfkill_alloc(name, dev, type,
1094 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1095
1096 if (!*rfkill)
1097 return -EINVAL;
1098
1099 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1100 result = rfkill_register(*rfkill);
1101 if (result) {
1102 rfkill_destroy(*rfkill);
1103 *rfkill = NULL;
1104 return result;
1105 }
1106 return 0;
1107}
1108
1109
1110static int eeepc_rfkill_init(struct device *dev)
1111{
1112 int result = 0;
1113
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001114 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001115
Corentin Chary7de39382009-06-25 13:25:38 +02001116 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1117 "eeepc-wlan", dev,
1118 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1119
1120 if (result && result != -ENODEV)
1121 goto exit;
1122
1123 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1124 "eeepc-bluetooth", dev,
1125 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1126
1127 if (result && result != -ENODEV)
1128 goto exit;
1129
Corentin Chary3cd530b2009-06-25 13:25:42 +02001130 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1131 "eeepc-wwan3g", dev,
1132 RFKILL_TYPE_WWAN, CM_ASL_3G);
1133
1134 if (result && result != -ENODEV)
1135 goto exit;
1136
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001137 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1138 "eeepc-wimax", dev,
1139 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1140
1141 if (result && result != -ENODEV)
1142 goto exit;
1143
Corentin Chary7de39382009-06-25 13:25:38 +02001144 result = eeepc_setup_pci_hotplug();
1145 /*
1146 * If we get -EBUSY then something else is handling the PCI hotplug -
1147 * don't fail in this case
1148 */
1149 if (result == -EBUSY)
1150 result = 0;
1151
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001152 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001153 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1154 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1155 /*
1156 * Refresh pci hotplug in case the rfkill state was changed during
1157 * setup.
1158 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001159 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001160
Corentin Chary7de39382009-06-25 13:25:38 +02001161exit:
1162 if (result && result != -ENODEV)
1163 eeepc_rfkill_exit();
1164 return result;
1165}
1166
Corentin Charya5fa4292008-03-13 12:56:37 +01001167static int eeepc_backlight_init(struct device *dev)
1168{
1169 struct backlight_device *bd;
1170
1171 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1172 NULL, &eeepcbl_ops);
1173 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001174 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001175 eeepc_backlight_device = NULL;
1176 return PTR_ERR(bd);
1177 }
1178 eeepc_backlight_device = bd;
1179 bd->props.max_brightness = 15;
1180 bd->props.brightness = read_brightness(NULL);
1181 bd->props.power = FB_BLANK_UNBLANK;
1182 backlight_update_status(bd);
1183 return 0;
1184}
1185
Corentin Charye1faa9d2008-03-13 12:57:18 +01001186static int eeepc_hwmon_init(struct device *dev)
1187{
1188 struct device *hwmon;
1189 int result;
1190
1191 hwmon = hwmon_device_register(dev);
1192 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001193 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001194 eeepc_hwmon_device = NULL;
1195 return PTR_ERR(hwmon);
1196 }
1197 eeepc_hwmon_device = hwmon;
1198 result = sysfs_create_group(&hwmon->kobj,
1199 &hwmon_attribute_group);
1200 if (result)
1201 eeepc_hwmon_exit();
1202 return result;
1203}
1204
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001205static int eeepc_input_init(struct device *dev)
1206{
1207 const struct key_entry *key;
1208 int result;
1209
1210 ehotk->inputdev = input_allocate_device();
1211 if (!ehotk->inputdev) {
1212 pr_info("Unable to allocate input device\n");
1213 return -ENOMEM;
1214 }
1215 ehotk->inputdev->name = "Asus EeePC extra buttons";
1216 ehotk->inputdev->dev.parent = dev;
1217 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1218 ehotk->inputdev->id.bustype = BUS_HOST;
1219 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1220 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1221
1222 for (key = eeepc_keymap; key->type != KE_END; key++) {
1223 switch (key->type) {
1224 case KE_KEY:
1225 set_bit(EV_KEY, ehotk->inputdev->evbit);
1226 set_bit(key->keycode, ehotk->inputdev->keybit);
1227 break;
1228 }
1229 }
1230 result = input_register_device(ehotk->inputdev);
1231 if (result) {
1232 pr_info("Unable to register input device\n");
1233 input_free_device(ehotk->inputdev);
1234 return result;
1235 }
1236 return 0;
1237}
1238
Corentin Chary3c0eb512009-12-03 07:44:52 +00001239static int eeepc_led_init(struct device *dev)
1240{
1241 int rv;
1242
1243 if (get_acpi(CM_ASL_TPD) == -ENODEV)
1244 return 0;
1245
Corentin Chary3c0eb512009-12-03 07:44:52 +00001246 led_workqueue = create_singlethread_workqueue("led_workqueue");
1247 if (!led_workqueue)
1248 return -ENOMEM;
1249
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001250 rv = led_classdev_register(dev, &tpd_led);
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001251 if (rv) {
1252 destroy_workqueue(led_workqueue);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001253 return rv;
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001254 }
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001255
Corentin Chary3c0eb512009-12-03 07:44:52 +00001256 return 0;
1257}
1258
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001259static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001260{
1261 struct device *dev;
1262 int result;
1263
Alan Jenkins1e779852009-08-28 12:56:35 +00001264 pr_notice(EEEPC_HOTK_NAME "\n");
1265 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1266 if (!ehotk)
1267 return -ENOMEM;
1268 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1269 ehotk->handle = device->handle;
1270 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1271 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1272 device->driver_data = ehotk;
1273 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001274
Alan Jenkins1e779852009-08-28 12:56:35 +00001275 result = eeepc_hotk_check();
1276 if (result)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001277 goto fail_platform_driver;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001278 eeepc_enable_camera();
1279
Eric Coopere59f8792008-03-13 12:55:46 +01001280 /* Register platform stuff */
1281 result = platform_driver_register(&platform_driver);
1282 if (result)
1283 goto fail_platform_driver;
1284 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1285 if (!platform_device) {
1286 result = -ENOMEM;
1287 goto fail_platform_device1;
1288 }
1289 result = platform_device_add(platform_device);
1290 if (result)
1291 goto fail_platform_device2;
1292 result = sysfs_create_group(&platform_device->dev.kobj,
1293 &platform_attribute_group);
1294 if (result)
1295 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001296
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001297 dev = &platform_device->dev;
1298
1299 if (!acpi_video_backlight_support()) {
1300 result = eeepc_backlight_init(dev);
1301 if (result)
1302 goto fail_backlight;
1303 } else
1304 pr_info("Backlight controlled by ACPI video "
1305 "driver\n");
1306
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001307 result = eeepc_input_init(dev);
1308 if (result)
1309 goto fail_input;
1310
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001311 result = eeepc_hwmon_init(dev);
1312 if (result)
1313 goto fail_hwmon;
1314
Corentin Chary3c0eb512009-12-03 07:44:52 +00001315 result = eeepc_led_init(dev);
1316 if (result)
1317 goto fail_led;
1318
Corentin Chary7de39382009-06-25 13:25:38 +02001319 result = eeepc_rfkill_init(dev);
1320 if (result)
1321 goto fail_rfkill;
1322
Eric Coopere59f8792008-03-13 12:55:46 +01001323 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001324
Corentin Chary7de39382009-06-25 13:25:38 +02001325fail_rfkill:
Corentin Chary3c0eb512009-12-03 07:44:52 +00001326 eeepc_led_exit();
1327fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001328 eeepc_hwmon_exit();
1329fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001330 eeepc_input_exit();
1331fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001332 eeepc_backlight_exit();
1333fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001334 sysfs_remove_group(&platform_device->dev.kobj,
1335 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001336fail_sysfs:
1337 platform_device_del(platform_device);
1338fail_platform_device2:
1339 platform_device_put(platform_device);
1340fail_platform_device1:
1341 platform_driver_unregister(&platform_driver);
1342fail_platform_driver:
Alan Jenkins1e779852009-08-28 12:56:35 +00001343 kfree(ehotk);
1344
Eric Coopere59f8792008-03-13 12:55:46 +01001345 return result;
1346}
1347
Alan Jenkins1e779852009-08-28 12:56:35 +00001348static int eeepc_hotk_remove(struct acpi_device *device, int type)
1349{
Alan Jenkins1e779852009-08-28 12:56:35 +00001350 eeepc_backlight_exit();
1351 eeepc_rfkill_exit();
1352 eeepc_input_exit();
1353 eeepc_hwmon_exit();
Corentin Chary3c0eb512009-12-03 07:44:52 +00001354 eeepc_led_exit();
Alan Jenkins1e779852009-08-28 12:56:35 +00001355 sysfs_remove_group(&platform_device->dev.kobj,
1356 &platform_attribute_group);
1357 platform_device_unregister(platform_device);
1358 platform_driver_unregister(&platform_driver);
1359
1360 kfree(ehotk);
1361 return 0;
1362}
1363
1364static int __init eeepc_laptop_init(void)
1365{
1366 int result;
1367
Alan Jenkins1e779852009-08-28 12:56:35 +00001368 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1369 if (result < 0)
1370 return result;
1371 if (!ehotk) {
1372 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1373 return -ENODEV;
1374 }
1375 return 0;
1376}
1377
1378static void __exit eeepc_laptop_exit(void)
1379{
1380 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1381}
1382
Eric Coopere59f8792008-03-13 12:55:46 +01001383module_init(eeepc_laptop_init);
1384module_exit(eeepc_laptop_exit);