blob: d379e74a05d0f7d55d22470a7c10f979bf969838 [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 Charyd1ec9c32009-08-28 12:56:41 +0000145 struct rfkill *wimax_rfkill;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200146 struct hotplug_slot *hotplug_slot;
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000147 struct mutex hotplug_lock;
Eric Coopere59f8792008-03-13 12:55:46 +0100148};
149
150/* The actual device the driver binds to */
151static struct eeepc_hotk *ehotk;
152
Darren Saltb56ab332009-10-13 00:13:33 +0200153static void eeepc_rfkill_hotplug(bool real);
154
Eric Coopere59f8792008-03-13 12:55:46 +0100155/* Platform device/driver */
Alan Jenkinsc200da52009-08-28 12:56:40 +0000156static int eeepc_hotk_thaw(struct device *device);
157static int eeepc_hotk_restore(struct device *device);
158
159static struct dev_pm_ops eeepc_pm_ops = {
160 .thaw = eeepc_hotk_thaw,
161 .restore = eeepc_hotk_restore,
162};
163
Eric Coopere59f8792008-03-13 12:55:46 +0100164static struct platform_driver platform_driver = {
165 .driver = {
166 .name = EEEPC_HOTK_FILE,
167 .owner = THIS_MODULE,
Alan Jenkinsc200da52009-08-28 12:56:40 +0000168 .pm = &eeepc_pm_ops,
Eric Coopere59f8792008-03-13 12:55:46 +0100169 }
170};
171
172static struct platform_device *platform_device;
173
Matthew Garretta195dcd2008-08-19 12:13:20 +0100174struct key_entry {
175 char type;
176 u8 code;
177 u16 keycode;
178};
179
180enum { KE_KEY, KE_END };
181
182static struct key_entry eeepc_keymap[] = {
183 /* Sleep already handled via generic ACPI code */
184 {KE_KEY, 0x10, KEY_WLAN },
Alan Jenkins978605c2009-04-27 09:23:39 +0200185 {KE_KEY, 0x11, KEY_WLAN },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100186 {KE_KEY, 0x12, KEY_PROG1 },
187 {KE_KEY, 0x13, KEY_MUTE },
188 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
189 {KE_KEY, 0x15, KEY_VOLUMEUP },
Matthew Garrettb5f6f262009-01-20 16:17:46 +0100190 {KE_KEY, 0x1a, KEY_COFFEE },
191 {KE_KEY, 0x1b, KEY_ZOOM },
192 {KE_KEY, 0x1c, KEY_PROG2 },
193 {KE_KEY, 0x1d, KEY_PROG3 },
Darren Salt64b86b62009-04-27 09:23:38 +0200194 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
195 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100196 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
197 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
198 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
199 {KE_END, 0},
200};
201
Eric Coopere59f8792008-03-13 12:55:46 +0100202/*
203 * The hotkey driver declaration
204 */
205static int eeepc_hotk_add(struct acpi_device *device);
206static int eeepc_hotk_remove(struct acpi_device *device, int type);
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600207static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
Eric Coopere59f8792008-03-13 12:55:46 +0100208
209static const struct acpi_device_id eeepc_device_ids[] = {
210 {EEEPC_HOTK_HID, 0},
211 {"", 0},
212};
213MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
214
215static struct acpi_driver eeepc_hotk_driver = {
216 .name = EEEPC_HOTK_NAME,
217 .class = EEEPC_HOTK_CLASS,
218 .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;
Darren Saltb56ab332009-10-13 00:13:33 +0200348 int ret;
349
350 if (asl != CM_ASL_WLAN)
351 return set_acpi(asl, !blocked);
352
353 /* hack to avoid panic with rt2860sta */
354 if (blocked)
355 eeepc_rfkill_hotplug(false);
356 ret = set_acpi(asl, !blocked);
357 return ret;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100358}
359
Johannes Berg19d337d2009-06-02 13:01:37 +0200360static const struct rfkill_ops eeepc_rfkill_ops = {
361 .set_block = eeepc_rfkill_set,
362};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100363
Rakib Mullickdcb73ee2009-10-13 00:13:32 +0200364static void __devinit eeepc_enable_camera(void)
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000365{
366 /*
367 * If the following call to set_acpi() fails, it's because there's no
368 * camera so we can ignore the error.
369 */
370 set_acpi(CM_ASL_CAMERA, 1);
371}
372
Matthew Garretta195dcd2008-08-19 12:13:20 +0100373/*
Eric Coopere59f8792008-03-13 12:55:46 +0100374 * Sys helpers
375 */
376static int parse_arg(const char *buf, unsigned long count, int *val)
377{
378 if (!count)
379 return 0;
380 if (sscanf(buf, "%i", val) != 1)
381 return -EINVAL;
382 return count;
383}
384
385static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
386{
387 int rv, value;
388
389 rv = parse_arg(buf, count, &value);
390 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200391 value = set_acpi(cm, value);
392 if (value < 0)
393 return value;
Eric Coopere59f8792008-03-13 12:55:46 +0100394 return rv;
395}
396
397static ssize_t show_sys_acpi(int cm, char *buf)
398{
Corentin Charyf36509e2009-06-25 13:25:40 +0200399 int value = get_acpi(cm);
400
401 if (value < 0)
402 return value;
403 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100404}
405
406#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
407 static ssize_t show_##_name(struct device *dev, \
408 struct device_attribute *attr, \
409 char *buf) \
410 { \
411 return show_sys_acpi(_cm, buf); \
412 } \
413 static ssize_t store_##_name(struct device *dev, \
414 struct device_attribute *attr, \
415 const char *buf, size_t count) \
416 { \
417 return store_sys_acpi(_cm, buf, count); \
418 } \
419 static struct device_attribute dev_attr_##_name = { \
420 .attr = { \
421 .name = __stringify(_name), \
422 .mode = 0644 }, \
423 .show = show_##_name, \
424 .store = store_##_name, \
425 }
426
427EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
428EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
429EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000430
431struct eeepc_cpufv {
432 int num;
433 int cur;
434};
435
436static int get_cpufv(struct eeepc_cpufv *c)
437{
438 c->cur = get_acpi(CM_ASL_CPUFV);
439 c->num = (c->cur >> 8) & 0xff;
440 c->cur &= 0xff;
441 if (c->cur < 0 || c->num <= 0 || c->num > 12)
442 return -ENODEV;
443 return 0;
444}
445
446static ssize_t show_available_cpufv(struct device *dev,
447 struct device_attribute *attr,
448 char *buf)
449{
450 struct eeepc_cpufv c;
451 int i;
452 ssize_t len = 0;
453
454 if (get_cpufv(&c))
455 return -ENODEV;
456 for (i = 0; i < c.num; i++)
457 len += sprintf(buf + len, "%d ", i);
458 len += sprintf(buf + len, "\n");
459 return len;
460}
461
462static ssize_t show_cpufv(struct device *dev,
463 struct device_attribute *attr,
464 char *buf)
465{
466 struct eeepc_cpufv c;
467
468 if (get_cpufv(&c))
469 return -ENODEV;
470 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
471}
472
473static ssize_t store_cpufv(struct device *dev,
474 struct device_attribute *attr,
475 const char *buf, size_t count)
476{
477 struct eeepc_cpufv c;
478 int rv, value;
479
480 if (get_cpufv(&c))
481 return -ENODEV;
482 rv = parse_arg(buf, count, &value);
483 if (rv < 0)
484 return rv;
485 if (!rv || value < 0 || value >= c.num)
486 return -EINVAL;
487 set_acpi(CM_ASL_CPUFV, value);
488 return rv;
489}
490
491static struct device_attribute dev_attr_cpufv = {
492 .attr = {
493 .name = "cpufv",
494 .mode = 0644 },
495 .show = show_cpufv,
496 .store = store_cpufv
497};
498
499static struct device_attribute dev_attr_available_cpufv = {
500 .attr = {
501 .name = "available_cpufv",
502 .mode = 0444 },
503 .show = show_available_cpufv
504};
Eric Coopere59f8792008-03-13 12:55:46 +0100505
506static struct attribute *platform_attributes[] = {
507 &dev_attr_camera.attr,
508 &dev_attr_cardr.attr,
509 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200510 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000511 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100512 NULL
513};
514
515static struct attribute_group platform_attribute_group = {
516 .attrs = platform_attributes
517};
518
519/*
520 * Hotkey functions
521 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100522static struct key_entry *eepc_get_entry_by_scancode(int code)
523{
524 struct key_entry *key;
525
526 for (key = eeepc_keymap; key->type != KE_END; key++)
527 if (code == key->code)
528 return key;
529
530 return NULL;
531}
532
533static struct key_entry *eepc_get_entry_by_keycode(int code)
534{
535 struct key_entry *key;
536
537 for (key = eeepc_keymap; key->type != KE_END; key++)
538 if (code == key->keycode && key->type == KE_KEY)
539 return key;
540
541 return NULL;
542}
543
544static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
545{
546 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
547
548 if (key && key->type == KE_KEY) {
549 *keycode = key->keycode;
550 return 0;
551 }
552
553 return -EINVAL;
554}
555
556static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
557{
558 struct key_entry *key;
559 int old_keycode;
560
561 if (keycode < 0 || keycode > KEY_MAX)
562 return -EINVAL;
563
564 key = eepc_get_entry_by_scancode(scancode);
565 if (key && key->type == KE_KEY) {
566 old_keycode = key->keycode;
567 key->keycode = keycode;
568 set_bit(keycode, dev->keybit);
569 if (!eepc_get_entry_by_keycode(old_keycode))
570 clear_bit(old_keycode, dev->keybit);
571 return 0;
572 }
573
574 return -EINVAL;
575}
576
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200577static void cmsg_quirk(int cm, const char *name)
578{
579 int dummy;
580
581 /* Some BIOSes do not report cm although it is avaliable.
582 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
583 if (!(ehotk->cm_supported & (1 << cm))
584 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
585 pr_info("%s (%x) not reported by BIOS,"
586 " enabling anyway\n", name, 1 << cm);
587 ehotk->cm_supported |= 1 << cm;
588 }
589}
590
591static void cmsg_quirks(void)
592{
593 cmsg_quirk(CM_ASL_LID, "LID");
594 cmsg_quirk(CM_ASL_TYPE, "TYPE");
595 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
596 cmsg_quirk(CM_ASL_TPD, "TPD");
597}
598
Eric Coopere59f8792008-03-13 12:55:46 +0100599static int eeepc_hotk_check(void)
600{
601 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
602 int result;
603
604 result = acpi_bus_get_status(ehotk->device);
605 if (result)
606 return result;
607 if (ehotk->device->status.present) {
608 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
609 &buffer)) {
Joe Perches19b53282009-06-25 13:25:37 +0200610 pr_err("Hotkey initialization failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100611 return -ENODEV;
612 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200613 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
Eric Coopere59f8792008-03-13 12:55:46 +0100614 }
615 /* get control methods supported */
616 if (read_acpi_int(ehotk->handle, "CMSG"
617 , &ehotk->cm_supported)) {
Joe Perches19b53282009-06-25 13:25:37 +0200618 pr_err("Get control methods supported failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100619 return -ENODEV;
620 } else {
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200621 cmsg_quirks();
Joe Perches19b53282009-06-25 13:25:37 +0200622 pr_info("Get control methods supported: 0x%x\n",
623 ehotk->cm_supported);
Eric Coopere59f8792008-03-13 12:55:46 +0100624 }
625 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200626 pr_err("Hotkey device not present, aborting\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100627 return -EINVAL;
628 }
629 return 0;
630}
631
Darren Salt64b86b62009-04-27 09:23:38 +0200632static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100633{
Darren Salt64b86b62009-04-27 09:23:38 +0200634 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100635 struct backlight_device *bd = eeepc_backlight_device;
Darren Salt64b86b62009-04-27 09:23:38 +0200636 if (bd) {
637 int old = bd->props.brightness;
Matthew Garrettd822d5c2009-07-14 17:06:04 +0100638 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
Darren Salt64b86b62009-04-27 09:23:38 +0200639 return old;
640 }
641 return -1;
Corentin Charya5fa4292008-03-13 12:56:37 +0100642}
643
Corentin Chary2b121bc2009-06-25 13:25:36 +0200644static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
645 u8 *value)
646{
647 int val = get_acpi(CM_ASL_WLAN);
648
649 if (val == 1 || val == 0)
650 *value = val;
651 else
652 return -EINVAL;
653
654 return 0;
655}
656
Darren Saltb56ab332009-10-13 00:13:33 +0200657static void eeepc_rfkill_hotplug(bool real)
Matthew Garrett57402942009-01-20 16:17:48 +0100658{
659 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000660 struct pci_bus *bus;
Darren Saltb56ab332009-10-13 00:13:33 +0200661 bool blocked = real ? eeepc_wlan_rfkill_blocked() : true;
Matthew Garrett57402942009-01-20 16:17:48 +0100662
Darren Saltb56ab332009-10-13 00:13:33 +0200663 if (real && ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000664 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000665
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000666 mutex_lock(&ehotk->hotplug_lock);
667
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000668 if (ehotk->hotplug_slot) {
669 bus = pci_find_bus(0, 1);
670 if (!bus) {
671 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000672 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100673 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000674
675 if (!blocked) {
676 dev = pci_get_slot(bus, 0);
677 if (dev) {
678 /* Device already present */
679 pci_dev_put(dev);
680 goto out_unlock;
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))
686 pr_err("Unable to hotplug wifi\n");
687 }
688 } else {
689 dev = pci_get_slot(bus, 0);
690 if (dev) {
691 pci_remove_bus_device(dev);
692 pci_dev_put(dev);
693 }
Matthew Garrett57402942009-01-20 16:17:48 +0100694 }
695 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000696
697out_unlock:
698 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100699}
700
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100701static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
702{
703 if (event != ACPI_NOTIFY_BUS_CHECK)
704 return;
705
Darren Saltb56ab332009-10-13 00:13:33 +0200706 eeepc_rfkill_hotplug(true);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100707}
708
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600709static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100710{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100711 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100712 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200713 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100714
Eric Coopere59f8792008-03-13 12:55:46 +0100715 if (!ehotk)
716 return;
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600717 if (event > ACPI_MAX_SYS_NOTIFY)
718 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100719 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200720 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100721 count = ehotk->event_count[event % 128]++;
722 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100723 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
724 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100725 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100726 if (ehotk->inputdev) {
Darren Salt64b86b62009-04-27 09:23:38 +0200727 if (brn != -ENODEV) {
728 /* brightness-change events need special
729 * handling for conversion to key events
730 */
731 if (brn < 0)
732 brn = event;
733 else
734 brn += NOTIFY_BRN_MIN;
735 if (event < brn)
736 event = NOTIFY_BRN_MIN; /* brightness down */
737 else if (event > brn)
738 event = NOTIFY_BRN_MIN + 2; /* ... up */
739 else
740 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
741 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100742 key = eepc_get_entry_by_scancode(event);
743 if (key) {
744 switch (key->type) {
745 case KE_KEY:
746 input_report_key(ehotk->inputdev, key->keycode,
747 1);
748 input_sync(ehotk->inputdev);
749 input_report_key(ehotk->inputdev, key->keycode,
750 0);
751 input_sync(ehotk->inputdev);
752 break;
753 }
754 }
755 }
Eric Coopere59f8792008-03-13 12:55:46 +0100756}
757
Matthew Garrett57402942009-01-20 16:17:48 +0100758static int eeepc_register_rfkill_notifier(char *node)
759{
760 acpi_status status = AE_OK;
761 acpi_handle handle;
762
763 status = acpi_get_handle(NULL, node, &handle);
764
765 if (ACPI_SUCCESS(status)) {
766 status = acpi_install_notify_handler(handle,
767 ACPI_SYSTEM_NOTIFY,
768 eeepc_rfkill_notify,
769 NULL);
770 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200771 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100772 } else
773 return -ENODEV;
774
775 return 0;
776}
777
778static void eeepc_unregister_rfkill_notifier(char *node)
779{
780 acpi_status status = AE_OK;
781 acpi_handle handle;
782
783 status = acpi_get_handle(NULL, node, &handle);
784
785 if (ACPI_SUCCESS(status)) {
786 status = acpi_remove_notify_handler(handle,
787 ACPI_SYSTEM_NOTIFY,
788 eeepc_rfkill_notify);
789 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200790 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100791 node);
792 }
793}
794
Corentin Chary2b121bc2009-06-25 13:25:36 +0200795static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
796{
797 kfree(hotplug_slot->info);
798 kfree(hotplug_slot);
799}
800
801static int eeepc_setup_pci_hotplug(void)
802{
803 int ret = -ENOMEM;
804 struct pci_bus *bus = pci_find_bus(0, 1);
805
806 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200807 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200808 return -ENODEV;
809 }
810
811 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
812 if (!ehotk->hotplug_slot)
813 goto error_slot;
814
815 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
816 GFP_KERNEL);
817 if (!ehotk->hotplug_slot->info)
818 goto error_info;
819
820 ehotk->hotplug_slot->private = ehotk;
821 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
822 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
823 eeepc_get_adapter_status(ehotk->hotplug_slot,
824 &ehotk->hotplug_slot->info->adapter_status);
825
826 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
827 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200828 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200829 goto error_register;
830 }
831
832 return 0;
833
834error_register:
835 kfree(ehotk->hotplug_slot->info);
836error_info:
837 kfree(ehotk->hotplug_slot);
838 ehotk->hotplug_slot = NULL;
839error_slot:
840 return ret;
841}
842
Alan Jenkinsc200da52009-08-28 12:56:40 +0000843static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100844{
Corentin Chary7de39382009-06-25 13:25:38 +0200845 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100846 bool wlan;
847
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000848 /*
849 * Work around bios bug - acpi _PTS turns off the wireless led
850 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000851 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100852 */
853 wlan = get_acpi(CM_ASL_WLAN);
854 set_acpi(CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100855 }
856
Alan Jenkinsc200da52009-08-28 12:56:40 +0000857 return 0;
858}
859
860static int eeepc_hotk_restore(struct device *device)
861{
862 /* Refresh both wlan rfkill state and pci hotplug */
863 if (ehotk->wlan_rfkill)
Darren Saltb56ab332009-10-13 00:13:33 +0200864 eeepc_rfkill_hotplug(true);
Alan Jenkinsc200da52009-08-28 12:56:40 +0000865
Corentin Chary7de39382009-06-25 13:25:38 +0200866 if (ehotk->bluetooth_rfkill)
867 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100868 get_acpi(CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa4746102009-08-28 12:56:38 +0000869 if (ehotk->wwan3g_rfkill)
870 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
871 get_acpi(CM_ASL_3G) != 1);
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000872 if (ehotk->wimax_rfkill)
873 rfkill_set_sw_state(ehotk->wimax_rfkill,
874 get_acpi(CM_ASL_WIMAX) != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100875
876 return 0;
877}
878
Eric Coopere59f8792008-03-13 12:55:46 +0100879/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100880 * Hwmon
881 */
882static int eeepc_get_fan_pwm(void)
883{
884 int value = 0;
885
886 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200887 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100888 return (value);
889}
890
891static void eeepc_set_fan_pwm(int value)
892{
Corentin Chary04dcd842008-10-09 15:33:57 +0200893 value = SENSORS_LIMIT(value, 0, 255);
894 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100895 ec_write(EEEPC_EC_SC02, value);
896}
897
898static int eeepc_get_fan_rpm(void)
899{
900 int high = 0;
901 int low = 0;
902
903 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
904 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
905 return (high << 8 | low);
906}
907
908static int eeepc_get_fan_ctrl(void)
909{
910 int value = 0;
911
912 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
913 return ((value & 0x02 ? 1 : 0));
914}
915
916static void eeepc_set_fan_ctrl(int manual)
917{
918 int value = 0;
919
920 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
921 if (manual)
922 value |= 0x02;
923 else
924 value &= ~0x02;
925 ec_write(EEEPC_EC_SFB3, value);
926}
927
928static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
929{
930 int rv, value;
931
932 rv = parse_arg(buf, count, &value);
933 if (rv > 0)
934 set(value);
935 return rv;
936}
937
938static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
939{
940 return sprintf(buf, "%d\n", get());
941}
942
943#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
944 static ssize_t show_##_name(struct device *dev, \
945 struct device_attribute *attr, \
946 char *buf) \
947 { \
948 return show_sys_hwmon(_set, buf); \
949 } \
950 static ssize_t store_##_name(struct device *dev, \
951 struct device_attribute *attr, \
952 const char *buf, size_t count) \
953 { \
954 return store_sys_hwmon(_get, buf, count); \
955 } \
956 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
957
958EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200959EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100960 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
961EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
962 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
963
Corentin Chary04dcd842008-10-09 15:33:57 +0200964static ssize_t
965show_name(struct device *dev, struct device_attribute *attr, char *buf)
966{
967 return sprintf(buf, "eeepc\n");
968}
969static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
970
Corentin Charye1faa9d2008-03-13 12:57:18 +0100971static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +0200972 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100973 &sensor_dev_attr_fan1_input.dev_attr.attr,
974 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +0200975 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100976 NULL
977};
978
979static struct attribute_group hwmon_attribute_group = {
980 .attrs = hwmon_attributes
981};
982
983/*
Eric Coopere59f8792008-03-13 12:55:46 +0100984 * exit/init
985 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100986static void eeepc_backlight_exit(void)
987{
988 if (eeepc_backlight_device)
989 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +0100990 eeepc_backlight_device = NULL;
991}
992
993static void eeepc_rfkill_exit(void)
994{
Alan Jenkins52cc96b2009-08-29 10:28:31 +0200995 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +0200996 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
997 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000998 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +0200999 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001000 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001001 ehotk->wlan_rfkill = NULL;
1002 }
1003 /*
1004 * Refresh pci hotplug in case the rfkill state was changed after
1005 * eeepc_unregister_rfkill_notifier()
1006 */
Darren Saltb56ab332009-10-13 00:13:33 +02001007 eeepc_rfkill_hotplug(true);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001008 if (ehotk->hotplug_slot)
1009 pci_hp_deregister(ehotk->hotplug_slot);
1010
Alan Jenkinsa82580692009-08-29 10:28:30 +02001011 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001012 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001013 rfkill_destroy(ehotk->bluetooth_rfkill);
1014 ehotk->bluetooth_rfkill = NULL;
1015 }
1016 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001017 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001018 rfkill_destroy(ehotk->wwan3g_rfkill);
1019 ehotk->wwan3g_rfkill = NULL;
1020 }
1021 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001022 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001023 rfkill_destroy(ehotk->wimax_rfkill);
1024 ehotk->wimax_rfkill = NULL;
1025 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001026}
1027
1028static void eeepc_input_exit(void)
1029{
1030 if (ehotk->inputdev)
1031 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001032}
1033
Corentin Charye1faa9d2008-03-13 12:57:18 +01001034static void eeepc_hwmon_exit(void)
1035{
1036 struct device *hwmon;
1037
1038 hwmon = eeepc_hwmon_device;
1039 if (!hwmon)
1040 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001041 sysfs_remove_group(&hwmon->kobj,
1042 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001043 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001044 eeepc_hwmon_device = NULL;
1045}
1046
Corentin Chary7de39382009-06-25 13:25:38 +02001047static int eeepc_new_rfkill(struct rfkill **rfkill,
1048 const char *name, struct device *dev,
1049 enum rfkill_type type, int cm)
1050{
1051 int result;
1052
Corentin Charyf36509e2009-06-25 13:25:40 +02001053 result = get_acpi(cm);
1054 if (result < 0)
1055 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001056
1057 *rfkill = rfkill_alloc(name, dev, type,
1058 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1059
1060 if (!*rfkill)
1061 return -EINVAL;
1062
1063 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1064 result = rfkill_register(*rfkill);
1065 if (result) {
1066 rfkill_destroy(*rfkill);
1067 *rfkill = NULL;
1068 return result;
1069 }
1070 return 0;
1071}
1072
1073
1074static int eeepc_rfkill_init(struct device *dev)
1075{
1076 int result = 0;
1077
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001078 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001079
Corentin Chary7de39382009-06-25 13:25:38 +02001080 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1081 "eeepc-wlan", dev,
1082 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1083
1084 if (result && result != -ENODEV)
1085 goto exit;
1086
1087 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1088 "eeepc-bluetooth", dev,
1089 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1090
1091 if (result && result != -ENODEV)
1092 goto exit;
1093
Corentin Chary3cd530b2009-06-25 13:25:42 +02001094 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1095 "eeepc-wwan3g", dev,
1096 RFKILL_TYPE_WWAN, CM_ASL_3G);
1097
1098 if (result && result != -ENODEV)
1099 goto exit;
1100
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001101 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1102 "eeepc-wimax", dev,
1103 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1104
1105 if (result && result != -ENODEV)
1106 goto exit;
1107
Corentin Chary7de39382009-06-25 13:25:38 +02001108 result = eeepc_setup_pci_hotplug();
1109 /*
1110 * If we get -EBUSY then something else is handling the PCI hotplug -
1111 * don't fail in this case
1112 */
1113 if (result == -EBUSY)
1114 result = 0;
1115
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001116 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001117 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1118 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1119 /*
1120 * Refresh pci hotplug in case the rfkill state was changed during
1121 * setup.
1122 */
Darren Saltb56ab332009-10-13 00:13:33 +02001123 eeepc_rfkill_hotplug(true);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001124
Corentin Chary7de39382009-06-25 13:25:38 +02001125exit:
1126 if (result && result != -ENODEV)
1127 eeepc_rfkill_exit();
1128 return result;
1129}
1130
Corentin Charya5fa4292008-03-13 12:56:37 +01001131static int eeepc_backlight_init(struct device *dev)
1132{
1133 struct backlight_device *bd;
1134
1135 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1136 NULL, &eeepcbl_ops);
1137 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001138 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001139 eeepc_backlight_device = NULL;
1140 return PTR_ERR(bd);
1141 }
1142 eeepc_backlight_device = bd;
1143 bd->props.max_brightness = 15;
1144 bd->props.brightness = read_brightness(NULL);
1145 bd->props.power = FB_BLANK_UNBLANK;
1146 backlight_update_status(bd);
1147 return 0;
1148}
1149
Corentin Charye1faa9d2008-03-13 12:57:18 +01001150static int eeepc_hwmon_init(struct device *dev)
1151{
1152 struct device *hwmon;
1153 int result;
1154
1155 hwmon = hwmon_device_register(dev);
1156 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001157 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001158 eeepc_hwmon_device = NULL;
1159 return PTR_ERR(hwmon);
1160 }
1161 eeepc_hwmon_device = hwmon;
1162 result = sysfs_create_group(&hwmon->kobj,
1163 &hwmon_attribute_group);
1164 if (result)
1165 eeepc_hwmon_exit();
1166 return result;
1167}
1168
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001169static int eeepc_input_init(struct device *dev)
1170{
1171 const struct key_entry *key;
1172 int result;
1173
1174 ehotk->inputdev = input_allocate_device();
1175 if (!ehotk->inputdev) {
1176 pr_info("Unable to allocate input device\n");
1177 return -ENOMEM;
1178 }
1179 ehotk->inputdev->name = "Asus EeePC extra buttons";
1180 ehotk->inputdev->dev.parent = dev;
1181 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1182 ehotk->inputdev->id.bustype = BUS_HOST;
1183 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1184 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1185
1186 for (key = eeepc_keymap; key->type != KE_END; key++) {
1187 switch (key->type) {
1188 case KE_KEY:
1189 set_bit(EV_KEY, ehotk->inputdev->evbit);
1190 set_bit(key->keycode, ehotk->inputdev->keybit);
1191 break;
1192 }
1193 }
1194 result = input_register_device(ehotk->inputdev);
1195 if (result) {
1196 pr_info("Unable to register input device\n");
1197 input_free_device(ehotk->inputdev);
1198 return result;
1199 }
1200 return 0;
1201}
1202
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001203static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001204{
1205 struct device *dev;
1206 int result;
1207
Alan Jenkins1e779852009-08-28 12:56:35 +00001208 if (!device)
Len Brownaeb41b82009-08-28 19:03:11 -04001209 return -EINVAL;
Alan Jenkins1e779852009-08-28 12:56:35 +00001210 pr_notice(EEEPC_HOTK_NAME "\n");
1211 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1212 if (!ehotk)
1213 return -ENOMEM;
1214 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1215 ehotk->handle = device->handle;
1216 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1217 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1218 device->driver_data = ehotk;
1219 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001220
Alan Jenkins1e779852009-08-28 12:56:35 +00001221 result = eeepc_hotk_check();
1222 if (result)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001223 goto fail_platform_driver;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001224 eeepc_enable_camera();
1225
Eric Coopere59f8792008-03-13 12:55:46 +01001226 /* Register platform stuff */
1227 result = platform_driver_register(&platform_driver);
1228 if (result)
1229 goto fail_platform_driver;
1230 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1231 if (!platform_device) {
1232 result = -ENOMEM;
1233 goto fail_platform_device1;
1234 }
1235 result = platform_device_add(platform_device);
1236 if (result)
1237 goto fail_platform_device2;
1238 result = sysfs_create_group(&platform_device->dev.kobj,
1239 &platform_attribute_group);
1240 if (result)
1241 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001242
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001243 dev = &platform_device->dev;
1244
1245 if (!acpi_video_backlight_support()) {
1246 result = eeepc_backlight_init(dev);
1247 if (result)
1248 goto fail_backlight;
1249 } else
1250 pr_info("Backlight controlled by ACPI video "
1251 "driver\n");
1252
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001253 result = eeepc_input_init(dev);
1254 if (result)
1255 goto fail_input;
1256
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001257 result = eeepc_hwmon_init(dev);
1258 if (result)
1259 goto fail_hwmon;
1260
Corentin Chary7de39382009-06-25 13:25:38 +02001261 result = eeepc_rfkill_init(dev);
1262 if (result)
1263 goto fail_rfkill;
1264
Eric Coopere59f8792008-03-13 12:55:46 +01001265 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001266
Corentin Chary7de39382009-06-25 13:25:38 +02001267fail_rfkill:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001268 eeepc_hwmon_exit();
1269fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001270 eeepc_input_exit();
1271fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001272 eeepc_backlight_exit();
1273fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001274 sysfs_remove_group(&platform_device->dev.kobj,
1275 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001276fail_sysfs:
1277 platform_device_del(platform_device);
1278fail_platform_device2:
1279 platform_device_put(platform_device);
1280fail_platform_device1:
1281 platform_driver_unregister(&platform_driver);
1282fail_platform_driver:
Alan Jenkins1e779852009-08-28 12:56:35 +00001283 kfree(ehotk);
1284
Eric Coopere59f8792008-03-13 12:55:46 +01001285 return result;
1286}
1287
Alan Jenkins1e779852009-08-28 12:56:35 +00001288static int eeepc_hotk_remove(struct acpi_device *device, int type)
1289{
1290 if (!device || !acpi_driver_data(device))
Len Brownaeb41b82009-08-28 19:03:11 -04001291 return -EINVAL;
Alan Jenkins1e779852009-08-28 12:56:35 +00001292
1293 eeepc_backlight_exit();
1294 eeepc_rfkill_exit();
1295 eeepc_input_exit();
1296 eeepc_hwmon_exit();
1297 sysfs_remove_group(&platform_device->dev.kobj,
1298 &platform_attribute_group);
1299 platform_device_unregister(platform_device);
1300 platform_driver_unregister(&platform_driver);
1301
1302 kfree(ehotk);
1303 return 0;
1304}
1305
1306static int __init eeepc_laptop_init(void)
1307{
1308 int result;
1309
1310 if (acpi_disabled)
1311 return -ENODEV;
1312 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1313 if (result < 0)
1314 return result;
1315 if (!ehotk) {
1316 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1317 return -ENODEV;
1318 }
1319 return 0;
1320}
1321
1322static void __exit eeepc_laptop_exit(void)
1323{
1324 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1325}
1326
Eric Coopere59f8792008-03-13 12:55:46 +01001327module_init(eeepc_laptop_init);
1328module_exit(eeepc_laptop_exit);