blob: ec560f16d7204a20c42b874d7e54902aaf2762cd [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>
Eric Coopere59f8792008-03-13 12:55:46 +010037
38#define EEEPC_LAPTOP_VERSION "0.1"
39
40#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
41#define EEEPC_HOTK_FILE "eeepc"
42#define EEEPC_HOTK_CLASS "hotkey"
43#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
44#define EEEPC_HOTK_HID "ASUS010"
45
Eric Coopere59f8792008-03-13 12:55:46 +010046
47/*
48 * Definitions for Asus EeePC
49 */
50#define NOTIFY_WLAN_ON 0x10
Corentin Charya5fa4292008-03-13 12:56:37 +010051#define NOTIFY_BRN_MIN 0x20
52#define NOTIFY_BRN_MAX 0x2f
Eric Coopere59f8792008-03-13 12:55:46 +010053
54enum {
55 DISABLE_ASL_WLAN = 0x0001,
56 DISABLE_ASL_BLUETOOTH = 0x0002,
57 DISABLE_ASL_IRDA = 0x0004,
58 DISABLE_ASL_CAMERA = 0x0008,
59 DISABLE_ASL_TV = 0x0010,
60 DISABLE_ASL_GPS = 0x0020,
61 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
62 DISABLE_ASL_MODEM = 0x0080,
Corentin Charyb7b700d2009-06-16 19:28:52 +000063 DISABLE_ASL_CARDREADER = 0x0100,
64 DISABLE_ASL_3G = 0x0200,
65 DISABLE_ASL_WIMAX = 0x0400,
66 DISABLE_ASL_HWCF = 0x0800
Eric Coopere59f8792008-03-13 12:55:46 +010067};
68
69enum {
70 CM_ASL_WLAN = 0,
71 CM_ASL_BLUETOOTH,
72 CM_ASL_IRDA,
73 CM_ASL_1394,
74 CM_ASL_CAMERA,
75 CM_ASL_TV,
76 CM_ASL_GPS,
77 CM_ASL_DVDROM,
78 CM_ASL_DISPLAYSWITCH,
79 CM_ASL_PANELBRIGHT,
80 CM_ASL_BIOSFLASH,
81 CM_ASL_ACPIFLASH,
82 CM_ASL_CPUFV,
83 CM_ASL_CPUTEMPERATURE,
84 CM_ASL_FANCPU,
85 CM_ASL_FANCHASSIS,
86 CM_ASL_USBPORT1,
87 CM_ASL_USBPORT2,
88 CM_ASL_USBPORT3,
89 CM_ASL_MODEM,
90 CM_ASL_CARDREADER,
Corentin Charyb7b700d2009-06-16 19:28:52 +000091 CM_ASL_3G,
92 CM_ASL_WIMAX,
93 CM_ASL_HWCF,
94 CM_ASL_LID,
95 CM_ASL_TYPE,
96 CM_ASL_PANELPOWER, /*P901*/
97 CM_ASL_TPD
Eric Coopere59f8792008-03-13 12:55:46 +010098};
99
Adrian Bunk14109462008-06-25 19:25:47 +0300100static const char *cm_getv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000101 "WLDG", "BTHG", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100102 "CAMG", NULL, NULL, NULL,
103 NULL, "PBLG", NULL, NULL,
104 "CFVG", NULL, NULL, NULL,
105 "USBG", NULL, NULL, "MODG",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000106 "CRDG", "M3GG", "WIMG", "HWCF",
107 "LIDG", "TYPE", "PBPG", "TPDG"
Eric Coopere59f8792008-03-13 12:55:46 +0100108};
109
Adrian Bunk14109462008-06-25 19:25:47 +0300110static const char *cm_setv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000111 "WLDS", "BTHS", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100112 "CAMS", NULL, NULL, NULL,
113 "SDSP", "PBLS", "HDPS", NULL,
114 "CFVS", NULL, NULL, NULL,
115 "USBG", NULL, NULL, "MODS",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000116 "CRDS", "M3GS", "WIMS", NULL,
117 NULL, NULL, "PBPS", "TPDS"
Eric Coopere59f8792008-03-13 12:55:46 +0100118};
119
Corentin Charye1faa9d2008-03-13 12:57:18 +0100120#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
121
122#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
123#define EEEPC_EC_SC02 0x63
124#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
125#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
126#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
127#define EEEPC_EC_SFB3 0xD3
128
Eric Coopere59f8792008-03-13 12:55:46 +0100129/*
130 * This is the main structure, we can use it to store useful information
131 * about the hotk device
132 */
133struct eeepc_hotk {
134 struct acpi_device *device; /* the device we are in */
135 acpi_handle handle; /* the handle of the hotk device */
136 u32 cm_supported; /* the control methods supported
137 by this BIOS */
138 uint init_flag; /* Init flags */
139 u16 event_count[128]; /* count for each event */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100140 struct input_dev *inputdev;
141 u16 *keycode_map;
Corentin Chary7de39382009-06-25 13:25:38 +0200142 struct rfkill *wlan_rfkill;
143 struct rfkill *bluetooth_rfkill;
Corentin Chary3cd530b2009-06-25 13:25:42 +0200144 struct rfkill *wwan3g_rfkill;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200145 struct hotplug_slot *hotplug_slot;
Eric Coopere59f8792008-03-13 12:55:46 +0100146};
147
148/* The actual device the driver binds to */
149static struct eeepc_hotk *ehotk;
150
151/* Platform device/driver */
152static struct platform_driver platform_driver = {
153 .driver = {
154 .name = EEEPC_HOTK_FILE,
155 .owner = THIS_MODULE,
156 }
157};
158
159static struct platform_device *platform_device;
160
Matthew Garretta195dcd2008-08-19 12:13:20 +0100161struct key_entry {
162 char type;
163 u8 code;
164 u16 keycode;
165};
166
167enum { KE_KEY, KE_END };
168
169static struct key_entry eeepc_keymap[] = {
170 /* Sleep already handled via generic ACPI code */
171 {KE_KEY, 0x10, KEY_WLAN },
Alan Jenkins978605c2009-04-27 09:23:39 +0200172 {KE_KEY, 0x11, KEY_WLAN },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100173 {KE_KEY, 0x12, KEY_PROG1 },
174 {KE_KEY, 0x13, KEY_MUTE },
175 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
176 {KE_KEY, 0x15, KEY_VOLUMEUP },
Matthew Garrettb5f6f262009-01-20 16:17:46 +0100177 {KE_KEY, 0x1a, KEY_COFFEE },
178 {KE_KEY, 0x1b, KEY_ZOOM },
179 {KE_KEY, 0x1c, KEY_PROG2 },
180 {KE_KEY, 0x1d, KEY_PROG3 },
Darren Salt64b86b62009-04-27 09:23:38 +0200181 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
182 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100183 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
184 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
185 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
186 {KE_END, 0},
187};
188
Eric Coopere59f8792008-03-13 12:55:46 +0100189/*
190 * The hotkey driver declaration
191 */
192static int eeepc_hotk_add(struct acpi_device *device);
193static int eeepc_hotk_remove(struct acpi_device *device, int type);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100194static int eeepc_hotk_resume(struct acpi_device *device);
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600195static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
Eric Coopere59f8792008-03-13 12:55:46 +0100196
197static const struct acpi_device_id eeepc_device_ids[] = {
198 {EEEPC_HOTK_HID, 0},
199 {"", 0},
200};
201MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
202
203static struct acpi_driver eeepc_hotk_driver = {
204 .name = EEEPC_HOTK_NAME,
205 .class = EEEPC_HOTK_CLASS,
206 .ids = eeepc_device_ids,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600207 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Eric Coopere59f8792008-03-13 12:55:46 +0100208 .ops = {
209 .add = eeepc_hotk_add,
210 .remove = eeepc_hotk_remove,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100211 .resume = eeepc_hotk_resume,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600212 .notify = eeepc_hotk_notify,
Eric Coopere59f8792008-03-13 12:55:46 +0100213 },
214};
215
Corentin Chary2b121bc2009-06-25 13:25:36 +0200216/* PCI hotplug ops */
217static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
218
219static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
220 .owner = THIS_MODULE,
221 .get_adapter_status = eeepc_get_adapter_status,
222 .get_power_status = eeepc_get_adapter_status,
223};
224
Corentin Charya5fa4292008-03-13 12:56:37 +0100225/* The backlight device /sys/class/backlight */
226static struct backlight_device *eeepc_backlight_device;
227
Corentin Charye1faa9d2008-03-13 12:57:18 +0100228/* The hwmon device */
229static struct device *eeepc_hwmon_device;
230
Corentin Charya5fa4292008-03-13 12:56:37 +0100231/*
232 * The backlight class declaration
233 */
234static int read_brightness(struct backlight_device *bd);
235static int update_bl_status(struct backlight_device *bd);
236static struct backlight_ops eeepcbl_ops = {
237 .get_brightness = read_brightness,
238 .update_status = update_bl_status,
239};
240
Eric Coopere59f8792008-03-13 12:55:46 +0100241MODULE_AUTHOR("Corentin Chary, Eric Cooper");
242MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
243MODULE_LICENSE("GPL");
244
245/*
246 * ACPI Helpers
247 */
248static int write_acpi_int(acpi_handle handle, const char *method, int val,
249 struct acpi_buffer *output)
250{
251 struct acpi_object_list params;
252 union acpi_object in_obj;
253 acpi_status status;
254
255 params.count = 1;
256 params.pointer = &in_obj;
257 in_obj.type = ACPI_TYPE_INTEGER;
258 in_obj.integer.value = val;
259
260 status = acpi_evaluate_object(handle, (char *)method, &params, output);
261 return (status == AE_OK ? 0 : -1);
262}
263
264static int read_acpi_int(acpi_handle handle, const char *method, int *val)
265{
266 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400267 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100268
269 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
270 if (ACPI_FAILURE(status)) {
271 *val = -1;
272 return -1;
273 } else {
274 *val = result;
275 return 0;
276 }
277}
278
279static int set_acpi(int cm, int value)
280{
281 if (ehotk->cm_supported & (0x1 << cm)) {
282 const char *method = cm_setv[cm];
283 if (method == NULL)
284 return -ENODEV;
285 if (write_acpi_int(ehotk->handle, method, value, NULL))
Joe Perches19b53282009-06-25 13:25:37 +0200286 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100287 }
288 return 0;
289}
290
291static int get_acpi(int cm)
292{
Corentin Charyf36509e2009-06-25 13:25:40 +0200293 int value = -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100294 if ((ehotk->cm_supported & (0x1 << cm))) {
295 const char *method = cm_getv[cm];
296 if (method == NULL)
297 return -ENODEV;
298 if (read_acpi_int(ehotk->handle, method, &value))
Joe Perches19b53282009-06-25 13:25:37 +0200299 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100300 }
301 return value;
302}
303
304/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100305 * Backlight
306 */
307static int read_brightness(struct backlight_device *bd)
308{
309 return get_acpi(CM_ASL_PANELBRIGHT);
310}
311
312static int set_brightness(struct backlight_device *bd, int value)
313{
314 value = max(0, min(15, value));
315 return set_acpi(CM_ASL_PANELBRIGHT, value);
316}
317
318static int update_bl_status(struct backlight_device *bd)
319{
320 return set_brightness(bd, bd->props.brightness);
321}
322
323/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100324 * Rfkill helpers
325 */
326
Johannes Berg19d337d2009-06-02 13:01:37 +0200327static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100328{
329 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200330 return false;
331 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100332}
333
Johannes Berg19d337d2009-06-02 13:01:37 +0200334static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100335{
Johannes Berg19d337d2009-06-02 13:01:37 +0200336 unsigned long asl = (unsigned long)data;
337 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100338}
339
Johannes Berg19d337d2009-06-02 13:01:37 +0200340static const struct rfkill_ops eeepc_rfkill_ops = {
341 .set_block = eeepc_rfkill_set,
342};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100343
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000344static void __init eeepc_enable_camera(void)
345{
346 /*
347 * If the following call to set_acpi() fails, it's because there's no
348 * camera so we can ignore the error.
349 */
350 set_acpi(CM_ASL_CAMERA, 1);
351}
352
Matthew Garretta195dcd2008-08-19 12:13:20 +0100353/*
Eric Coopere59f8792008-03-13 12:55:46 +0100354 * Sys helpers
355 */
356static int parse_arg(const char *buf, unsigned long count, int *val)
357{
358 if (!count)
359 return 0;
360 if (sscanf(buf, "%i", val) != 1)
361 return -EINVAL;
362 return count;
363}
364
365static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
366{
367 int rv, value;
368
369 rv = parse_arg(buf, count, &value);
370 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200371 value = set_acpi(cm, value);
372 if (value < 0)
373 return value;
Eric Coopere59f8792008-03-13 12:55:46 +0100374 return rv;
375}
376
377static ssize_t show_sys_acpi(int cm, char *buf)
378{
Corentin Charyf36509e2009-06-25 13:25:40 +0200379 int value = get_acpi(cm);
380
381 if (value < 0)
382 return value;
383 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100384}
385
386#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
387 static ssize_t show_##_name(struct device *dev, \
388 struct device_attribute *attr, \
389 char *buf) \
390 { \
391 return show_sys_acpi(_cm, buf); \
392 } \
393 static ssize_t store_##_name(struct device *dev, \
394 struct device_attribute *attr, \
395 const char *buf, size_t count) \
396 { \
397 return store_sys_acpi(_cm, buf, count); \
398 } \
399 static struct device_attribute dev_attr_##_name = { \
400 .attr = { \
401 .name = __stringify(_name), \
402 .mode = 0644 }, \
403 .show = show_##_name, \
404 .store = store_##_name, \
405 }
406
407EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
408EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
409EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000410
411struct eeepc_cpufv {
412 int num;
413 int cur;
414};
415
416static int get_cpufv(struct eeepc_cpufv *c)
417{
418 c->cur = get_acpi(CM_ASL_CPUFV);
419 c->num = (c->cur >> 8) & 0xff;
420 c->cur &= 0xff;
421 if (c->cur < 0 || c->num <= 0 || c->num > 12)
422 return -ENODEV;
423 return 0;
424}
425
426static ssize_t show_available_cpufv(struct device *dev,
427 struct device_attribute *attr,
428 char *buf)
429{
430 struct eeepc_cpufv c;
431 int i;
432 ssize_t len = 0;
433
434 if (get_cpufv(&c))
435 return -ENODEV;
436 for (i = 0; i < c.num; i++)
437 len += sprintf(buf + len, "%d ", i);
438 len += sprintf(buf + len, "\n");
439 return len;
440}
441
442static ssize_t show_cpufv(struct device *dev,
443 struct device_attribute *attr,
444 char *buf)
445{
446 struct eeepc_cpufv c;
447
448 if (get_cpufv(&c))
449 return -ENODEV;
450 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
451}
452
453static ssize_t store_cpufv(struct device *dev,
454 struct device_attribute *attr,
455 const char *buf, size_t count)
456{
457 struct eeepc_cpufv c;
458 int rv, value;
459
460 if (get_cpufv(&c))
461 return -ENODEV;
462 rv = parse_arg(buf, count, &value);
463 if (rv < 0)
464 return rv;
465 if (!rv || value < 0 || value >= c.num)
466 return -EINVAL;
467 set_acpi(CM_ASL_CPUFV, value);
468 return rv;
469}
470
471static struct device_attribute dev_attr_cpufv = {
472 .attr = {
473 .name = "cpufv",
474 .mode = 0644 },
475 .show = show_cpufv,
476 .store = store_cpufv
477};
478
479static struct device_attribute dev_attr_available_cpufv = {
480 .attr = {
481 .name = "available_cpufv",
482 .mode = 0444 },
483 .show = show_available_cpufv
484};
Eric Coopere59f8792008-03-13 12:55:46 +0100485
486static struct attribute *platform_attributes[] = {
487 &dev_attr_camera.attr,
488 &dev_attr_cardr.attr,
489 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200490 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000491 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100492 NULL
493};
494
495static struct attribute_group platform_attribute_group = {
496 .attrs = platform_attributes
497};
498
499/*
500 * Hotkey functions
501 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100502static struct key_entry *eepc_get_entry_by_scancode(int code)
503{
504 struct key_entry *key;
505
506 for (key = eeepc_keymap; key->type != KE_END; key++)
507 if (code == key->code)
508 return key;
509
510 return NULL;
511}
512
513static struct key_entry *eepc_get_entry_by_keycode(int code)
514{
515 struct key_entry *key;
516
517 for (key = eeepc_keymap; key->type != KE_END; key++)
518 if (code == key->keycode && key->type == KE_KEY)
519 return key;
520
521 return NULL;
522}
523
524static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
525{
526 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
527
528 if (key && key->type == KE_KEY) {
529 *keycode = key->keycode;
530 return 0;
531 }
532
533 return -EINVAL;
534}
535
536static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
537{
538 struct key_entry *key;
539 int old_keycode;
540
541 if (keycode < 0 || keycode > KEY_MAX)
542 return -EINVAL;
543
544 key = eepc_get_entry_by_scancode(scancode);
545 if (key && key->type == KE_KEY) {
546 old_keycode = key->keycode;
547 key->keycode = keycode;
548 set_bit(keycode, dev->keybit);
549 if (!eepc_get_entry_by_keycode(old_keycode))
550 clear_bit(old_keycode, dev->keybit);
551 return 0;
552 }
553
554 return -EINVAL;
555}
556
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200557static void cmsg_quirk(int cm, const char *name)
558{
559 int dummy;
560
561 /* Some BIOSes do not report cm although it is avaliable.
562 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
563 if (!(ehotk->cm_supported & (1 << cm))
564 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
565 pr_info("%s (%x) not reported by BIOS,"
566 " enabling anyway\n", name, 1 << cm);
567 ehotk->cm_supported |= 1 << cm;
568 }
569}
570
571static void cmsg_quirks(void)
572{
573 cmsg_quirk(CM_ASL_LID, "LID");
574 cmsg_quirk(CM_ASL_TYPE, "TYPE");
575 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
576 cmsg_quirk(CM_ASL_TPD, "TPD");
577}
578
Eric Coopere59f8792008-03-13 12:55:46 +0100579static int eeepc_hotk_check(void)
580{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100581 const struct key_entry *key;
Eric Coopere59f8792008-03-13 12:55:46 +0100582 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
583 int result;
584
585 result = acpi_bus_get_status(ehotk->device);
586 if (result)
587 return result;
588 if (ehotk->device->status.present) {
589 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
590 &buffer)) {
Joe Perches19b53282009-06-25 13:25:37 +0200591 pr_err("Hotkey initialization failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100592 return -ENODEV;
593 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200594 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
Eric Coopere59f8792008-03-13 12:55:46 +0100595 }
596 /* get control methods supported */
597 if (read_acpi_int(ehotk->handle, "CMSG"
598 , &ehotk->cm_supported)) {
Joe Perches19b53282009-06-25 13:25:37 +0200599 pr_err("Get control methods supported failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100600 return -ENODEV;
601 } else {
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200602 cmsg_quirks();
Joe Perches19b53282009-06-25 13:25:37 +0200603 pr_info("Get control methods supported: 0x%x\n",
604 ehotk->cm_supported);
Eric Coopere59f8792008-03-13 12:55:46 +0100605 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100606 ehotk->inputdev = input_allocate_device();
607 if (!ehotk->inputdev) {
Joe Perches19b53282009-06-25 13:25:37 +0200608 pr_info("Unable to allocate input device\n");
Matthew Garretta195dcd2008-08-19 12:13:20 +0100609 return 0;
610 }
611 ehotk->inputdev->name = "Asus EeePC extra buttons";
612 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
613 ehotk->inputdev->id.bustype = BUS_HOST;
614 ehotk->inputdev->getkeycode = eeepc_getkeycode;
615 ehotk->inputdev->setkeycode = eeepc_setkeycode;
616
617 for (key = eeepc_keymap; key->type != KE_END; key++) {
618 switch (key->type) {
619 case KE_KEY:
620 set_bit(EV_KEY, ehotk->inputdev->evbit);
621 set_bit(key->keycode, ehotk->inputdev->keybit);
622 break;
623 }
624 }
625 result = input_register_device(ehotk->inputdev);
626 if (result) {
Joe Perches19b53282009-06-25 13:25:37 +0200627 pr_info("Unable to register input device\n");
Matthew Garretta195dcd2008-08-19 12:13:20 +0100628 input_free_device(ehotk->inputdev);
629 return 0;
630 }
Eric Coopere59f8792008-03-13 12:55:46 +0100631 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200632 pr_err("Hotkey device not present, aborting\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100633 return -EINVAL;
634 }
635 return 0;
636}
637
Darren Salt64b86b62009-04-27 09:23:38 +0200638static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100639{
Darren Salt64b86b62009-04-27 09:23:38 +0200640 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100641 struct backlight_device *bd = eeepc_backlight_device;
Darren Salt64b86b62009-04-27 09:23:38 +0200642 if (bd) {
643 int old = bd->props.brightness;
Darren Salt7695fb02009-02-07 01:02:07 -0500644 bd->props.brightness = read_brightness(bd);
Darren Salt64b86b62009-04-27 09:23:38 +0200645 return old;
646 }
647 return -1;
Corentin Charya5fa4292008-03-13 12:56:37 +0100648}
649
Corentin Chary2b121bc2009-06-25 13:25:36 +0200650static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
651 u8 *value)
652{
653 int val = get_acpi(CM_ASL_WLAN);
654
655 if (val == 1 || val == 0)
656 *value = val;
657 else
658 return -EINVAL;
659
660 return 0;
661}
662
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100663static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100664{
665 struct pci_dev *dev;
666 struct pci_bus *bus = pci_find_bus(0, 1);
Johannes Berg19d337d2009-06-02 13:01:37 +0200667 bool blocked;
Matthew Garrett57402942009-01-20 16:17:48 +0100668
Matthew Garrett57402942009-01-20 16:17:48 +0100669 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200670 pr_warning("Unable to find PCI bus 1?\n");
Matthew Garrett57402942009-01-20 16:17:48 +0100671 return;
672 }
673
Johannes Berg19d337d2009-06-02 13:01:37 +0200674 blocked = eeepc_wlan_rfkill_blocked();
675 if (!blocked) {
Matthew Garrett57402942009-01-20 16:17:48 +0100676 dev = pci_get_slot(bus, 0);
677 if (dev) {
678 /* Device already present */
679 pci_dev_put(dev);
680 return;
681 }
682 dev = pci_scan_single_device(bus, 0);
683 if (dev) {
684 pci_bus_assign_resources(bus);
685 if (pci_bus_add_device(dev))
Joe Perches19b53282009-06-25 13:25:37 +0200686 pr_err("Unable to hotplug wifi\n");
Matthew Garrett57402942009-01-20 16:17:48 +0100687 }
688 } else {
689 dev = pci_get_slot(bus, 0);
690 if (dev) {
691 pci_remove_bus_device(dev);
692 pci_dev_put(dev);
693 }
694 }
Alan Jenkins978605c2009-04-27 09:23:39 +0200695
Corentin Chary7de39382009-06-25 13:25:38 +0200696 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Matthew Garrett57402942009-01-20 16:17:48 +0100697}
698
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100699static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
700{
701 if (event != ACPI_NOTIFY_BUS_CHECK)
702 return;
703
704 eeepc_rfkill_hotplug();
705}
706
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600707static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100708{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100709 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100710 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200711 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100712
Eric Coopere59f8792008-03-13 12:55:46 +0100713 if (!ehotk)
714 return;
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600715 if (event > ACPI_MAX_SYS_NOTIFY)
716 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100717 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200718 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100719 count = ehotk->event_count[event % 128]++;
720 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100721 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
722 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100723 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100724 if (ehotk->inputdev) {
Darren Salt64b86b62009-04-27 09:23:38 +0200725 if (brn != -ENODEV) {
726 /* brightness-change events need special
727 * handling for conversion to key events
728 */
729 if (brn < 0)
730 brn = event;
731 else
732 brn += NOTIFY_BRN_MIN;
733 if (event < brn)
734 event = NOTIFY_BRN_MIN; /* brightness down */
735 else if (event > brn)
736 event = NOTIFY_BRN_MIN + 2; /* ... up */
737 else
738 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
739 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100740 key = eepc_get_entry_by_scancode(event);
741 if (key) {
742 switch (key->type) {
743 case KE_KEY:
744 input_report_key(ehotk->inputdev, key->keycode,
745 1);
746 input_sync(ehotk->inputdev);
747 input_report_key(ehotk->inputdev, key->keycode,
748 0);
749 input_sync(ehotk->inputdev);
750 break;
751 }
752 }
753 }
Eric Coopere59f8792008-03-13 12:55:46 +0100754}
755
Matthew Garrett57402942009-01-20 16:17:48 +0100756static int eeepc_register_rfkill_notifier(char *node)
757{
758 acpi_status status = AE_OK;
759 acpi_handle handle;
760
761 status = acpi_get_handle(NULL, node, &handle);
762
763 if (ACPI_SUCCESS(status)) {
764 status = acpi_install_notify_handler(handle,
765 ACPI_SYSTEM_NOTIFY,
766 eeepc_rfkill_notify,
767 NULL);
768 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200769 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100770 } else
771 return -ENODEV;
772
773 return 0;
774}
775
776static void eeepc_unregister_rfkill_notifier(char *node)
777{
778 acpi_status status = AE_OK;
779 acpi_handle handle;
780
781 status = acpi_get_handle(NULL, node, &handle);
782
783 if (ACPI_SUCCESS(status)) {
784 status = acpi_remove_notify_handler(handle,
785 ACPI_SYSTEM_NOTIFY,
786 eeepc_rfkill_notify);
787 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200788 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100789 node);
790 }
791}
792
Corentin Chary2b121bc2009-06-25 13:25:36 +0200793static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
794{
795 kfree(hotplug_slot->info);
796 kfree(hotplug_slot);
797}
798
799static int eeepc_setup_pci_hotplug(void)
800{
801 int ret = -ENOMEM;
802 struct pci_bus *bus = pci_find_bus(0, 1);
803
804 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200805 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200806 return -ENODEV;
807 }
808
809 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
810 if (!ehotk->hotplug_slot)
811 goto error_slot;
812
813 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
814 GFP_KERNEL);
815 if (!ehotk->hotplug_slot->info)
816 goto error_info;
817
818 ehotk->hotplug_slot->private = ehotk;
819 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
820 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
821 eeepc_get_adapter_status(ehotk->hotplug_slot,
822 &ehotk->hotplug_slot->info->adapter_status);
823
824 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
825 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200826 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200827 goto error_register;
828 }
829
830 return 0;
831
832error_register:
833 kfree(ehotk->hotplug_slot->info);
834error_info:
835 kfree(ehotk->hotplug_slot);
836 ehotk->hotplug_slot = NULL;
837error_slot:
838 return ret;
839}
840
Eric Coopere59f8792008-03-13 12:55:46 +0100841static int eeepc_hotk_add(struct acpi_device *device)
842{
Eric Coopere59f8792008-03-13 12:55:46 +0100843 int result;
844
845 if (!device)
846 return -EINVAL;
Joe Perches19b53282009-06-25 13:25:37 +0200847 pr_notice(EEEPC_HOTK_NAME "\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100848 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
849 if (!ehotk)
850 return -ENOMEM;
851 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
852 ehotk->handle = device->handle;
853 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
854 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700855 device->driver_data = ehotk;
Eric Coopere59f8792008-03-13 12:55:46 +0100856 ehotk->device = device;
857 result = eeepc_hotk_check();
858 if (result)
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100859 goto ehotk_fail;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100860
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100861 return 0;
862
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100863 ehotk_fail:
864 kfree(ehotk);
865 ehotk = NULL;
866
Eric Coopere59f8792008-03-13 12:55:46 +0100867 return result;
868}
869
870static int eeepc_hotk_remove(struct acpi_device *device, int type)
871{
Eric Coopere59f8792008-03-13 12:55:46 +0100872 if (!device || !acpi_driver_data(device))
873 return -EINVAL;
Matthew Garrett57402942009-01-20 16:17:48 +0100874
Eric Coopere59f8792008-03-13 12:55:46 +0100875 kfree(ehotk);
876 return 0;
877}
878
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100879static int eeepc_hotk_resume(struct acpi_device *device)
880{
Corentin Chary7de39382009-06-25 13:25:38 +0200881 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100882 bool wlan;
883
884 /* Workaround - it seems that _PTS disables the wireless
885 without notification or changing the value read by WLAN.
886 Normally this is fine because the correct value is restored
887 from the non-volatile storage on resume, but we need to do
888 it ourself if case suspend is aborted, or we lose wireless.
889 */
890 wlan = get_acpi(CM_ASL_WLAN);
891 set_acpi(CM_ASL_WLAN, wlan);
892
Corentin Chary7de39382009-06-25 13:25:38 +0200893 rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100894
895 eeepc_rfkill_hotplug();
896 }
897
Corentin Chary7de39382009-06-25 13:25:38 +0200898 if (ehotk->bluetooth_rfkill)
899 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100900 get_acpi(CM_ASL_BLUETOOTH) != 1);
901
902 return 0;
903}
904
Eric Coopere59f8792008-03-13 12:55:46 +0100905/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100906 * Hwmon
907 */
908static int eeepc_get_fan_pwm(void)
909{
910 int value = 0;
911
912 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200913 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100914 return (value);
915}
916
917static void eeepc_set_fan_pwm(int value)
918{
Corentin Chary04dcd842008-10-09 15:33:57 +0200919 value = SENSORS_LIMIT(value, 0, 255);
920 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100921 ec_write(EEEPC_EC_SC02, value);
922}
923
924static int eeepc_get_fan_rpm(void)
925{
926 int high = 0;
927 int low = 0;
928
929 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
930 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
931 return (high << 8 | low);
932}
933
934static int eeepc_get_fan_ctrl(void)
935{
936 int value = 0;
937
938 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
939 return ((value & 0x02 ? 1 : 0));
940}
941
942static void eeepc_set_fan_ctrl(int manual)
943{
944 int value = 0;
945
946 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
947 if (manual)
948 value |= 0x02;
949 else
950 value &= ~0x02;
951 ec_write(EEEPC_EC_SFB3, value);
952}
953
954static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
955{
956 int rv, value;
957
958 rv = parse_arg(buf, count, &value);
959 if (rv > 0)
960 set(value);
961 return rv;
962}
963
964static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
965{
966 return sprintf(buf, "%d\n", get());
967}
968
969#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
970 static ssize_t show_##_name(struct device *dev, \
971 struct device_attribute *attr, \
972 char *buf) \
973 { \
974 return show_sys_hwmon(_set, buf); \
975 } \
976 static ssize_t store_##_name(struct device *dev, \
977 struct device_attribute *attr, \
978 const char *buf, size_t count) \
979 { \
980 return store_sys_hwmon(_get, buf, count); \
981 } \
982 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
983
984EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200985EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100986 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
987EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
988 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
989
Corentin Chary04dcd842008-10-09 15:33:57 +0200990static ssize_t
991show_name(struct device *dev, struct device_attribute *attr, char *buf)
992{
993 return sprintf(buf, "eeepc\n");
994}
995static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
996
Corentin Charye1faa9d2008-03-13 12:57:18 +0100997static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +0200998 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100999 &sensor_dev_attr_fan1_input.dev_attr.attr,
1000 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001001 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001002 NULL
1003};
1004
1005static struct attribute_group hwmon_attribute_group = {
1006 .attrs = hwmon_attributes
1007};
1008
1009/*
Eric Coopere59f8792008-03-13 12:55:46 +01001010 * exit/init
1011 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001012static void eeepc_backlight_exit(void)
1013{
1014 if (eeepc_backlight_device)
1015 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001016 eeepc_backlight_device = NULL;
1017}
1018
1019static void eeepc_rfkill_exit(void)
1020{
Corentin Chary7de39382009-06-25 13:25:38 +02001021 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1022 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
1023 if (ehotk->wlan_rfkill)
1024 rfkill_unregister(ehotk->wlan_rfkill);
1025 if (ehotk->bluetooth_rfkill)
1026 rfkill_unregister(ehotk->bluetooth_rfkill);
Corentin Chary3cd530b2009-06-25 13:25:42 +02001027 if (ehotk->wwan3g_rfkill)
1028 rfkill_unregister(ehotk->wwan3g_rfkill);
Corentin Chary7de39382009-06-25 13:25:38 +02001029 if (ehotk->hotplug_slot)
1030 pci_hp_deregister(ehotk->hotplug_slot);
Corentin Charya9df80c2009-01-20 16:17:40 +01001031}
1032
1033static void eeepc_input_exit(void)
1034{
1035 if (ehotk->inputdev)
1036 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001037}
1038
Corentin Charye1faa9d2008-03-13 12:57:18 +01001039static void eeepc_hwmon_exit(void)
1040{
1041 struct device *hwmon;
1042
1043 hwmon = eeepc_hwmon_device;
1044 if (!hwmon)
1045 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001046 sysfs_remove_group(&hwmon->kobj,
1047 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001048 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001049 eeepc_hwmon_device = NULL;
1050}
1051
Eric Coopere59f8792008-03-13 12:55:46 +01001052static void __exit eeepc_laptop_exit(void)
1053{
Corentin Charya5fa4292008-03-13 12:56:37 +01001054 eeepc_backlight_exit();
Corentin Charya9df80c2009-01-20 16:17:40 +01001055 eeepc_rfkill_exit();
1056 eeepc_input_exit();
Corentin Charye1faa9d2008-03-13 12:57:18 +01001057 eeepc_hwmon_exit();
Eric Coopere59f8792008-03-13 12:55:46 +01001058 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1059 sysfs_remove_group(&platform_device->dev.kobj,
1060 &platform_attribute_group);
1061 platform_device_unregister(platform_device);
1062 platform_driver_unregister(&platform_driver);
1063}
1064
Corentin Chary7de39382009-06-25 13:25:38 +02001065static int eeepc_new_rfkill(struct rfkill **rfkill,
1066 const char *name, struct device *dev,
1067 enum rfkill_type type, int cm)
1068{
1069 int result;
1070
Corentin Charyf36509e2009-06-25 13:25:40 +02001071 result = get_acpi(cm);
1072 if (result < 0)
1073 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001074
1075 *rfkill = rfkill_alloc(name, dev, type,
1076 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1077
1078 if (!*rfkill)
1079 return -EINVAL;
1080
1081 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1082 result = rfkill_register(*rfkill);
1083 if (result) {
1084 rfkill_destroy(*rfkill);
1085 *rfkill = NULL;
1086 return result;
1087 }
1088 return 0;
1089}
1090
1091
1092static int eeepc_rfkill_init(struct device *dev)
1093{
1094 int result = 0;
1095
1096 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1097 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1098
1099 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1100 "eeepc-wlan", dev,
1101 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1102
1103 if (result && result != -ENODEV)
1104 goto exit;
1105
1106 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1107 "eeepc-bluetooth", dev,
1108 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1109
1110 if (result && result != -ENODEV)
1111 goto exit;
1112
Corentin Chary3cd530b2009-06-25 13:25:42 +02001113 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1114 "eeepc-wwan3g", dev,
1115 RFKILL_TYPE_WWAN, CM_ASL_3G);
1116
1117 if (result && result != -ENODEV)
1118 goto exit;
1119
Corentin Chary7de39382009-06-25 13:25:38 +02001120 result = eeepc_setup_pci_hotplug();
1121 /*
1122 * If we get -EBUSY then something else is handling the PCI hotplug -
1123 * don't fail in this case
1124 */
1125 if (result == -EBUSY)
1126 result = 0;
1127
1128exit:
1129 if (result && result != -ENODEV)
1130 eeepc_rfkill_exit();
1131 return result;
1132}
1133
Corentin Charya5fa4292008-03-13 12:56:37 +01001134static int eeepc_backlight_init(struct device *dev)
1135{
1136 struct backlight_device *bd;
1137
1138 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1139 NULL, &eeepcbl_ops);
1140 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001141 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001142 eeepc_backlight_device = NULL;
1143 return PTR_ERR(bd);
1144 }
1145 eeepc_backlight_device = bd;
1146 bd->props.max_brightness = 15;
1147 bd->props.brightness = read_brightness(NULL);
1148 bd->props.power = FB_BLANK_UNBLANK;
1149 backlight_update_status(bd);
1150 return 0;
1151}
1152
Corentin Charye1faa9d2008-03-13 12:57:18 +01001153static int eeepc_hwmon_init(struct device *dev)
1154{
1155 struct device *hwmon;
1156 int result;
1157
1158 hwmon = hwmon_device_register(dev);
1159 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001160 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001161 eeepc_hwmon_device = NULL;
1162 return PTR_ERR(hwmon);
1163 }
1164 eeepc_hwmon_device = hwmon;
1165 result = sysfs_create_group(&hwmon->kobj,
1166 &hwmon_attribute_group);
1167 if (result)
1168 eeepc_hwmon_exit();
1169 return result;
1170}
1171
Eric Coopere59f8792008-03-13 12:55:46 +01001172static int __init eeepc_laptop_init(void)
1173{
1174 struct device *dev;
1175 int result;
1176
1177 if (acpi_disabled)
1178 return -ENODEV;
1179 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1180 if (result < 0)
1181 return result;
1182 if (!ehotk) {
1183 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1184 return -ENODEV;
1185 }
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001186
1187 eeepc_enable_camera();
1188
Eric Coopere59f8792008-03-13 12:55:46 +01001189 /* Register platform stuff */
1190 result = platform_driver_register(&platform_driver);
1191 if (result)
1192 goto fail_platform_driver;
1193 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1194 if (!platform_device) {
1195 result = -ENOMEM;
1196 goto fail_platform_device1;
1197 }
1198 result = platform_device_add(platform_device);
1199 if (result)
1200 goto fail_platform_device2;
1201 result = sysfs_create_group(&platform_device->dev.kobj,
1202 &platform_attribute_group);
1203 if (result)
1204 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001205
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001206 dev = &platform_device->dev;
1207
1208 if (!acpi_video_backlight_support()) {
1209 result = eeepc_backlight_init(dev);
1210 if (result)
1211 goto fail_backlight;
1212 } else
1213 pr_info("Backlight controlled by ACPI video "
1214 "driver\n");
1215
1216 result = eeepc_hwmon_init(dev);
1217 if (result)
1218 goto fail_hwmon;
1219
Corentin Chary7de39382009-06-25 13:25:38 +02001220 result = eeepc_rfkill_init(dev);
1221 if (result)
1222 goto fail_rfkill;
1223
Eric Coopere59f8792008-03-13 12:55:46 +01001224 return 0;
Corentin Chary7de39382009-06-25 13:25:38 +02001225fail_rfkill:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001226 eeepc_hwmon_exit();
1227fail_hwmon:
1228 eeepc_backlight_exit();
1229fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001230 sysfs_remove_group(&platform_device->dev.kobj,
1231 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001232fail_sysfs:
1233 platform_device_del(platform_device);
1234fail_platform_device2:
1235 platform_device_put(platform_device);
1236fail_platform_device1:
1237 platform_driver_unregister(&platform_driver);
1238fail_platform_driver:
Corentin Charya9df80c2009-01-20 16:17:40 +01001239 eeepc_input_exit();
Eric Coopere59f8792008-03-13 12:55:46 +01001240 return result;
1241}
1242
1243module_init(eeepc_laptop_init);
1244module_exit(eeepc_laptop_exit);