blob: 3f9b286b854eb6025dd9e59856ca36fc14558948 [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
Alan Jenkins463b4e42009-12-03 07:45:03 +0000121#define EEEPC_EC_SC00 0x61
122#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
123#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
124#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100125
Alan Jenkins463b4e42009-12-03 07:45:03 +0000126#define EEEPC_EC_SFB0 0xD0
127#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */
128
Corentin Charye1faa9d2008-03-13 12:57:18 +0100129
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 */
Eric Coopere59f8792008-03-13 12:55:46 +0100139 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
153/* Platform device/driver */
Alan Jenkinsc200da52009-08-28 12:56:40 +0000154static int eeepc_hotk_thaw(struct device *device);
155static int eeepc_hotk_restore(struct device *device);
156
157static struct dev_pm_ops eeepc_pm_ops = {
158 .thaw = eeepc_hotk_thaw,
159 .restore = eeepc_hotk_restore,
160};
161
Eric Coopere59f8792008-03-13 12:55:46 +0100162static struct platform_driver platform_driver = {
163 .driver = {
164 .name = EEEPC_HOTK_FILE,
165 .owner = THIS_MODULE,
Alan Jenkinsc200da52009-08-28 12:56:40 +0000166 .pm = &eeepc_pm_ops,
Eric Coopere59f8792008-03-13 12:55:46 +0100167 }
168};
169
170static struct platform_device *platform_device;
171
Matthew Garretta195dcd2008-08-19 12:13:20 +0100172struct key_entry {
173 char type;
174 u8 code;
175 u16 keycode;
176};
177
178enum { KE_KEY, KE_END };
179
180static struct key_entry eeepc_keymap[] = {
181 /* Sleep already handled via generic ACPI code */
182 {KE_KEY, 0x10, KEY_WLAN },
Alan Jenkins978605c2009-04-27 09:23:39 +0200183 {KE_KEY, 0x11, KEY_WLAN },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100184 {KE_KEY, 0x12, KEY_PROG1 },
185 {KE_KEY, 0x13, KEY_MUTE },
186 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
187 {KE_KEY, 0x15, KEY_VOLUMEUP },
Matthew Garrettb5f6f262009-01-20 16:17:46 +0100188 {KE_KEY, 0x1a, KEY_COFFEE },
189 {KE_KEY, 0x1b, KEY_ZOOM },
190 {KE_KEY, 0x1c, KEY_PROG2 },
191 {KE_KEY, 0x1d, KEY_PROG3 },
Darren Salt64b86b62009-04-27 09:23:38 +0200192 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
193 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100194 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
195 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
196 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
197 {KE_END, 0},
198};
199
Eric Coopere59f8792008-03-13 12:55:46 +0100200/*
201 * The hotkey driver declaration
202 */
203static int eeepc_hotk_add(struct acpi_device *device);
204static int eeepc_hotk_remove(struct acpi_device *device, int type);
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600205static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
Eric Coopere59f8792008-03-13 12:55:46 +0100206
207static const struct acpi_device_id eeepc_device_ids[] = {
208 {EEEPC_HOTK_HID, 0},
209 {"", 0},
210};
211MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
212
213static struct acpi_driver eeepc_hotk_driver = {
214 .name = EEEPC_HOTK_NAME,
215 .class = EEEPC_HOTK_CLASS,
Alan Jenkinseacec302009-12-03 07:44:55 +0000216 .owner = THIS_MODULE,
Eric Coopere59f8792008-03-13 12:55:46 +0100217 .ids = eeepc_device_ids,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600218 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Eric Coopere59f8792008-03-13 12:55:46 +0100219 .ops = {
220 .add = eeepc_hotk_add,
221 .remove = eeepc_hotk_remove,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600222 .notify = eeepc_hotk_notify,
Eric Coopere59f8792008-03-13 12:55:46 +0100223 },
224};
225
Corentin Chary2b121bc2009-06-25 13:25:36 +0200226/* PCI hotplug ops */
227static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
228
229static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
230 .owner = THIS_MODULE,
231 .get_adapter_status = eeepc_get_adapter_status,
232 .get_power_status = eeepc_get_adapter_status,
233};
234
Corentin Charya5fa4292008-03-13 12:56:37 +0100235/* The backlight device /sys/class/backlight */
236static struct backlight_device *eeepc_backlight_device;
237
Corentin Charye1faa9d2008-03-13 12:57:18 +0100238/* The hwmon device */
239static struct device *eeepc_hwmon_device;
240
Corentin Charya5fa4292008-03-13 12:56:37 +0100241/*
242 * The backlight class declaration
243 */
244static int read_brightness(struct backlight_device *bd);
245static int update_bl_status(struct backlight_device *bd);
246static struct backlight_ops eeepcbl_ops = {
247 .get_brightness = read_brightness,
248 .update_status = update_bl_status,
249};
250
Eric Coopere59f8792008-03-13 12:55:46 +0100251MODULE_AUTHOR("Corentin Chary, Eric Cooper");
252MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
253MODULE_LICENSE("GPL");
254
255/*
256 * ACPI Helpers
257 */
Alan Jenkins6b188a72009-12-03 07:45:02 +0000258static int write_acpi_int(acpi_handle handle, const char *method, int val)
Eric Coopere59f8792008-03-13 12:55:46 +0100259{
260 struct acpi_object_list params;
261 union acpi_object in_obj;
262 acpi_status status;
263
264 params.count = 1;
265 params.pointer = &in_obj;
266 in_obj.type = ACPI_TYPE_INTEGER;
267 in_obj.integer.value = val;
268
Alan Jenkins6b188a72009-12-03 07:45:02 +0000269 status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
Eric Coopere59f8792008-03-13 12:55:46 +0100270 return (status == AE_OK ? 0 : -1);
271}
272
273static int read_acpi_int(acpi_handle handle, const char *method, int *val)
274{
275 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400276 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100277
278 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
279 if (ACPI_FAILURE(status)) {
280 *val = -1;
281 return -1;
282 } else {
283 *val = result;
284 return 0;
285 }
286}
287
288static int set_acpi(int cm, int value)
289{
Alan Jenkins13f70022009-12-03 07:44:59 +0000290 const char *method = cm_setv[cm];
291
292 if (method == NULL)
293 return -ENODEV;
294 if ((ehotk->cm_supported & (0x1 << cm)) == 0)
295 return -ENODEV;
296
Alan Jenkins6b188a72009-12-03 07:45:02 +0000297 if (write_acpi_int(ehotk->handle, method, value))
Alan Jenkins13f70022009-12-03 07:44:59 +0000298 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100299 return 0;
300}
301
302static int get_acpi(int cm)
303{
Alan Jenkins13f70022009-12-03 07:44:59 +0000304 const char *method = cm_getv[cm];
305 int value;
306
307 if (method == NULL)
308 return -ENODEV;
309 if ((ehotk->cm_supported & (0x1 << cm)) == 0)
310 return -ENODEV;
311
312 if (read_acpi_int(ehotk->handle, method, &value))
313 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100314 return value;
315}
316
317/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100318 * Backlight
319 */
320static int read_brightness(struct backlight_device *bd)
321{
322 return get_acpi(CM_ASL_PANELBRIGHT);
323}
324
325static int set_brightness(struct backlight_device *bd, int value)
326{
Corentin Charya5fa4292008-03-13 12:56:37 +0100327 return set_acpi(CM_ASL_PANELBRIGHT, value);
328}
329
330static int update_bl_status(struct backlight_device *bd)
331{
332 return set_brightness(bd, bd->props.brightness);
333}
334
335/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100336 * Rfkill helpers
337 */
338
Johannes Berg19d337d2009-06-02 13:01:37 +0200339static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100340{
341 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200342 return false;
343 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100344}
345
Johannes Berg19d337d2009-06-02 13:01:37 +0200346static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100347{
Johannes Berg19d337d2009-06-02 13:01:37 +0200348 unsigned long asl = (unsigned long)data;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200349 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100350}
351
Johannes Berg19d337d2009-06-02 13:01:37 +0200352static const struct rfkill_ops eeepc_rfkill_ops = {
353 .set_block = eeepc_rfkill_set,
354};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100355
Rakib Mullickdcb73ee2009-10-13 00:13:32 +0200356static void __devinit eeepc_enable_camera(void)
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000357{
358 /*
359 * If the following call to set_acpi() fails, it's because there's no
360 * camera so we can ignore the error.
361 */
Luca Niccoli80f0c892009-10-16 22:22:47 +0200362 if (get_acpi(CM_ASL_CAMERA) == 0)
363 set_acpi(CM_ASL_CAMERA, 1);
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000364}
365
Matthew Garretta195dcd2008-08-19 12:13:20 +0100366/*
Eric Coopere59f8792008-03-13 12:55:46 +0100367 * Sys helpers
368 */
369static int parse_arg(const char *buf, unsigned long count, int *val)
370{
371 if (!count)
372 return 0;
373 if (sscanf(buf, "%i", val) != 1)
374 return -EINVAL;
375 return count;
376}
377
378static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
379{
380 int rv, value;
381
382 rv = parse_arg(buf, count, &value);
383 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200384 value = set_acpi(cm, value);
385 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000386 return -EIO;
Eric Coopere59f8792008-03-13 12:55:46 +0100387 return rv;
388}
389
390static ssize_t show_sys_acpi(int cm, char *buf)
391{
Corentin Charyf36509e2009-06-25 13:25:40 +0200392 int value = get_acpi(cm);
393
394 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000395 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200396 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100397}
398
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000399#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Eric Coopere59f8792008-03-13 12:55:46 +0100400 static ssize_t show_##_name(struct device *dev, \
401 struct device_attribute *attr, \
402 char *buf) \
403 { \
404 return show_sys_acpi(_cm, buf); \
405 } \
406 static ssize_t store_##_name(struct device *dev, \
407 struct device_attribute *attr, \
408 const char *buf, size_t count) \
409 { \
410 return store_sys_acpi(_cm, buf, count); \
411 } \
412 static struct device_attribute dev_attr_##_name = { \
413 .attr = { \
414 .name = __stringify(_name), \
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000415 .mode = _mode }, \
Eric Coopere59f8792008-03-13 12:55:46 +0100416 .show = show_##_name, \
417 .store = store_##_name, \
418 }
419
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000420EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
421EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
422EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000423
424struct eeepc_cpufv {
425 int num;
426 int cur;
427};
428
429static int get_cpufv(struct eeepc_cpufv *c)
430{
431 c->cur = get_acpi(CM_ASL_CPUFV);
432 c->num = (c->cur >> 8) & 0xff;
433 c->cur &= 0xff;
434 if (c->cur < 0 || c->num <= 0 || c->num > 12)
435 return -ENODEV;
436 return 0;
437}
438
439static ssize_t show_available_cpufv(struct device *dev,
440 struct device_attribute *attr,
441 char *buf)
442{
443 struct eeepc_cpufv c;
444 int i;
445 ssize_t len = 0;
446
447 if (get_cpufv(&c))
448 return -ENODEV;
449 for (i = 0; i < c.num; i++)
450 len += sprintf(buf + len, "%d ", i);
451 len += sprintf(buf + len, "\n");
452 return len;
453}
454
455static ssize_t show_cpufv(struct device *dev,
456 struct device_attribute *attr,
457 char *buf)
458{
459 struct eeepc_cpufv c;
460
461 if (get_cpufv(&c))
462 return -ENODEV;
463 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
464}
465
466static ssize_t store_cpufv(struct device *dev,
467 struct device_attribute *attr,
468 const char *buf, size_t count)
469{
470 struct eeepc_cpufv c;
471 int rv, value;
472
473 if (get_cpufv(&c))
474 return -ENODEV;
475 rv = parse_arg(buf, count, &value);
476 if (rv < 0)
477 return rv;
478 if (!rv || value < 0 || value >= c.num)
479 return -EINVAL;
480 set_acpi(CM_ASL_CPUFV, value);
481 return rv;
482}
483
484static struct device_attribute dev_attr_cpufv = {
485 .attr = {
486 .name = "cpufv",
487 .mode = 0644 },
488 .show = show_cpufv,
489 .store = store_cpufv
490};
491
492static struct device_attribute dev_attr_available_cpufv = {
493 .attr = {
494 .name = "available_cpufv",
495 .mode = 0444 },
496 .show = show_available_cpufv
497};
Eric Coopere59f8792008-03-13 12:55:46 +0100498
499static struct attribute *platform_attributes[] = {
500 &dev_attr_camera.attr,
501 &dev_attr_cardr.attr,
502 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200503 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000504 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100505 NULL
506};
507
508static struct attribute_group platform_attribute_group = {
509 .attrs = platform_attributes
510};
511
512/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000513 * LEDs
514 */
515/*
516 * These functions actually update the LED's, and are called from a
517 * workqueue. By doing this as separate work rather than when the LED
518 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
519 * potentially bad time, such as a timer interrupt.
520 */
521static int tpd_led_wk;
522
523static void tpd_led_update(struct work_struct *ignored)
524{
525 int value = tpd_led_wk;
526 set_acpi(CM_ASL_TPD, value);
527}
528
529static struct workqueue_struct *led_workqueue;
530static DECLARE_WORK(tpd_led_work, tpd_led_update);
531
532static void tpd_led_set(struct led_classdev *led_cdev,
533 enum led_brightness value)
534{
535 tpd_led_wk = (value > 0) ? 1 : 0;
536 queue_work(led_workqueue, &tpd_led_work);
537}
538
539static struct led_classdev tpd_led = {
540 .name = "eeepc::touchpad",
541 .brightness_set = tpd_led_set,
542 .max_brightness = 1
543};
544
545/*
Eric Coopere59f8792008-03-13 12:55:46 +0100546 * Hotkey functions
547 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100548static struct key_entry *eepc_get_entry_by_scancode(int code)
549{
550 struct key_entry *key;
551
552 for (key = eeepc_keymap; key->type != KE_END; key++)
553 if (code == key->code)
554 return key;
555
556 return NULL;
557}
558
559static struct key_entry *eepc_get_entry_by_keycode(int code)
560{
561 struct key_entry *key;
562
563 for (key = eeepc_keymap; key->type != KE_END; key++)
564 if (code == key->keycode && key->type == KE_KEY)
565 return key;
566
567 return NULL;
568}
569
570static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
571{
572 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
573
574 if (key && key->type == KE_KEY) {
575 *keycode = key->keycode;
576 return 0;
577 }
578
579 return -EINVAL;
580}
581
582static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
583{
584 struct key_entry *key;
585 int old_keycode;
586
587 if (keycode < 0 || keycode > KEY_MAX)
588 return -EINVAL;
589
590 key = eepc_get_entry_by_scancode(scancode);
591 if (key && key->type == KE_KEY) {
592 old_keycode = key->keycode;
593 key->keycode = keycode;
594 set_bit(keycode, dev->keybit);
595 if (!eepc_get_entry_by_keycode(old_keycode))
596 clear_bit(old_keycode, dev->keybit);
597 return 0;
598 }
599
600 return -EINVAL;
601}
602
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200603static void cmsg_quirk(int cm, const char *name)
604{
605 int dummy;
606
607 /* Some BIOSes do not report cm although it is avaliable.
608 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
609 if (!(ehotk->cm_supported & (1 << cm))
610 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
611 pr_info("%s (%x) not reported by BIOS,"
612 " enabling anyway\n", name, 1 << cm);
613 ehotk->cm_supported |= 1 << cm;
614 }
615}
616
617static void cmsg_quirks(void)
618{
619 cmsg_quirk(CM_ASL_LID, "LID");
620 cmsg_quirk(CM_ASL_TYPE, "TYPE");
621 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
622 cmsg_quirk(CM_ASL_TPD, "TPD");
623}
624
Alan Jenkins6b188a72009-12-03 07:45:02 +0000625static int eeepc_hotk_init(void)
Eric Coopere59f8792008-03-13 12:55:46 +0100626{
Alan Jenkins6b188a72009-12-03 07:45:02 +0000627 unsigned int init_flags;
Eric Coopere59f8792008-03-13 12:55:46 +0100628 int result;
629
630 result = acpi_bus_get_status(ehotk->device);
631 if (result)
632 return result;
Alan Jenkins6b188a72009-12-03 07:45:02 +0000633 if (!ehotk->device->status.present) {
Joe Perches19b53282009-06-25 13:25:37 +0200634 pr_err("Hotkey device not present, aborting\n");
Alan Jenkins6b188a72009-12-03 07:45:02 +0000635 return -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100636 }
Alan Jenkins6b188a72009-12-03 07:45:02 +0000637
638 init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
639 pr_notice("Hotkey init flags 0x%x\n", init_flags);
640
641 if (write_acpi_int(ehotk->handle, "INIT", init_flags)) {
642 pr_err("Hotkey initialization failed\n");
643 return -ENODEV;
644 }
645
646 /* get control methods supported */
647 if (read_acpi_int(ehotk->handle, "CMSG",
648 &ehotk->cm_supported)) {
649 pr_err("Get control methods supported failed\n");
650 return -ENODEV;
651 }
652 cmsg_quirks();
653 pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported);
654
Eric Coopere59f8792008-03-13 12:55:46 +0100655 return 0;
656}
657
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000658static int eeepc_backlight_notify(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100659{
660 struct backlight_device *bd = eeepc_backlight_device;
Alan Jenkinsa2a1d362009-12-03 07:45:00 +0000661 int old = bd->props.brightness;
662
663 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
664
665 return old;
Corentin Charya5fa4292008-03-13 12:56:37 +0100666}
667
Corentin Chary2b121bc2009-06-25 13:25:36 +0200668static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
669 u8 *value)
670{
671 int val = get_acpi(CM_ASL_WLAN);
672
673 if (val == 1 || val == 0)
674 *value = val;
675 else
676 return -EINVAL;
677
678 return 0;
679}
680
Corentin Chary58ce48a2009-10-16 22:22:46 +0200681static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100682{
683 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000684 struct pci_bus *bus;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200685 bool blocked = eeepc_wlan_rfkill_blocked();
Matthew Garrett57402942009-01-20 16:17:48 +0100686
Corentin Chary58ce48a2009-10-16 22:22:46 +0200687 if (ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000688 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000689
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000690 mutex_lock(&ehotk->hotplug_lock);
691
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000692 if (ehotk->hotplug_slot) {
693 bus = pci_find_bus(0, 1);
694 if (!bus) {
695 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000696 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100697 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000698
699 if (!blocked) {
700 dev = pci_get_slot(bus, 0);
701 if (dev) {
702 /* Device already present */
703 pci_dev_put(dev);
704 goto out_unlock;
705 }
706 dev = pci_scan_single_device(bus, 0);
707 if (dev) {
708 pci_bus_assign_resources(bus);
709 if (pci_bus_add_device(dev))
710 pr_err("Unable to hotplug wifi\n");
711 }
712 } else {
713 dev = pci_get_slot(bus, 0);
714 if (dev) {
715 pci_remove_bus_device(dev);
716 pci_dev_put(dev);
717 }
Matthew Garrett57402942009-01-20 16:17:48 +0100718 }
719 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000720
721out_unlock:
722 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100723}
724
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100725static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
726{
727 if (event != ACPI_NOTIFY_BUS_CHECK)
728 return;
729
Corentin Chary58ce48a2009-10-16 22:22:46 +0200730 eeepc_rfkill_hotplug();
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100731}
732
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000733static void eeepc_input_notify(int event)
Eric Coopere59f8792008-03-13 12:55:46 +0100734{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100735 static struct key_entry *key;
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000736
737 key = eepc_get_entry_by_scancode(event);
738 if (key) {
739 switch (key->type) {
740 case KE_KEY:
741 input_report_key(ehotk->inputdev, key->keycode,
742 1);
743 input_sync(ehotk->inputdev);
744 input_report_key(ehotk->inputdev, key->keycode,
745 0);
746 input_sync(ehotk->inputdev);
747 break;
748 }
749 }
750}
751
752static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
753{
Corentin Chary7950b712009-02-15 19:30:20 +0100754 u16 count;
755
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600756 if (event > ACPI_MAX_SYS_NOTIFY)
757 return;
Corentin Chary7950b712009-02-15 19:30:20 +0100758 count = ehotk->event_count[event % 128]++;
759 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100760 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
761 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100762 count);
Alan Jenkinsa2a1d362009-12-03 07:45:00 +0000763
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000764 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
765 int old_brightness, new_brightness;
766
767 /* Update backlight device. */
768 old_brightness = eeepc_backlight_notify();
769
770 /* Convert brightness event to keypress (obsolescent hack). */
771 new_brightness = event - NOTIFY_BRN_MIN;
772
773 if (new_brightness < old_brightness) {
774 event = NOTIFY_BRN_MIN; /* brightness down */
775 } else if (new_brightness > old_brightness) {
776 event = NOTIFY_BRN_MAX; /* brightness up */
777 } else {
778 /*
779 * no change in brightness - already at min/max,
780 * event will be desired value (or else ignored).
781 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100782 }
783 }
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000784 eeepc_input_notify(event);
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{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000913 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100914
Alan Jenkins463b4e42009-12-03 07:45:03 +0000915 ec_read(EEEPC_EC_FAN_PWM, &value);
916 return value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100917}
918
919static void eeepc_set_fan_pwm(int value)
920{
Corentin Chary04dcd842008-10-09 15:33:57 +0200921 value = SENSORS_LIMIT(value, 0, 255);
922 value = value * 100 / 255;
Alan Jenkins463b4e42009-12-03 07:45:03 +0000923 ec_write(EEEPC_EC_FAN_PWM, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100924}
925
926static int eeepc_get_fan_rpm(void)
927{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000928 u8 high = 0;
929 u8 low = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100930
Alan Jenkins463b4e42009-12-03 07:45:03 +0000931 ec_read(EEEPC_EC_FAN_HRPM, &high);
932 ec_read(EEEPC_EC_FAN_LRPM, &low);
933 return high << 8 | low;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100934}
935
936static int eeepc_get_fan_ctrl(void)
937{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000938 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100939
Alan Jenkins463b4e42009-12-03 07:45:03 +0000940 ec_read(EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000941 if (value & 0x02)
942 return 1; /* manual */
943 else
944 return 2; /* automatic */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100945}
946
947static void eeepc_set_fan_ctrl(int manual)
948{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000949 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100950
Alan Jenkins463b4e42009-12-03 07:45:03 +0000951 ec_read(EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000952 if (manual == 1)
Corentin Charye1faa9d2008-03-13 12:57:18 +0100953 value |= 0x02;
954 else
955 value &= ~0x02;
Alan Jenkins463b4e42009-12-03 07:45:03 +0000956 ec_write(EEEPC_EC_FAN_CTRL, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100957}
958
959static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
960{
961 int rv, value;
962
963 rv = parse_arg(buf, count, &value);
964 if (rv > 0)
965 set(value);
966 return rv;
967}
968
969static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
970{
971 return sprintf(buf, "%d\n", get());
972}
973
974#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
975 static ssize_t show_##_name(struct device *dev, \
976 struct device_attribute *attr, \
977 char *buf) \
978 { \
979 return show_sys_hwmon(_set, buf); \
980 } \
981 static ssize_t store_##_name(struct device *dev, \
982 struct device_attribute *attr, \
983 const char *buf, size_t count) \
984 { \
985 return store_sys_hwmon(_get, buf, count); \
986 } \
987 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
988
989EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200990EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100991 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
992EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
993 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
994
Corentin Chary04dcd842008-10-09 15:33:57 +0200995static ssize_t
996show_name(struct device *dev, struct device_attribute *attr, char *buf)
997{
998 return sprintf(buf, "eeepc\n");
999}
1000static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
1001
Corentin Charye1faa9d2008-03-13 12:57:18 +01001002static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +02001003 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001004 &sensor_dev_attr_fan1_input.dev_attr.attr,
1005 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001006 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001007 NULL
1008};
1009
1010static struct attribute_group hwmon_attribute_group = {
1011 .attrs = hwmon_attributes
1012};
1013
1014/*
Eric Coopere59f8792008-03-13 12:55:46 +01001015 * exit/init
1016 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001017static void eeepc_backlight_exit(void)
1018{
1019 if (eeepc_backlight_device)
1020 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001021 eeepc_backlight_device = NULL;
1022}
1023
1024static void eeepc_rfkill_exit(void)
1025{
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001026 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +02001027 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1028 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001029 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001030 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001031 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001032 ehotk->wlan_rfkill = NULL;
1033 }
1034 /*
1035 * Refresh pci hotplug in case the rfkill state was changed after
1036 * eeepc_unregister_rfkill_notifier()
1037 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001038 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001039 if (ehotk->hotplug_slot)
1040 pci_hp_deregister(ehotk->hotplug_slot);
1041
Alan Jenkinsa82580692009-08-29 10:28:30 +02001042 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001043 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001044 rfkill_destroy(ehotk->bluetooth_rfkill);
1045 ehotk->bluetooth_rfkill = NULL;
1046 }
1047 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001048 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001049 rfkill_destroy(ehotk->wwan3g_rfkill);
1050 ehotk->wwan3g_rfkill = NULL;
1051 }
1052 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001053 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001054 rfkill_destroy(ehotk->wimax_rfkill);
1055 ehotk->wimax_rfkill = NULL;
1056 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001057}
1058
1059static void eeepc_input_exit(void)
1060{
1061 if (ehotk->inputdev)
1062 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001063}
1064
Corentin Charye1faa9d2008-03-13 12:57:18 +01001065static void eeepc_hwmon_exit(void)
1066{
1067 struct device *hwmon;
1068
1069 hwmon = eeepc_hwmon_device;
1070 if (!hwmon)
1071 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001072 sysfs_remove_group(&hwmon->kobj,
1073 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001074 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001075 eeepc_hwmon_device = NULL;
1076}
1077
Corentin Chary3c0eb512009-12-03 07:44:52 +00001078static void eeepc_led_exit(void)
1079{
Corentin Chary3c0eb512009-12-03 07:44:52 +00001080 if (tpd_led.dev)
1081 led_classdev_unregister(&tpd_led);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001082 if (led_workqueue)
1083 destroy_workqueue(led_workqueue);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001084}
1085
Corentin Chary7de39382009-06-25 13:25:38 +02001086static int eeepc_new_rfkill(struct rfkill **rfkill,
1087 const char *name, struct device *dev,
1088 enum rfkill_type type, int cm)
1089{
1090 int result;
1091
Corentin Charyf36509e2009-06-25 13:25:40 +02001092 result = get_acpi(cm);
1093 if (result < 0)
1094 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001095
1096 *rfkill = rfkill_alloc(name, dev, type,
1097 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1098
1099 if (!*rfkill)
1100 return -EINVAL;
1101
1102 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1103 result = rfkill_register(*rfkill);
1104 if (result) {
1105 rfkill_destroy(*rfkill);
1106 *rfkill = NULL;
1107 return result;
1108 }
1109 return 0;
1110}
1111
1112
1113static int eeepc_rfkill_init(struct device *dev)
1114{
1115 int result = 0;
1116
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001117 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001118
Corentin Chary7de39382009-06-25 13:25:38 +02001119 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1120 "eeepc-wlan", dev,
1121 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1122
1123 if (result && result != -ENODEV)
1124 goto exit;
1125
1126 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1127 "eeepc-bluetooth", dev,
1128 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1129
1130 if (result && result != -ENODEV)
1131 goto exit;
1132
Corentin Chary3cd530b2009-06-25 13:25:42 +02001133 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1134 "eeepc-wwan3g", dev,
1135 RFKILL_TYPE_WWAN, CM_ASL_3G);
1136
1137 if (result && result != -ENODEV)
1138 goto exit;
1139
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001140 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1141 "eeepc-wimax", dev,
1142 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1143
1144 if (result && result != -ENODEV)
1145 goto exit;
1146
Corentin Chary7de39382009-06-25 13:25:38 +02001147 result = eeepc_setup_pci_hotplug();
1148 /*
1149 * If we get -EBUSY then something else is handling the PCI hotplug -
1150 * don't fail in this case
1151 */
1152 if (result == -EBUSY)
1153 result = 0;
1154
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001155 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001156 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1157 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1158 /*
1159 * Refresh pci hotplug in case the rfkill state was changed during
1160 * setup.
1161 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001162 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001163
Corentin Chary7de39382009-06-25 13:25:38 +02001164exit:
1165 if (result && result != -ENODEV)
1166 eeepc_rfkill_exit();
1167 return result;
1168}
1169
Corentin Charya5fa4292008-03-13 12:56:37 +01001170static int eeepc_backlight_init(struct device *dev)
1171{
1172 struct backlight_device *bd;
1173
1174 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1175 NULL, &eeepcbl_ops);
1176 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001177 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001178 eeepc_backlight_device = NULL;
1179 return PTR_ERR(bd);
1180 }
1181 eeepc_backlight_device = bd;
1182 bd->props.max_brightness = 15;
1183 bd->props.brightness = read_brightness(NULL);
1184 bd->props.power = FB_BLANK_UNBLANK;
1185 backlight_update_status(bd);
1186 return 0;
1187}
1188
Corentin Charye1faa9d2008-03-13 12:57:18 +01001189static int eeepc_hwmon_init(struct device *dev)
1190{
1191 struct device *hwmon;
1192 int result;
1193
1194 hwmon = hwmon_device_register(dev);
1195 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001196 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001197 eeepc_hwmon_device = NULL;
1198 return PTR_ERR(hwmon);
1199 }
1200 eeepc_hwmon_device = hwmon;
1201 result = sysfs_create_group(&hwmon->kobj,
1202 &hwmon_attribute_group);
1203 if (result)
1204 eeepc_hwmon_exit();
1205 return result;
1206}
1207
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001208static int eeepc_input_init(struct device *dev)
1209{
1210 const struct key_entry *key;
1211 int result;
1212
1213 ehotk->inputdev = input_allocate_device();
1214 if (!ehotk->inputdev) {
1215 pr_info("Unable to allocate input device\n");
1216 return -ENOMEM;
1217 }
1218 ehotk->inputdev->name = "Asus EeePC extra buttons";
1219 ehotk->inputdev->dev.parent = dev;
1220 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1221 ehotk->inputdev->id.bustype = BUS_HOST;
1222 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1223 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1224
1225 for (key = eeepc_keymap; key->type != KE_END; key++) {
1226 switch (key->type) {
1227 case KE_KEY:
1228 set_bit(EV_KEY, ehotk->inputdev->evbit);
1229 set_bit(key->keycode, ehotk->inputdev->keybit);
1230 break;
1231 }
1232 }
1233 result = input_register_device(ehotk->inputdev);
1234 if (result) {
1235 pr_info("Unable to register input device\n");
1236 input_free_device(ehotk->inputdev);
1237 return result;
1238 }
1239 return 0;
1240}
1241
Corentin Chary3c0eb512009-12-03 07:44:52 +00001242static int eeepc_led_init(struct device *dev)
1243{
1244 int rv;
1245
1246 if (get_acpi(CM_ASL_TPD) == -ENODEV)
1247 return 0;
1248
Corentin Chary3c0eb512009-12-03 07:44:52 +00001249 led_workqueue = create_singlethread_workqueue("led_workqueue");
1250 if (!led_workqueue)
1251 return -ENOMEM;
1252
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001253 rv = led_classdev_register(dev, &tpd_led);
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001254 if (rv) {
1255 destroy_workqueue(led_workqueue);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001256 return rv;
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001257 }
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001258
Corentin Chary3c0eb512009-12-03 07:44:52 +00001259 return 0;
1260}
1261
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001262static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001263{
1264 struct device *dev;
1265 int result;
1266
Alan Jenkins1e779852009-08-28 12:56:35 +00001267 pr_notice(EEEPC_HOTK_NAME "\n");
1268 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1269 if (!ehotk)
1270 return -ENOMEM;
Alan Jenkins1e779852009-08-28 12:56:35 +00001271 ehotk->handle = device->handle;
1272 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1273 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1274 device->driver_data = ehotk;
1275 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001276
Alan Jenkins6b188a72009-12-03 07:45:02 +00001277 result = eeepc_hotk_init();
Alan Jenkins1e779852009-08-28 12:56:35 +00001278 if (result)
Alan Jenkins22072e92009-12-03 07:45:05 +00001279 goto fail_platform_device1;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001280 eeepc_enable_camera();
1281
Eric Coopere59f8792008-03-13 12:55:46 +01001282 /* Register platform stuff */
Eric Coopere59f8792008-03-13 12:55:46 +01001283 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1284 if (!platform_device) {
1285 result = -ENOMEM;
1286 goto fail_platform_device1;
1287 }
1288 result = platform_device_add(platform_device);
1289 if (result)
1290 goto fail_platform_device2;
1291 result = sysfs_create_group(&platform_device->dev.kobj,
1292 &platform_attribute_group);
1293 if (result)
1294 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001295
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001296 dev = &platform_device->dev;
1297
1298 if (!acpi_video_backlight_support()) {
1299 result = eeepc_backlight_init(dev);
1300 if (result)
1301 goto fail_backlight;
1302 } else
1303 pr_info("Backlight controlled by ACPI video "
1304 "driver\n");
1305
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001306 result = eeepc_input_init(dev);
1307 if (result)
1308 goto fail_input;
1309
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001310 result = eeepc_hwmon_init(dev);
1311 if (result)
1312 goto fail_hwmon;
1313
Corentin Chary3c0eb512009-12-03 07:44:52 +00001314 result = eeepc_led_init(dev);
1315 if (result)
1316 goto fail_led;
1317
Corentin Chary7de39382009-06-25 13:25:38 +02001318 result = eeepc_rfkill_init(dev);
1319 if (result)
1320 goto fail_rfkill;
1321
Eric Coopere59f8792008-03-13 12:55:46 +01001322 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001323
Corentin Chary7de39382009-06-25 13:25:38 +02001324fail_rfkill:
Corentin Chary3c0eb512009-12-03 07:44:52 +00001325 eeepc_led_exit();
1326fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001327 eeepc_hwmon_exit();
1328fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001329 eeepc_input_exit();
1330fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001331 eeepc_backlight_exit();
1332fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001333 sysfs_remove_group(&platform_device->dev.kobj,
1334 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001335fail_sysfs:
1336 platform_device_del(platform_device);
1337fail_platform_device2:
1338 platform_device_put(platform_device);
1339fail_platform_device1:
Alan Jenkins1e779852009-08-28 12:56:35 +00001340 kfree(ehotk);
1341
Eric Coopere59f8792008-03-13 12:55:46 +01001342 return result;
1343}
1344
Alan Jenkins1e779852009-08-28 12:56:35 +00001345static int eeepc_hotk_remove(struct acpi_device *device, int type)
1346{
Alan Jenkins1e779852009-08-28 12:56:35 +00001347 eeepc_backlight_exit();
1348 eeepc_rfkill_exit();
1349 eeepc_input_exit();
1350 eeepc_hwmon_exit();
Corentin Chary3c0eb512009-12-03 07:44:52 +00001351 eeepc_led_exit();
Alan Jenkins1e779852009-08-28 12:56:35 +00001352 sysfs_remove_group(&platform_device->dev.kobj,
1353 &platform_attribute_group);
1354 platform_device_unregister(platform_device);
Alan Jenkins1e779852009-08-28 12:56:35 +00001355
1356 kfree(ehotk);
1357 return 0;
1358}
1359
1360static int __init eeepc_laptop_init(void)
1361{
1362 int result;
1363
Alan Jenkins22072e92009-12-03 07:45:05 +00001364 result = platform_driver_register(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001365 if (result < 0)
1366 return result;
Alan Jenkins22072e92009-12-03 07:45:05 +00001367
1368 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1369 if (result < 0)
1370 goto fail_acpi_driver;
Alan Jenkins1e779852009-08-28 12:56:35 +00001371 if (!ehotk) {
Alan Jenkins22072e92009-12-03 07:45:05 +00001372 result = -ENODEV;
1373 goto fail_no_device;
Alan Jenkins1e779852009-08-28 12:56:35 +00001374 }
1375 return 0;
Alan Jenkins22072e92009-12-03 07:45:05 +00001376
1377fail_no_device:
1378 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1379fail_acpi_driver:
1380 platform_driver_unregister(&platform_driver);
1381 return result;
Alan Jenkins1e779852009-08-28 12:56:35 +00001382}
1383
1384static void __exit eeepc_laptop_exit(void)
1385{
1386 acpi_bus_unregister_driver(&eeepc_hotk_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001387 platform_driver_unregister(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001388}
1389
Eric Coopere59f8792008-03-13 12:55:46 +01001390module_init(eeepc_laptop_init);
1391module_exit(eeepc_laptop_exit);