blob: abd7389a449300a96af9b63d9cb59b89ba02f952 [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{
Alan Jenkins13f70022009-12-03 07:44:59 +0000292 const char *method = cm_setv[cm];
293
294 if (method == NULL)
295 return -ENODEV;
296 if ((ehotk->cm_supported & (0x1 << cm)) == 0)
297 return -ENODEV;
298
299 if (write_acpi_int(ehotk->handle, method, value, NULL))
300 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100301 return 0;
302}
303
304static int get_acpi(int cm)
305{
Alan Jenkins13f70022009-12-03 07:44:59 +0000306 const char *method = cm_getv[cm];
307 int value;
308
309 if (method == NULL)
310 return -ENODEV;
311 if ((ehotk->cm_supported & (0x1 << cm)) == 0)
312 return -ENODEV;
313
314 if (read_acpi_int(ehotk->handle, method, &value))
315 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100316 return value;
317}
318
319/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100320 * Backlight
321 */
322static int read_brightness(struct backlight_device *bd)
323{
324 return get_acpi(CM_ASL_PANELBRIGHT);
325}
326
327static int set_brightness(struct backlight_device *bd, int value)
328{
329 value = max(0, min(15, value));
330 return set_acpi(CM_ASL_PANELBRIGHT, value);
331}
332
333static int update_bl_status(struct backlight_device *bd)
334{
335 return set_brightness(bd, bd->props.brightness);
336}
337
338/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100339 * Rfkill helpers
340 */
341
Johannes Berg19d337d2009-06-02 13:01:37 +0200342static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100343{
344 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200345 return false;
346 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100347}
348
Johannes Berg19d337d2009-06-02 13:01:37 +0200349static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100350{
Johannes Berg19d337d2009-06-02 13:01:37 +0200351 unsigned long asl = (unsigned long)data;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200352 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100353}
354
Johannes Berg19d337d2009-06-02 13:01:37 +0200355static const struct rfkill_ops eeepc_rfkill_ops = {
356 .set_block = eeepc_rfkill_set,
357};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100358
Rakib Mullickdcb73ee2009-10-13 00:13:32 +0200359static void __devinit eeepc_enable_camera(void)
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000360{
361 /*
362 * If the following call to set_acpi() fails, it's because there's no
363 * camera so we can ignore the error.
364 */
Luca Niccoli80f0c892009-10-16 22:22:47 +0200365 if (get_acpi(CM_ASL_CAMERA) == 0)
366 set_acpi(CM_ASL_CAMERA, 1);
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000367}
368
Matthew Garretta195dcd2008-08-19 12:13:20 +0100369/*
Eric Coopere59f8792008-03-13 12:55:46 +0100370 * Sys helpers
371 */
372static int parse_arg(const char *buf, unsigned long count, int *val)
373{
374 if (!count)
375 return 0;
376 if (sscanf(buf, "%i", val) != 1)
377 return -EINVAL;
378 return count;
379}
380
381static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
382{
383 int rv, value;
384
385 rv = parse_arg(buf, count, &value);
386 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200387 value = set_acpi(cm, value);
388 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000389 return -EIO;
Eric Coopere59f8792008-03-13 12:55:46 +0100390 return rv;
391}
392
393static ssize_t show_sys_acpi(int cm, char *buf)
394{
Corentin Charyf36509e2009-06-25 13:25:40 +0200395 int value = get_acpi(cm);
396
397 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000398 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200399 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100400}
401
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000402#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Eric Coopere59f8792008-03-13 12:55:46 +0100403 static ssize_t show_##_name(struct device *dev, \
404 struct device_attribute *attr, \
405 char *buf) \
406 { \
407 return show_sys_acpi(_cm, buf); \
408 } \
409 static ssize_t store_##_name(struct device *dev, \
410 struct device_attribute *attr, \
411 const char *buf, size_t count) \
412 { \
413 return store_sys_acpi(_cm, buf, count); \
414 } \
415 static struct device_attribute dev_attr_##_name = { \
416 .attr = { \
417 .name = __stringify(_name), \
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000418 .mode = _mode }, \
Eric Coopere59f8792008-03-13 12:55:46 +0100419 .show = show_##_name, \
420 .store = store_##_name, \
421 }
422
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000423EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
424EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
425EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000426
427struct eeepc_cpufv {
428 int num;
429 int cur;
430};
431
432static int get_cpufv(struct eeepc_cpufv *c)
433{
434 c->cur = get_acpi(CM_ASL_CPUFV);
435 c->num = (c->cur >> 8) & 0xff;
436 c->cur &= 0xff;
437 if (c->cur < 0 || c->num <= 0 || c->num > 12)
438 return -ENODEV;
439 return 0;
440}
441
442static ssize_t show_available_cpufv(struct device *dev,
443 struct device_attribute *attr,
444 char *buf)
445{
446 struct eeepc_cpufv c;
447 int i;
448 ssize_t len = 0;
449
450 if (get_cpufv(&c))
451 return -ENODEV;
452 for (i = 0; i < c.num; i++)
453 len += sprintf(buf + len, "%d ", i);
454 len += sprintf(buf + len, "\n");
455 return len;
456}
457
458static ssize_t show_cpufv(struct device *dev,
459 struct device_attribute *attr,
460 char *buf)
461{
462 struct eeepc_cpufv c;
463
464 if (get_cpufv(&c))
465 return -ENODEV;
466 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
467}
468
469static ssize_t store_cpufv(struct device *dev,
470 struct device_attribute *attr,
471 const char *buf, size_t count)
472{
473 struct eeepc_cpufv c;
474 int rv, value;
475
476 if (get_cpufv(&c))
477 return -ENODEV;
478 rv = parse_arg(buf, count, &value);
479 if (rv < 0)
480 return rv;
481 if (!rv || value < 0 || value >= c.num)
482 return -EINVAL;
483 set_acpi(CM_ASL_CPUFV, value);
484 return rv;
485}
486
487static struct device_attribute dev_attr_cpufv = {
488 .attr = {
489 .name = "cpufv",
490 .mode = 0644 },
491 .show = show_cpufv,
492 .store = store_cpufv
493};
494
495static struct device_attribute dev_attr_available_cpufv = {
496 .attr = {
497 .name = "available_cpufv",
498 .mode = 0444 },
499 .show = show_available_cpufv
500};
Eric Coopere59f8792008-03-13 12:55:46 +0100501
502static struct attribute *platform_attributes[] = {
503 &dev_attr_camera.attr,
504 &dev_attr_cardr.attr,
505 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200506 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000507 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100508 NULL
509};
510
511static struct attribute_group platform_attribute_group = {
512 .attrs = platform_attributes
513};
514
515/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000516 * LEDs
517 */
518/*
519 * These functions actually update the LED's, and are called from a
520 * workqueue. By doing this as separate work rather than when the LED
521 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
522 * potentially bad time, such as a timer interrupt.
523 */
524static int tpd_led_wk;
525
526static void tpd_led_update(struct work_struct *ignored)
527{
528 int value = tpd_led_wk;
529 set_acpi(CM_ASL_TPD, value);
530}
531
532static struct workqueue_struct *led_workqueue;
533static DECLARE_WORK(tpd_led_work, tpd_led_update);
534
535static void tpd_led_set(struct led_classdev *led_cdev,
536 enum led_brightness value)
537{
538 tpd_led_wk = (value > 0) ? 1 : 0;
539 queue_work(led_workqueue, &tpd_led_work);
540}
541
542static struct led_classdev tpd_led = {
543 .name = "eeepc::touchpad",
544 .brightness_set = tpd_led_set,
545 .max_brightness = 1
546};
547
548/*
Eric Coopere59f8792008-03-13 12:55:46 +0100549 * Hotkey functions
550 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100551static struct key_entry *eepc_get_entry_by_scancode(int code)
552{
553 struct key_entry *key;
554
555 for (key = eeepc_keymap; key->type != KE_END; key++)
556 if (code == key->code)
557 return key;
558
559 return NULL;
560}
561
562static struct key_entry *eepc_get_entry_by_keycode(int code)
563{
564 struct key_entry *key;
565
566 for (key = eeepc_keymap; key->type != KE_END; key++)
567 if (code == key->keycode && key->type == KE_KEY)
568 return key;
569
570 return NULL;
571}
572
573static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
574{
575 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
576
577 if (key && key->type == KE_KEY) {
578 *keycode = key->keycode;
579 return 0;
580 }
581
582 return -EINVAL;
583}
584
585static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
586{
587 struct key_entry *key;
588 int old_keycode;
589
590 if (keycode < 0 || keycode > KEY_MAX)
591 return -EINVAL;
592
593 key = eepc_get_entry_by_scancode(scancode);
594 if (key && key->type == KE_KEY) {
595 old_keycode = key->keycode;
596 key->keycode = keycode;
597 set_bit(keycode, dev->keybit);
598 if (!eepc_get_entry_by_keycode(old_keycode))
599 clear_bit(old_keycode, dev->keybit);
600 return 0;
601 }
602
603 return -EINVAL;
604}
605
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200606static void cmsg_quirk(int cm, const char *name)
607{
608 int dummy;
609
610 /* Some BIOSes do not report cm although it is avaliable.
611 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
612 if (!(ehotk->cm_supported & (1 << cm))
613 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
614 pr_info("%s (%x) not reported by BIOS,"
615 " enabling anyway\n", name, 1 << cm);
616 ehotk->cm_supported |= 1 << cm;
617 }
618}
619
620static void cmsg_quirks(void)
621{
622 cmsg_quirk(CM_ASL_LID, "LID");
623 cmsg_quirk(CM_ASL_TYPE, "TYPE");
624 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
625 cmsg_quirk(CM_ASL_TPD, "TPD");
626}
627
Eric Coopere59f8792008-03-13 12:55:46 +0100628static int eeepc_hotk_check(void)
629{
630 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
631 int result;
632
633 result = acpi_bus_get_status(ehotk->device);
634 if (result)
635 return result;
636 if (ehotk->device->status.present) {
637 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
638 &buffer)) {
Joe Perches19b53282009-06-25 13:25:37 +0200639 pr_err("Hotkey initialization failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100640 return -ENODEV;
641 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200642 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
Eric Coopere59f8792008-03-13 12:55:46 +0100643 }
644 /* get control methods supported */
645 if (read_acpi_int(ehotk->handle, "CMSG"
646 , &ehotk->cm_supported)) {
Joe Perches19b53282009-06-25 13:25:37 +0200647 pr_err("Get control methods supported failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100648 return -ENODEV;
649 } else {
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200650 cmsg_quirks();
Joe Perches19b53282009-06-25 13:25:37 +0200651 pr_info("Get control methods supported: 0x%x\n",
652 ehotk->cm_supported);
Eric Coopere59f8792008-03-13 12:55:46 +0100653 }
654 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200655 pr_err("Hotkey device not present, aborting\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100656 return -EINVAL;
657 }
658 return 0;
659}
660
Darren Salt64b86b62009-04-27 09:23:38 +0200661static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100662{
Darren Salt64b86b62009-04-27 09:23:38 +0200663 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100664 struct backlight_device *bd = eeepc_backlight_device;
Darren Salt64b86b62009-04-27 09:23:38 +0200665 if (bd) {
666 int old = bd->props.brightness;
Matthew Garrettd822d5c2009-07-14 17:06:04 +0100667 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
Darren Salt64b86b62009-04-27 09:23:38 +0200668 return old;
669 }
670 return -1;
Corentin Charya5fa4292008-03-13 12:56:37 +0100671}
672
Corentin Chary2b121bc2009-06-25 13:25:36 +0200673static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
674 u8 *value)
675{
676 int val = get_acpi(CM_ASL_WLAN);
677
678 if (val == 1 || val == 0)
679 *value = val;
680 else
681 return -EINVAL;
682
683 return 0;
684}
685
Corentin Chary58ce48a2009-10-16 22:22:46 +0200686static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100687{
688 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000689 struct pci_bus *bus;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200690 bool blocked = eeepc_wlan_rfkill_blocked();
Matthew Garrett57402942009-01-20 16:17:48 +0100691
Corentin Chary58ce48a2009-10-16 22:22:46 +0200692 if (ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000693 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000694
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000695 mutex_lock(&ehotk->hotplug_lock);
696
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000697 if (ehotk->hotplug_slot) {
698 bus = pci_find_bus(0, 1);
699 if (!bus) {
700 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000701 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100702 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000703
704 if (!blocked) {
705 dev = pci_get_slot(bus, 0);
706 if (dev) {
707 /* Device already present */
708 pci_dev_put(dev);
709 goto out_unlock;
710 }
711 dev = pci_scan_single_device(bus, 0);
712 if (dev) {
713 pci_bus_assign_resources(bus);
714 if (pci_bus_add_device(dev))
715 pr_err("Unable to hotplug wifi\n");
716 }
717 } else {
718 dev = pci_get_slot(bus, 0);
719 if (dev) {
720 pci_remove_bus_device(dev);
721 pci_dev_put(dev);
722 }
Matthew Garrett57402942009-01-20 16:17:48 +0100723 }
724 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000725
726out_unlock:
727 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100728}
729
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100730static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
731{
732 if (event != ACPI_NOTIFY_BUS_CHECK)
733 return;
734
Corentin Chary58ce48a2009-10-16 22:22:46 +0200735 eeepc_rfkill_hotplug();
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100736}
737
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600738static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100739{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100740 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100741 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200742 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100743
Eric Coopere59f8792008-03-13 12:55:46 +0100744 if (!ehotk)
745 return;
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600746 if (event > ACPI_MAX_SYS_NOTIFY)
747 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100748 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200749 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100750 count = ehotk->event_count[event % 128]++;
751 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100752 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
753 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100754 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100755 if (ehotk->inputdev) {
Darren Salt64b86b62009-04-27 09:23:38 +0200756 if (brn != -ENODEV) {
757 /* brightness-change events need special
758 * handling for conversion to key events
759 */
760 if (brn < 0)
761 brn = event;
762 else
763 brn += NOTIFY_BRN_MIN;
764 if (event < brn)
765 event = NOTIFY_BRN_MIN; /* brightness down */
766 else if (event > brn)
767 event = NOTIFY_BRN_MIN + 2; /* ... up */
768 else
769 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
770 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100771 key = eepc_get_entry_by_scancode(event);
772 if (key) {
773 switch (key->type) {
774 case KE_KEY:
775 input_report_key(ehotk->inputdev, key->keycode,
776 1);
777 input_sync(ehotk->inputdev);
778 input_report_key(ehotk->inputdev, key->keycode,
779 0);
780 input_sync(ehotk->inputdev);
781 break;
782 }
783 }
784 }
Eric Coopere59f8792008-03-13 12:55:46 +0100785}
786
Matthew Garrett57402942009-01-20 16:17:48 +0100787static int eeepc_register_rfkill_notifier(char *node)
788{
789 acpi_status status = AE_OK;
790 acpi_handle handle;
791
792 status = acpi_get_handle(NULL, node, &handle);
793
794 if (ACPI_SUCCESS(status)) {
795 status = acpi_install_notify_handler(handle,
796 ACPI_SYSTEM_NOTIFY,
797 eeepc_rfkill_notify,
798 NULL);
799 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200800 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100801 } else
802 return -ENODEV;
803
804 return 0;
805}
806
807static void eeepc_unregister_rfkill_notifier(char *node)
808{
809 acpi_status status = AE_OK;
810 acpi_handle handle;
811
812 status = acpi_get_handle(NULL, node, &handle);
813
814 if (ACPI_SUCCESS(status)) {
815 status = acpi_remove_notify_handler(handle,
816 ACPI_SYSTEM_NOTIFY,
817 eeepc_rfkill_notify);
818 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200819 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100820 node);
821 }
822}
823
Corentin Chary2b121bc2009-06-25 13:25:36 +0200824static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
825{
826 kfree(hotplug_slot->info);
827 kfree(hotplug_slot);
828}
829
830static int eeepc_setup_pci_hotplug(void)
831{
832 int ret = -ENOMEM;
833 struct pci_bus *bus = pci_find_bus(0, 1);
834
835 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200836 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200837 return -ENODEV;
838 }
839
840 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
841 if (!ehotk->hotplug_slot)
842 goto error_slot;
843
844 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
845 GFP_KERNEL);
846 if (!ehotk->hotplug_slot->info)
847 goto error_info;
848
849 ehotk->hotplug_slot->private = ehotk;
850 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
851 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
852 eeepc_get_adapter_status(ehotk->hotplug_slot,
853 &ehotk->hotplug_slot->info->adapter_status);
854
855 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
856 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200857 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200858 goto error_register;
859 }
860
861 return 0;
862
863error_register:
864 kfree(ehotk->hotplug_slot->info);
865error_info:
866 kfree(ehotk->hotplug_slot);
867 ehotk->hotplug_slot = NULL;
868error_slot:
869 return ret;
870}
871
Alan Jenkinsc200da52009-08-28 12:56:40 +0000872static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100873{
Corentin Chary7de39382009-06-25 13:25:38 +0200874 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100875 bool wlan;
876
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000877 /*
878 * Work around bios bug - acpi _PTS turns off the wireless led
879 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000880 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100881 */
882 wlan = get_acpi(CM_ASL_WLAN);
883 set_acpi(CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100884 }
885
Alan Jenkinsc200da52009-08-28 12:56:40 +0000886 return 0;
887}
888
889static int eeepc_hotk_restore(struct device *device)
890{
891 /* Refresh both wlan rfkill state and pci hotplug */
892 if (ehotk->wlan_rfkill)
Corentin Chary58ce48a2009-10-16 22:22:46 +0200893 eeepc_rfkill_hotplug();
Alan Jenkinsc200da52009-08-28 12:56:40 +0000894
Corentin Chary7de39382009-06-25 13:25:38 +0200895 if (ehotk->bluetooth_rfkill)
896 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100897 get_acpi(CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa4746102009-08-28 12:56:38 +0000898 if (ehotk->wwan3g_rfkill)
899 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
900 get_acpi(CM_ASL_3G) != 1);
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000901 if (ehotk->wimax_rfkill)
902 rfkill_set_sw_state(ehotk->wimax_rfkill,
903 get_acpi(CM_ASL_WIMAX) != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100904
905 return 0;
906}
907
Eric Coopere59f8792008-03-13 12:55:46 +0100908/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100909 * Hwmon
910 */
911static int eeepc_get_fan_pwm(void)
912{
913 int value = 0;
914
915 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200916 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100917 return (value);
918}
919
920static void eeepc_set_fan_pwm(int value)
921{
Corentin Chary04dcd842008-10-09 15:33:57 +0200922 value = SENSORS_LIMIT(value, 0, 255);
923 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100924 ec_write(EEEPC_EC_SC02, value);
925}
926
927static int eeepc_get_fan_rpm(void)
928{
929 int high = 0;
930 int low = 0;
931
932 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
933 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
934 return (high << 8 | low);
935}
936
937static int eeepc_get_fan_ctrl(void)
938{
939 int value = 0;
940
941 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000942 if (value & 0x02)
943 return 1; /* manual */
944 else
945 return 2; /* automatic */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100946}
947
948static void eeepc_set_fan_ctrl(int manual)
949{
950 int value = 0;
951
952 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000953 if (manual == 1)
Corentin Charye1faa9d2008-03-13 12:57:18 +0100954 value |= 0x02;
955 else
956 value &= ~0x02;
957 ec_write(EEEPC_EC_SFB3, value);
958}
959
960static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
961{
962 int rv, value;
963
964 rv = parse_arg(buf, count, &value);
965 if (rv > 0)
966 set(value);
967 return rv;
968}
969
970static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
971{
972 return sprintf(buf, "%d\n", get());
973}
974
975#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
976 static ssize_t show_##_name(struct device *dev, \
977 struct device_attribute *attr, \
978 char *buf) \
979 { \
980 return show_sys_hwmon(_set, buf); \
981 } \
982 static ssize_t store_##_name(struct device *dev, \
983 struct device_attribute *attr, \
984 const char *buf, size_t count) \
985 { \
986 return store_sys_hwmon(_get, buf, count); \
987 } \
988 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
989
990EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200991EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100992 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
993EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
994 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
995
Corentin Chary04dcd842008-10-09 15:33:57 +0200996static ssize_t
997show_name(struct device *dev, struct device_attribute *attr, char *buf)
998{
999 return sprintf(buf, "eeepc\n");
1000}
1001static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
1002
Corentin Charye1faa9d2008-03-13 12:57:18 +01001003static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +02001004 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001005 &sensor_dev_attr_fan1_input.dev_attr.attr,
1006 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001007 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001008 NULL
1009};
1010
1011static struct attribute_group hwmon_attribute_group = {
1012 .attrs = hwmon_attributes
1013};
1014
1015/*
Eric Coopere59f8792008-03-13 12:55:46 +01001016 * exit/init
1017 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001018static void eeepc_backlight_exit(void)
1019{
1020 if (eeepc_backlight_device)
1021 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001022 eeepc_backlight_device = NULL;
1023}
1024
1025static void eeepc_rfkill_exit(void)
1026{
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001027 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +02001028 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1029 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001030 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001031 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001032 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001033 ehotk->wlan_rfkill = NULL;
1034 }
1035 /*
1036 * Refresh pci hotplug in case the rfkill state was changed after
1037 * eeepc_unregister_rfkill_notifier()
1038 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001039 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001040 if (ehotk->hotplug_slot)
1041 pci_hp_deregister(ehotk->hotplug_slot);
1042
Alan Jenkinsa82580692009-08-29 10:28:30 +02001043 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001044 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001045 rfkill_destroy(ehotk->bluetooth_rfkill);
1046 ehotk->bluetooth_rfkill = NULL;
1047 }
1048 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001049 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001050 rfkill_destroy(ehotk->wwan3g_rfkill);
1051 ehotk->wwan3g_rfkill = NULL;
1052 }
1053 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001054 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001055 rfkill_destroy(ehotk->wimax_rfkill);
1056 ehotk->wimax_rfkill = NULL;
1057 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001058}
1059
1060static void eeepc_input_exit(void)
1061{
1062 if (ehotk->inputdev)
1063 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001064}
1065
Corentin Charye1faa9d2008-03-13 12:57:18 +01001066static void eeepc_hwmon_exit(void)
1067{
1068 struct device *hwmon;
1069
1070 hwmon = eeepc_hwmon_device;
1071 if (!hwmon)
1072 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001073 sysfs_remove_group(&hwmon->kobj,
1074 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001075 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001076 eeepc_hwmon_device = NULL;
1077}
1078
Corentin Chary3c0eb512009-12-03 07:44:52 +00001079static void eeepc_led_exit(void)
1080{
Corentin Chary3c0eb512009-12-03 07:44:52 +00001081 if (tpd_led.dev)
1082 led_classdev_unregister(&tpd_led);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001083 if (led_workqueue)
1084 destroy_workqueue(led_workqueue);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001085}
1086
Corentin Chary7de39382009-06-25 13:25:38 +02001087static int eeepc_new_rfkill(struct rfkill **rfkill,
1088 const char *name, struct device *dev,
1089 enum rfkill_type type, int cm)
1090{
1091 int result;
1092
Corentin Charyf36509e2009-06-25 13:25:40 +02001093 result = get_acpi(cm);
1094 if (result < 0)
1095 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001096
1097 *rfkill = rfkill_alloc(name, dev, type,
1098 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1099
1100 if (!*rfkill)
1101 return -EINVAL;
1102
1103 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1104 result = rfkill_register(*rfkill);
1105 if (result) {
1106 rfkill_destroy(*rfkill);
1107 *rfkill = NULL;
1108 return result;
1109 }
1110 return 0;
1111}
1112
1113
1114static int eeepc_rfkill_init(struct device *dev)
1115{
1116 int result = 0;
1117
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001118 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001119
Corentin Chary7de39382009-06-25 13:25:38 +02001120 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1121 "eeepc-wlan", dev,
1122 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1123
1124 if (result && result != -ENODEV)
1125 goto exit;
1126
1127 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1128 "eeepc-bluetooth", dev,
1129 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1130
1131 if (result && result != -ENODEV)
1132 goto exit;
1133
Corentin Chary3cd530b2009-06-25 13:25:42 +02001134 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1135 "eeepc-wwan3g", dev,
1136 RFKILL_TYPE_WWAN, CM_ASL_3G);
1137
1138 if (result && result != -ENODEV)
1139 goto exit;
1140
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001141 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1142 "eeepc-wimax", dev,
1143 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1144
1145 if (result && result != -ENODEV)
1146 goto exit;
1147
Corentin Chary7de39382009-06-25 13:25:38 +02001148 result = eeepc_setup_pci_hotplug();
1149 /*
1150 * If we get -EBUSY then something else is handling the PCI hotplug -
1151 * don't fail in this case
1152 */
1153 if (result == -EBUSY)
1154 result = 0;
1155
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001156 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001157 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1158 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1159 /*
1160 * Refresh pci hotplug in case the rfkill state was changed during
1161 * setup.
1162 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001163 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001164
Corentin Chary7de39382009-06-25 13:25:38 +02001165exit:
1166 if (result && result != -ENODEV)
1167 eeepc_rfkill_exit();
1168 return result;
1169}
1170
Corentin Charya5fa4292008-03-13 12:56:37 +01001171static int eeepc_backlight_init(struct device *dev)
1172{
1173 struct backlight_device *bd;
1174
1175 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1176 NULL, &eeepcbl_ops);
1177 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001178 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001179 eeepc_backlight_device = NULL;
1180 return PTR_ERR(bd);
1181 }
1182 eeepc_backlight_device = bd;
1183 bd->props.max_brightness = 15;
1184 bd->props.brightness = read_brightness(NULL);
1185 bd->props.power = FB_BLANK_UNBLANK;
1186 backlight_update_status(bd);
1187 return 0;
1188}
1189
Corentin Charye1faa9d2008-03-13 12:57:18 +01001190static int eeepc_hwmon_init(struct device *dev)
1191{
1192 struct device *hwmon;
1193 int result;
1194
1195 hwmon = hwmon_device_register(dev);
1196 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001197 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001198 eeepc_hwmon_device = NULL;
1199 return PTR_ERR(hwmon);
1200 }
1201 eeepc_hwmon_device = hwmon;
1202 result = sysfs_create_group(&hwmon->kobj,
1203 &hwmon_attribute_group);
1204 if (result)
1205 eeepc_hwmon_exit();
1206 return result;
1207}
1208
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001209static int eeepc_input_init(struct device *dev)
1210{
1211 const struct key_entry *key;
1212 int result;
1213
1214 ehotk->inputdev = input_allocate_device();
1215 if (!ehotk->inputdev) {
1216 pr_info("Unable to allocate input device\n");
1217 return -ENOMEM;
1218 }
1219 ehotk->inputdev->name = "Asus EeePC extra buttons";
1220 ehotk->inputdev->dev.parent = dev;
1221 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1222 ehotk->inputdev->id.bustype = BUS_HOST;
1223 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1224 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1225
1226 for (key = eeepc_keymap; key->type != KE_END; key++) {
1227 switch (key->type) {
1228 case KE_KEY:
1229 set_bit(EV_KEY, ehotk->inputdev->evbit);
1230 set_bit(key->keycode, ehotk->inputdev->keybit);
1231 break;
1232 }
1233 }
1234 result = input_register_device(ehotk->inputdev);
1235 if (result) {
1236 pr_info("Unable to register input device\n");
1237 input_free_device(ehotk->inputdev);
1238 return result;
1239 }
1240 return 0;
1241}
1242
Corentin Chary3c0eb512009-12-03 07:44:52 +00001243static int eeepc_led_init(struct device *dev)
1244{
1245 int rv;
1246
1247 if (get_acpi(CM_ASL_TPD) == -ENODEV)
1248 return 0;
1249
Corentin Chary3c0eb512009-12-03 07:44:52 +00001250 led_workqueue = create_singlethread_workqueue("led_workqueue");
1251 if (!led_workqueue)
1252 return -ENOMEM;
1253
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001254 rv = led_classdev_register(dev, &tpd_led);
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001255 if (rv) {
1256 destroy_workqueue(led_workqueue);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001257 return rv;
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001258 }
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001259
Corentin Chary3c0eb512009-12-03 07:44:52 +00001260 return 0;
1261}
1262
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001263static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001264{
1265 struct device *dev;
1266 int result;
1267
Alan Jenkins1e779852009-08-28 12:56:35 +00001268 pr_notice(EEEPC_HOTK_NAME "\n");
1269 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1270 if (!ehotk)
1271 return -ENOMEM;
1272 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1273 ehotk->handle = device->handle;
1274 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1275 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1276 device->driver_data = ehotk;
1277 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001278
Alan Jenkins1e779852009-08-28 12:56:35 +00001279 result = eeepc_hotk_check();
1280 if (result)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001281 goto fail_platform_driver;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001282 eeepc_enable_camera();
1283
Eric Coopere59f8792008-03-13 12:55:46 +01001284 /* Register platform stuff */
1285 result = platform_driver_register(&platform_driver);
1286 if (result)
1287 goto fail_platform_driver;
1288 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1289 if (!platform_device) {
1290 result = -ENOMEM;
1291 goto fail_platform_device1;
1292 }
1293 result = platform_device_add(platform_device);
1294 if (result)
1295 goto fail_platform_device2;
1296 result = sysfs_create_group(&platform_device->dev.kobj,
1297 &platform_attribute_group);
1298 if (result)
1299 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001300
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001301 dev = &platform_device->dev;
1302
1303 if (!acpi_video_backlight_support()) {
1304 result = eeepc_backlight_init(dev);
1305 if (result)
1306 goto fail_backlight;
1307 } else
1308 pr_info("Backlight controlled by ACPI video "
1309 "driver\n");
1310
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001311 result = eeepc_input_init(dev);
1312 if (result)
1313 goto fail_input;
1314
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001315 result = eeepc_hwmon_init(dev);
1316 if (result)
1317 goto fail_hwmon;
1318
Corentin Chary3c0eb512009-12-03 07:44:52 +00001319 result = eeepc_led_init(dev);
1320 if (result)
1321 goto fail_led;
1322
Corentin Chary7de39382009-06-25 13:25:38 +02001323 result = eeepc_rfkill_init(dev);
1324 if (result)
1325 goto fail_rfkill;
1326
Eric Coopere59f8792008-03-13 12:55:46 +01001327 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001328
Corentin Chary7de39382009-06-25 13:25:38 +02001329fail_rfkill:
Corentin Chary3c0eb512009-12-03 07:44:52 +00001330 eeepc_led_exit();
1331fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001332 eeepc_hwmon_exit();
1333fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001334 eeepc_input_exit();
1335fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001336 eeepc_backlight_exit();
1337fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001338 sysfs_remove_group(&platform_device->dev.kobj,
1339 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001340fail_sysfs:
1341 platform_device_del(platform_device);
1342fail_platform_device2:
1343 platform_device_put(platform_device);
1344fail_platform_device1:
1345 platform_driver_unregister(&platform_driver);
1346fail_platform_driver:
Alan Jenkins1e779852009-08-28 12:56:35 +00001347 kfree(ehotk);
1348
Eric Coopere59f8792008-03-13 12:55:46 +01001349 return result;
1350}
1351
Alan Jenkins1e779852009-08-28 12:56:35 +00001352static int eeepc_hotk_remove(struct acpi_device *device, int type)
1353{
Alan Jenkins1e779852009-08-28 12:56:35 +00001354 eeepc_backlight_exit();
1355 eeepc_rfkill_exit();
1356 eeepc_input_exit();
1357 eeepc_hwmon_exit();
Corentin Chary3c0eb512009-12-03 07:44:52 +00001358 eeepc_led_exit();
Alan Jenkins1e779852009-08-28 12:56:35 +00001359 sysfs_remove_group(&platform_device->dev.kobj,
1360 &platform_attribute_group);
1361 platform_device_unregister(platform_device);
1362 platform_driver_unregister(&platform_driver);
1363
1364 kfree(ehotk);
1365 return 0;
1366}
1367
1368static int __init eeepc_laptop_init(void)
1369{
1370 int result;
1371
Alan Jenkins1e779852009-08-28 12:56:35 +00001372 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1373 if (result < 0)
1374 return result;
1375 if (!ehotk) {
1376 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1377 return -ENODEV;
1378 }
1379 return 0;
1380}
1381
1382static void __exit eeepc_laptop_exit(void)
1383{
1384 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1385}
1386
Eric Coopere59f8792008-03-13 12:55:46 +01001387module_init(eeepc_laptop_init);
1388module_exit(eeepc_laptop_exit);