blob: f4f67967aae2a78785d0e72a84f766caeaae5550 [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
Alan Jenkins9db106b2009-12-03 07:45:06 +0000512static int eeepc_platform_init(void)
513{
514 int result;
515
516 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
517 if (!platform_device)
518 return -ENOMEM;
519
520 result = platform_device_add(platform_device);
521 if (result)
522 goto fail_platform_device;
523
524 result = sysfs_create_group(&platform_device->dev.kobj,
525 &platform_attribute_group);
526 if (result)
527 goto fail_sysfs;
528 return 0;
529
530fail_sysfs:
531 platform_device_del(platform_device);
532fail_platform_device:
533 platform_device_put(platform_device);
534 return result;
535}
536
537static void eeepc_platform_exit(void)
538{
539 sysfs_remove_group(&platform_device->dev.kobj,
540 &platform_attribute_group);
541 platform_device_unregister(platform_device);
542}
543
Eric Coopere59f8792008-03-13 12:55:46 +0100544/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000545 * LEDs
546 */
547/*
548 * These functions actually update the LED's, and are called from a
549 * workqueue. By doing this as separate work rather than when the LED
550 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
551 * potentially bad time, such as a timer interrupt.
552 */
553static int tpd_led_wk;
554
555static void tpd_led_update(struct work_struct *ignored)
556{
557 int value = tpd_led_wk;
558 set_acpi(CM_ASL_TPD, value);
559}
560
561static struct workqueue_struct *led_workqueue;
562static DECLARE_WORK(tpd_led_work, tpd_led_update);
563
564static void tpd_led_set(struct led_classdev *led_cdev,
565 enum led_brightness value)
566{
567 tpd_led_wk = (value > 0) ? 1 : 0;
568 queue_work(led_workqueue, &tpd_led_work);
569}
570
571static struct led_classdev tpd_led = {
572 .name = "eeepc::touchpad",
573 .brightness_set = tpd_led_set,
574 .max_brightness = 1
575};
576
577/*
Eric Coopere59f8792008-03-13 12:55:46 +0100578 * Hotkey functions
579 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100580static struct key_entry *eepc_get_entry_by_scancode(int code)
581{
582 struct key_entry *key;
583
584 for (key = eeepc_keymap; key->type != KE_END; key++)
585 if (code == key->code)
586 return key;
587
588 return NULL;
589}
590
591static struct key_entry *eepc_get_entry_by_keycode(int code)
592{
593 struct key_entry *key;
594
595 for (key = eeepc_keymap; key->type != KE_END; key++)
596 if (code == key->keycode && key->type == KE_KEY)
597 return key;
598
599 return NULL;
600}
601
602static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
603{
604 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
605
606 if (key && key->type == KE_KEY) {
607 *keycode = key->keycode;
608 return 0;
609 }
610
611 return -EINVAL;
612}
613
614static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
615{
616 struct key_entry *key;
617 int old_keycode;
618
619 if (keycode < 0 || keycode > KEY_MAX)
620 return -EINVAL;
621
622 key = eepc_get_entry_by_scancode(scancode);
623 if (key && key->type == KE_KEY) {
624 old_keycode = key->keycode;
625 key->keycode = keycode;
626 set_bit(keycode, dev->keybit);
627 if (!eepc_get_entry_by_keycode(old_keycode))
628 clear_bit(old_keycode, dev->keybit);
629 return 0;
630 }
631
632 return -EINVAL;
633}
634
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200635static void cmsg_quirk(int cm, const char *name)
636{
637 int dummy;
638
639 /* Some BIOSes do not report cm although it is avaliable.
640 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
641 if (!(ehotk->cm_supported & (1 << cm))
642 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
643 pr_info("%s (%x) not reported by BIOS,"
644 " enabling anyway\n", name, 1 << cm);
645 ehotk->cm_supported |= 1 << cm;
646 }
647}
648
649static void cmsg_quirks(void)
650{
651 cmsg_quirk(CM_ASL_LID, "LID");
652 cmsg_quirk(CM_ASL_TYPE, "TYPE");
653 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
654 cmsg_quirk(CM_ASL_TPD, "TPD");
655}
656
Alan Jenkins6b188a72009-12-03 07:45:02 +0000657static int eeepc_hotk_init(void)
Eric Coopere59f8792008-03-13 12:55:46 +0100658{
Alan Jenkins6b188a72009-12-03 07:45:02 +0000659 unsigned int init_flags;
Eric Coopere59f8792008-03-13 12:55:46 +0100660 int result;
661
662 result = acpi_bus_get_status(ehotk->device);
663 if (result)
664 return result;
Alan Jenkins6b188a72009-12-03 07:45:02 +0000665 if (!ehotk->device->status.present) {
Joe Perches19b53282009-06-25 13:25:37 +0200666 pr_err("Hotkey device not present, aborting\n");
Alan Jenkins6b188a72009-12-03 07:45:02 +0000667 return -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100668 }
Alan Jenkins6b188a72009-12-03 07:45:02 +0000669
670 init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
671 pr_notice("Hotkey init flags 0x%x\n", init_flags);
672
673 if (write_acpi_int(ehotk->handle, "INIT", init_flags)) {
674 pr_err("Hotkey initialization failed\n");
675 return -ENODEV;
676 }
677
678 /* get control methods supported */
679 if (read_acpi_int(ehotk->handle, "CMSG",
680 &ehotk->cm_supported)) {
681 pr_err("Get control methods supported failed\n");
682 return -ENODEV;
683 }
684 cmsg_quirks();
685 pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported);
686
Eric Coopere59f8792008-03-13 12:55:46 +0100687 return 0;
688}
689
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000690static int eeepc_backlight_notify(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100691{
692 struct backlight_device *bd = eeepc_backlight_device;
Alan Jenkinsa2a1d362009-12-03 07:45:00 +0000693 int old = bd->props.brightness;
694
695 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
696
697 return old;
Corentin Charya5fa4292008-03-13 12:56:37 +0100698}
699
Corentin Chary2b121bc2009-06-25 13:25:36 +0200700static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
701 u8 *value)
702{
703 int val = get_acpi(CM_ASL_WLAN);
704
705 if (val == 1 || val == 0)
706 *value = val;
707 else
708 return -EINVAL;
709
710 return 0;
711}
712
Corentin Chary58ce48a2009-10-16 22:22:46 +0200713static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100714{
715 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000716 struct pci_bus *bus;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200717 bool blocked = eeepc_wlan_rfkill_blocked();
Matthew Garrett57402942009-01-20 16:17:48 +0100718
Corentin Chary58ce48a2009-10-16 22:22:46 +0200719 if (ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000720 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000721
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000722 mutex_lock(&ehotk->hotplug_lock);
723
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000724 if (ehotk->hotplug_slot) {
725 bus = pci_find_bus(0, 1);
726 if (!bus) {
727 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000728 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100729 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000730
731 if (!blocked) {
732 dev = pci_get_slot(bus, 0);
733 if (dev) {
734 /* Device already present */
735 pci_dev_put(dev);
736 goto out_unlock;
737 }
738 dev = pci_scan_single_device(bus, 0);
739 if (dev) {
740 pci_bus_assign_resources(bus);
741 if (pci_bus_add_device(dev))
742 pr_err("Unable to hotplug wifi\n");
743 }
744 } else {
745 dev = pci_get_slot(bus, 0);
746 if (dev) {
747 pci_remove_bus_device(dev);
748 pci_dev_put(dev);
749 }
Matthew Garrett57402942009-01-20 16:17:48 +0100750 }
751 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000752
753out_unlock:
754 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100755}
756
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100757static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
758{
759 if (event != ACPI_NOTIFY_BUS_CHECK)
760 return;
761
Corentin Chary58ce48a2009-10-16 22:22:46 +0200762 eeepc_rfkill_hotplug();
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100763}
764
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000765static void eeepc_input_notify(int event)
Eric Coopere59f8792008-03-13 12:55:46 +0100766{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100767 static struct key_entry *key;
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000768
769 key = eepc_get_entry_by_scancode(event);
770 if (key) {
771 switch (key->type) {
772 case KE_KEY:
773 input_report_key(ehotk->inputdev, key->keycode,
774 1);
775 input_sync(ehotk->inputdev);
776 input_report_key(ehotk->inputdev, key->keycode,
777 0);
778 input_sync(ehotk->inputdev);
779 break;
780 }
781 }
782}
783
784static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
785{
Corentin Chary7950b712009-02-15 19:30:20 +0100786 u16 count;
787
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600788 if (event > ACPI_MAX_SYS_NOTIFY)
789 return;
Corentin Chary7950b712009-02-15 19:30:20 +0100790 count = ehotk->event_count[event % 128]++;
791 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100792 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
793 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100794 count);
Alan Jenkinsa2a1d362009-12-03 07:45:00 +0000795
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000796 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
797 int old_brightness, new_brightness;
798
799 /* Update backlight device. */
800 old_brightness = eeepc_backlight_notify();
801
802 /* Convert brightness event to keypress (obsolescent hack). */
803 new_brightness = event - NOTIFY_BRN_MIN;
804
805 if (new_brightness < old_brightness) {
806 event = NOTIFY_BRN_MIN; /* brightness down */
807 } else if (new_brightness > old_brightness) {
808 event = NOTIFY_BRN_MAX; /* brightness up */
809 } else {
810 /*
811 * no change in brightness - already at min/max,
812 * event will be desired value (or else ignored).
813 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100814 }
815 }
Alan Jenkinsbf9598bc2009-12-03 07:45:04 +0000816 eeepc_input_notify(event);
Eric Coopere59f8792008-03-13 12:55:46 +0100817}
818
Matthew Garrett57402942009-01-20 16:17:48 +0100819static int eeepc_register_rfkill_notifier(char *node)
820{
821 acpi_status status = AE_OK;
822 acpi_handle handle;
823
824 status = acpi_get_handle(NULL, node, &handle);
825
826 if (ACPI_SUCCESS(status)) {
827 status = acpi_install_notify_handler(handle,
828 ACPI_SYSTEM_NOTIFY,
829 eeepc_rfkill_notify,
830 NULL);
831 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200832 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100833 } else
834 return -ENODEV;
835
836 return 0;
837}
838
839static void eeepc_unregister_rfkill_notifier(char *node)
840{
841 acpi_status status = AE_OK;
842 acpi_handle handle;
843
844 status = acpi_get_handle(NULL, node, &handle);
845
846 if (ACPI_SUCCESS(status)) {
847 status = acpi_remove_notify_handler(handle,
848 ACPI_SYSTEM_NOTIFY,
849 eeepc_rfkill_notify);
850 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200851 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100852 node);
853 }
854}
855
Corentin Chary2b121bc2009-06-25 13:25:36 +0200856static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
857{
858 kfree(hotplug_slot->info);
859 kfree(hotplug_slot);
860}
861
862static int eeepc_setup_pci_hotplug(void)
863{
864 int ret = -ENOMEM;
865 struct pci_bus *bus = pci_find_bus(0, 1);
866
867 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200868 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200869 return -ENODEV;
870 }
871
872 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
873 if (!ehotk->hotplug_slot)
874 goto error_slot;
875
876 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
877 GFP_KERNEL);
878 if (!ehotk->hotplug_slot->info)
879 goto error_info;
880
881 ehotk->hotplug_slot->private = ehotk;
882 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
883 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
884 eeepc_get_adapter_status(ehotk->hotplug_slot,
885 &ehotk->hotplug_slot->info->adapter_status);
886
887 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
888 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200889 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200890 goto error_register;
891 }
892
893 return 0;
894
895error_register:
896 kfree(ehotk->hotplug_slot->info);
897error_info:
898 kfree(ehotk->hotplug_slot);
899 ehotk->hotplug_slot = NULL;
900error_slot:
901 return ret;
902}
903
Alan Jenkinsc200da52009-08-28 12:56:40 +0000904static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100905{
Corentin Chary7de39382009-06-25 13:25:38 +0200906 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100907 bool wlan;
908
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000909 /*
910 * Work around bios bug - acpi _PTS turns off the wireless led
911 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000912 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100913 */
914 wlan = get_acpi(CM_ASL_WLAN);
915 set_acpi(CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100916 }
917
Alan Jenkinsc200da52009-08-28 12:56:40 +0000918 return 0;
919}
920
921static int eeepc_hotk_restore(struct device *device)
922{
923 /* Refresh both wlan rfkill state and pci hotplug */
924 if (ehotk->wlan_rfkill)
Corentin Chary58ce48a2009-10-16 22:22:46 +0200925 eeepc_rfkill_hotplug();
Alan Jenkinsc200da52009-08-28 12:56:40 +0000926
Corentin Chary7de39382009-06-25 13:25:38 +0200927 if (ehotk->bluetooth_rfkill)
928 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100929 get_acpi(CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa4746102009-08-28 12:56:38 +0000930 if (ehotk->wwan3g_rfkill)
931 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
932 get_acpi(CM_ASL_3G) != 1);
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000933 if (ehotk->wimax_rfkill)
934 rfkill_set_sw_state(ehotk->wimax_rfkill,
935 get_acpi(CM_ASL_WIMAX) != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100936
937 return 0;
938}
939
Eric Coopere59f8792008-03-13 12:55:46 +0100940/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100941 * Hwmon
942 */
943static int eeepc_get_fan_pwm(void)
944{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000945 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100946
Alan Jenkins463b4e42009-12-03 07:45:03 +0000947 ec_read(EEEPC_EC_FAN_PWM, &value);
948 return value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100949}
950
951static void eeepc_set_fan_pwm(int value)
952{
Corentin Chary04dcd842008-10-09 15:33:57 +0200953 value = SENSORS_LIMIT(value, 0, 255);
954 value = value * 100 / 255;
Alan Jenkins463b4e42009-12-03 07:45:03 +0000955 ec_write(EEEPC_EC_FAN_PWM, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100956}
957
958static int eeepc_get_fan_rpm(void)
959{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000960 u8 high = 0;
961 u8 low = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100962
Alan Jenkins463b4e42009-12-03 07:45:03 +0000963 ec_read(EEEPC_EC_FAN_HRPM, &high);
964 ec_read(EEEPC_EC_FAN_LRPM, &low);
965 return high << 8 | low;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100966}
967
968static int eeepc_get_fan_ctrl(void)
969{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000970 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100971
Alan Jenkins463b4e42009-12-03 07:45:03 +0000972 ec_read(EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000973 if (value & 0x02)
974 return 1; /* manual */
975 else
976 return 2; /* automatic */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100977}
978
979static void eeepc_set_fan_ctrl(int manual)
980{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000981 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100982
Alan Jenkins463b4e42009-12-03 07:45:03 +0000983 ec_read(EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000984 if (manual == 1)
Corentin Charye1faa9d2008-03-13 12:57:18 +0100985 value |= 0x02;
986 else
987 value &= ~0x02;
Alan Jenkins463b4e42009-12-03 07:45:03 +0000988 ec_write(EEEPC_EC_FAN_CTRL, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100989}
990
991static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
992{
993 int rv, value;
994
995 rv = parse_arg(buf, count, &value);
996 if (rv > 0)
997 set(value);
998 return rv;
999}
1000
1001static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
1002{
1003 return sprintf(buf, "%d\n", get());
1004}
1005
1006#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
1007 static ssize_t show_##_name(struct device *dev, \
1008 struct device_attribute *attr, \
1009 char *buf) \
1010 { \
1011 return show_sys_hwmon(_set, buf); \
1012 } \
1013 static ssize_t store_##_name(struct device *dev, \
1014 struct device_attribute *attr, \
1015 const char *buf, size_t count) \
1016 { \
1017 return store_sys_hwmon(_get, buf, count); \
1018 } \
1019 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
1020
1021EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +02001022EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001023 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
1024EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
1025 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
1026
Corentin Chary04dcd842008-10-09 15:33:57 +02001027static ssize_t
1028show_name(struct device *dev, struct device_attribute *attr, char *buf)
1029{
1030 return sprintf(buf, "eeepc\n");
1031}
1032static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
1033
Corentin Charye1faa9d2008-03-13 12:57:18 +01001034static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +02001035 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001036 &sensor_dev_attr_fan1_input.dev_attr.attr,
1037 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001038 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001039 NULL
1040};
1041
1042static struct attribute_group hwmon_attribute_group = {
1043 .attrs = hwmon_attributes
1044};
1045
1046/*
Eric Coopere59f8792008-03-13 12:55:46 +01001047 * exit/init
1048 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001049static void eeepc_backlight_exit(void)
1050{
1051 if (eeepc_backlight_device)
1052 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001053 eeepc_backlight_device = NULL;
1054}
1055
1056static void eeepc_rfkill_exit(void)
1057{
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001058 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +02001059 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1060 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001061 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001062 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001063 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001064 ehotk->wlan_rfkill = NULL;
1065 }
1066 /*
1067 * Refresh pci hotplug in case the rfkill state was changed after
1068 * eeepc_unregister_rfkill_notifier()
1069 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001070 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001071 if (ehotk->hotplug_slot)
1072 pci_hp_deregister(ehotk->hotplug_slot);
1073
Alan Jenkinsa82580692009-08-29 10:28:30 +02001074 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001075 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001076 rfkill_destroy(ehotk->bluetooth_rfkill);
1077 ehotk->bluetooth_rfkill = NULL;
1078 }
1079 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001080 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001081 rfkill_destroy(ehotk->wwan3g_rfkill);
1082 ehotk->wwan3g_rfkill = NULL;
1083 }
1084 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001085 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa82580692009-08-29 10:28:30 +02001086 rfkill_destroy(ehotk->wimax_rfkill);
1087 ehotk->wimax_rfkill = NULL;
1088 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001089}
1090
1091static void eeepc_input_exit(void)
1092{
1093 if (ehotk->inputdev)
1094 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001095}
1096
Corentin Charye1faa9d2008-03-13 12:57:18 +01001097static void eeepc_hwmon_exit(void)
1098{
1099 struct device *hwmon;
1100
1101 hwmon = eeepc_hwmon_device;
1102 if (!hwmon)
1103 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001104 sysfs_remove_group(&hwmon->kobj,
1105 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001106 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001107 eeepc_hwmon_device = NULL;
1108}
1109
Corentin Chary3c0eb512009-12-03 07:44:52 +00001110static void eeepc_led_exit(void)
1111{
Corentin Chary3c0eb512009-12-03 07:44:52 +00001112 if (tpd_led.dev)
1113 led_classdev_unregister(&tpd_led);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001114 if (led_workqueue)
1115 destroy_workqueue(led_workqueue);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001116}
1117
Corentin Chary7de39382009-06-25 13:25:38 +02001118static int eeepc_new_rfkill(struct rfkill **rfkill,
1119 const char *name, struct device *dev,
1120 enum rfkill_type type, int cm)
1121{
1122 int result;
1123
Corentin Charyf36509e2009-06-25 13:25:40 +02001124 result = get_acpi(cm);
1125 if (result < 0)
1126 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001127
1128 *rfkill = rfkill_alloc(name, dev, type,
1129 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1130
1131 if (!*rfkill)
1132 return -EINVAL;
1133
1134 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1135 result = rfkill_register(*rfkill);
1136 if (result) {
1137 rfkill_destroy(*rfkill);
1138 *rfkill = NULL;
1139 return result;
1140 }
1141 return 0;
1142}
1143
1144
1145static int eeepc_rfkill_init(struct device *dev)
1146{
1147 int result = 0;
1148
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001149 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001150
Corentin Chary7de39382009-06-25 13:25:38 +02001151 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1152 "eeepc-wlan", dev,
1153 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1154
1155 if (result && result != -ENODEV)
1156 goto exit;
1157
1158 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1159 "eeepc-bluetooth", dev,
1160 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1161
1162 if (result && result != -ENODEV)
1163 goto exit;
1164
Corentin Chary3cd530b2009-06-25 13:25:42 +02001165 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1166 "eeepc-wwan3g", dev,
1167 RFKILL_TYPE_WWAN, CM_ASL_3G);
1168
1169 if (result && result != -ENODEV)
1170 goto exit;
1171
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001172 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1173 "eeepc-wimax", dev,
1174 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1175
1176 if (result && result != -ENODEV)
1177 goto exit;
1178
Corentin Chary7de39382009-06-25 13:25:38 +02001179 result = eeepc_setup_pci_hotplug();
1180 /*
1181 * If we get -EBUSY then something else is handling the PCI hotplug -
1182 * don't fail in this case
1183 */
1184 if (result == -EBUSY)
1185 result = 0;
1186
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001187 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001188 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1189 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1190 /*
1191 * Refresh pci hotplug in case the rfkill state was changed during
1192 * setup.
1193 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001194 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001195
Corentin Chary7de39382009-06-25 13:25:38 +02001196exit:
1197 if (result && result != -ENODEV)
1198 eeepc_rfkill_exit();
1199 return result;
1200}
1201
Corentin Charya5fa4292008-03-13 12:56:37 +01001202static int eeepc_backlight_init(struct device *dev)
1203{
1204 struct backlight_device *bd;
1205
1206 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1207 NULL, &eeepcbl_ops);
1208 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001209 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001210 eeepc_backlight_device = NULL;
1211 return PTR_ERR(bd);
1212 }
1213 eeepc_backlight_device = bd;
1214 bd->props.max_brightness = 15;
1215 bd->props.brightness = read_brightness(NULL);
1216 bd->props.power = FB_BLANK_UNBLANK;
1217 backlight_update_status(bd);
1218 return 0;
1219}
1220
Corentin Charye1faa9d2008-03-13 12:57:18 +01001221static int eeepc_hwmon_init(struct device *dev)
1222{
1223 struct device *hwmon;
1224 int result;
1225
1226 hwmon = hwmon_device_register(dev);
1227 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001228 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001229 eeepc_hwmon_device = NULL;
1230 return PTR_ERR(hwmon);
1231 }
1232 eeepc_hwmon_device = hwmon;
1233 result = sysfs_create_group(&hwmon->kobj,
1234 &hwmon_attribute_group);
1235 if (result)
1236 eeepc_hwmon_exit();
1237 return result;
1238}
1239
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001240static int eeepc_input_init(struct device *dev)
1241{
1242 const struct key_entry *key;
1243 int result;
1244
1245 ehotk->inputdev = input_allocate_device();
1246 if (!ehotk->inputdev) {
1247 pr_info("Unable to allocate input device\n");
1248 return -ENOMEM;
1249 }
1250 ehotk->inputdev->name = "Asus EeePC extra buttons";
1251 ehotk->inputdev->dev.parent = dev;
1252 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1253 ehotk->inputdev->id.bustype = BUS_HOST;
1254 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1255 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1256
1257 for (key = eeepc_keymap; key->type != KE_END; key++) {
1258 switch (key->type) {
1259 case KE_KEY:
1260 set_bit(EV_KEY, ehotk->inputdev->evbit);
1261 set_bit(key->keycode, ehotk->inputdev->keybit);
1262 break;
1263 }
1264 }
1265 result = input_register_device(ehotk->inputdev);
1266 if (result) {
1267 pr_info("Unable to register input device\n");
1268 input_free_device(ehotk->inputdev);
1269 return result;
1270 }
1271 return 0;
1272}
1273
Corentin Chary3c0eb512009-12-03 07:44:52 +00001274static int eeepc_led_init(struct device *dev)
1275{
1276 int rv;
1277
1278 if (get_acpi(CM_ASL_TPD) == -ENODEV)
1279 return 0;
1280
Corentin Chary3c0eb512009-12-03 07:44:52 +00001281 led_workqueue = create_singlethread_workqueue("led_workqueue");
1282 if (!led_workqueue)
1283 return -ENOMEM;
1284
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001285 rv = led_classdev_register(dev, &tpd_led);
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001286 if (rv) {
1287 destroy_workqueue(led_workqueue);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001288 return rv;
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001289 }
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001290
Corentin Chary3c0eb512009-12-03 07:44:52 +00001291 return 0;
1292}
1293
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001294static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001295{
1296 struct device *dev;
1297 int result;
1298
Alan Jenkins1e779852009-08-28 12:56:35 +00001299 pr_notice(EEEPC_HOTK_NAME "\n");
1300 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1301 if (!ehotk)
1302 return -ENOMEM;
Alan Jenkins1e779852009-08-28 12:56:35 +00001303 ehotk->handle = device->handle;
1304 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1305 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1306 device->driver_data = ehotk;
1307 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001308
Alan Jenkins6b188a72009-12-03 07:45:02 +00001309 result = eeepc_hotk_init();
Alan Jenkins1e779852009-08-28 12:56:35 +00001310 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +00001311 goto fail_platform;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001312 eeepc_enable_camera();
1313
Alan Jenkins9db106b2009-12-03 07:45:06 +00001314 result = eeepc_platform_init();
Eric Coopere59f8792008-03-13 12:55:46 +01001315 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +00001316 goto fail_platform;
Corentin Chary7de39382009-06-25 13:25:38 +02001317
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001318 dev = &platform_device->dev;
1319
1320 if (!acpi_video_backlight_support()) {
1321 result = eeepc_backlight_init(dev);
1322 if (result)
1323 goto fail_backlight;
1324 } else
Alan Jenkins9db106b2009-12-03 07:45:06 +00001325 pr_info("Backlight controlled by ACPI video driver\n");
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001326
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001327 result = eeepc_input_init(dev);
1328 if (result)
1329 goto fail_input;
1330
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001331 result = eeepc_hwmon_init(dev);
1332 if (result)
1333 goto fail_hwmon;
1334
Corentin Chary3c0eb512009-12-03 07:44:52 +00001335 result = eeepc_led_init(dev);
1336 if (result)
1337 goto fail_led;
1338
Corentin Chary7de39382009-06-25 13:25:38 +02001339 result = eeepc_rfkill_init(dev);
1340 if (result)
1341 goto fail_rfkill;
1342
Eric Coopere59f8792008-03-13 12:55:46 +01001343 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001344
Corentin Chary7de39382009-06-25 13:25:38 +02001345fail_rfkill:
Corentin Chary3c0eb512009-12-03 07:44:52 +00001346 eeepc_led_exit();
1347fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001348 eeepc_hwmon_exit();
1349fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001350 eeepc_input_exit();
1351fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001352 eeepc_backlight_exit();
1353fail_backlight:
Alan Jenkins9db106b2009-12-03 07:45:06 +00001354 eeepc_platform_exit();
1355fail_platform:
Alan Jenkins1e779852009-08-28 12:56:35 +00001356 kfree(ehotk);
1357
Eric Coopere59f8792008-03-13 12:55:46 +01001358 return result;
1359}
1360
Alan Jenkins1e779852009-08-28 12:56:35 +00001361static int eeepc_hotk_remove(struct acpi_device *device, int type)
1362{
Alan Jenkins1e779852009-08-28 12:56:35 +00001363 eeepc_backlight_exit();
1364 eeepc_rfkill_exit();
1365 eeepc_input_exit();
1366 eeepc_hwmon_exit();
Corentin Chary3c0eb512009-12-03 07:44:52 +00001367 eeepc_led_exit();
Alan Jenkins9db106b2009-12-03 07:45:06 +00001368 eeepc_platform_exit();
Alan Jenkins1e779852009-08-28 12:56:35 +00001369
1370 kfree(ehotk);
1371 return 0;
1372}
1373
1374static int __init eeepc_laptop_init(void)
1375{
1376 int result;
1377
Alan Jenkins22072e92009-12-03 07:45:05 +00001378 result = platform_driver_register(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001379 if (result < 0)
1380 return result;
Alan Jenkins22072e92009-12-03 07:45:05 +00001381
1382 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1383 if (result < 0)
1384 goto fail_acpi_driver;
Alan Jenkins1e779852009-08-28 12:56:35 +00001385 if (!ehotk) {
Alan Jenkins22072e92009-12-03 07:45:05 +00001386 result = -ENODEV;
1387 goto fail_no_device;
Alan Jenkins1e779852009-08-28 12:56:35 +00001388 }
1389 return 0;
Alan Jenkins22072e92009-12-03 07:45:05 +00001390
1391fail_no_device:
1392 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1393fail_acpi_driver:
1394 platform_driver_unregister(&platform_driver);
1395 return result;
Alan Jenkins1e779852009-08-28 12:56:35 +00001396}
1397
1398static void __exit eeepc_laptop_exit(void)
1399{
1400 acpi_bus_unregister_driver(&eeepc_hotk_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001401 platform_driver_unregister(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001402}
1403
Eric Coopere59f8792008-03-13 12:55:46 +01001404module_init(eeepc_laptop_init);
1405module_exit(eeepc_laptop_exit);