blob: f6b7796b24ca97cf36d5d1641d389ee7d1f0262f [file] [log] [blame]
Eric Coopere59f8792008-03-13 12:55:46 +01001/*
2 * eepc-laptop.c - Asus Eee PC extras
3 *
4 * Based on asus_acpi.c as patched for the Eee PC by Asus:
5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
6 * Based on eee.c from eeepc-linux
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
Joe Perches19b53282009-06-25 13:25:37 +020019#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
Eric Coopere59f8792008-03-13 12:55:46 +010021#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/types.h>
25#include <linux/platform_device.h>
Corentin Charya5fa4292008-03-13 12:56:37 +010026#include <linux/backlight.h>
27#include <linux/fb.h>
Corentin Charye1faa9d2008-03-13 12:57:18 +010028#include <linux/hwmon.h>
29#include <linux/hwmon-sysfs.h>
Eric Coopere59f8792008-03-13 12:55:46 +010030#include <acpi/acpi_drivers.h>
31#include <acpi/acpi_bus.h>
32#include <linux/uaccess.h>
Matthew Garretta195dcd2008-08-19 12:13:20 +010033#include <linux/input.h>
34#include <linux/rfkill.h>
Matthew Garrett57402942009-01-20 16:17:48 +010035#include <linux/pci.h>
Corentin Chary2b121bc2009-06-25 13:25:36 +020036#include <linux/pci_hotplug.h>
Corentin Chary3c0eb512009-12-03 07:44:52 +000037#include <linux/leds.h>
Eric Coopere59f8792008-03-13 12:55:46 +010038
39#define EEEPC_LAPTOP_VERSION "0.1"
40
41#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
42#define EEEPC_HOTK_FILE "eeepc"
43#define EEEPC_HOTK_CLASS "hotkey"
44#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
45#define EEEPC_HOTK_HID "ASUS010"
46
Eric Coopere59f8792008-03-13 12:55:46 +010047
48/*
49 * Definitions for Asus EeePC
50 */
51#define NOTIFY_WLAN_ON 0x10
Corentin Charya5fa4292008-03-13 12:56:37 +010052#define NOTIFY_BRN_MIN 0x20
53#define NOTIFY_BRN_MAX 0x2f
Eric Coopere59f8792008-03-13 12:55:46 +010054
55enum {
56 DISABLE_ASL_WLAN = 0x0001,
57 DISABLE_ASL_BLUETOOTH = 0x0002,
58 DISABLE_ASL_IRDA = 0x0004,
59 DISABLE_ASL_CAMERA = 0x0008,
60 DISABLE_ASL_TV = 0x0010,
61 DISABLE_ASL_GPS = 0x0020,
62 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
63 DISABLE_ASL_MODEM = 0x0080,
Corentin Charyb7b700d2009-06-16 19:28:52 +000064 DISABLE_ASL_CARDREADER = 0x0100,
65 DISABLE_ASL_3G = 0x0200,
66 DISABLE_ASL_WIMAX = 0x0400,
67 DISABLE_ASL_HWCF = 0x0800
Eric Coopere59f8792008-03-13 12:55:46 +010068};
69
70enum {
71 CM_ASL_WLAN = 0,
72 CM_ASL_BLUETOOTH,
73 CM_ASL_IRDA,
74 CM_ASL_1394,
75 CM_ASL_CAMERA,
76 CM_ASL_TV,
77 CM_ASL_GPS,
78 CM_ASL_DVDROM,
79 CM_ASL_DISPLAYSWITCH,
80 CM_ASL_PANELBRIGHT,
81 CM_ASL_BIOSFLASH,
82 CM_ASL_ACPIFLASH,
83 CM_ASL_CPUFV,
84 CM_ASL_CPUTEMPERATURE,
85 CM_ASL_FANCPU,
86 CM_ASL_FANCHASSIS,
87 CM_ASL_USBPORT1,
88 CM_ASL_USBPORT2,
89 CM_ASL_USBPORT3,
90 CM_ASL_MODEM,
91 CM_ASL_CARDREADER,
Corentin Charyb7b700d2009-06-16 19:28:52 +000092 CM_ASL_3G,
93 CM_ASL_WIMAX,
94 CM_ASL_HWCF,
95 CM_ASL_LID,
96 CM_ASL_TYPE,
97 CM_ASL_PANELPOWER, /*P901*/
98 CM_ASL_TPD
Eric Coopere59f8792008-03-13 12:55:46 +010099};
100
Adrian Bunk14109462008-06-25 19:25:47 +0300101static const char *cm_getv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000102 "WLDG", "BTHG", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100103 "CAMG", NULL, NULL, NULL,
104 NULL, "PBLG", NULL, NULL,
105 "CFVG", NULL, NULL, NULL,
106 "USBG", NULL, NULL, "MODG",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000107 "CRDG", "M3GG", "WIMG", "HWCF",
108 "LIDG", "TYPE", "PBPG", "TPDG"
Eric Coopere59f8792008-03-13 12:55:46 +0100109};
110
Adrian Bunk14109462008-06-25 19:25:47 +0300111static const char *cm_setv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000112 "WLDS", "BTHS", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100113 "CAMS", NULL, NULL, NULL,
114 "SDSP", "PBLS", "HDPS", NULL,
115 "CFVS", NULL, NULL, NULL,
116 "USBG", NULL, NULL, "MODS",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000117 "CRDS", "M3GS", "WIMS", NULL,
118 NULL, NULL, "PBPS", "TPDS"
Eric Coopere59f8792008-03-13 12:55:46 +0100119};
120
Corentin Charye1faa9d2008-03-13 12:57:18 +0100121#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
122
123#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
124#define EEEPC_EC_SC02 0x63
125#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
126#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
127#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
128#define EEEPC_EC_SFB3 0xD3
129
Eric Coopere59f8792008-03-13 12:55:46 +0100130/*
131 * This is the main structure, we can use it to store useful information
132 * about the hotk device
133 */
134struct eeepc_hotk {
135 struct acpi_device *device; /* the device we are in */
136 acpi_handle handle; /* the handle of the hotk device */
137 u32 cm_supported; /* the control methods supported
138 by this BIOS */
139 uint init_flag; /* Init flags */
140 u16 event_count[128]; /* count for each event */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100141 struct input_dev *inputdev;
142 u16 *keycode_map;
Corentin Chary7de39382009-06-25 13:25:38 +0200143 struct rfkill *wlan_rfkill;
144 struct rfkill *bluetooth_rfkill;
Corentin Chary3cd530b2009-06-25 13:25:42 +0200145 struct rfkill *wwan3g_rfkill;
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000146 struct rfkill *wimax_rfkill;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200147 struct hotplug_slot *hotplug_slot;
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000148 struct mutex hotplug_lock;
Eric Coopere59f8792008-03-13 12:55:46 +0100149};
150
151/* The actual device the driver binds to */
152static struct eeepc_hotk *ehotk;
153
154/* Platform device/driver */
Alan Jenkinsc200da52009-08-28 12:56:40 +0000155static int eeepc_hotk_thaw(struct device *device);
156static int eeepc_hotk_restore(struct device *device);
157
158static struct dev_pm_ops eeepc_pm_ops = {
159 .thaw = eeepc_hotk_thaw,
160 .restore = eeepc_hotk_restore,
161};
162
Eric Coopere59f8792008-03-13 12:55:46 +0100163static struct platform_driver platform_driver = {
164 .driver = {
165 .name = EEEPC_HOTK_FILE,
166 .owner = THIS_MODULE,
Alan Jenkinsc200da52009-08-28 12:56:40 +0000167 .pm = &eeepc_pm_ops,
Eric Coopere59f8792008-03-13 12:55:46 +0100168 }
169};
170
171static struct platform_device *platform_device;
172
Matthew Garretta195dcd2008-08-19 12:13:20 +0100173struct key_entry {
174 char type;
175 u8 code;
176 u16 keycode;
177};
178
179enum { KE_KEY, KE_END };
180
181static struct key_entry eeepc_keymap[] = {
182 /* Sleep already handled via generic ACPI code */
183 {KE_KEY, 0x10, KEY_WLAN },
Alan Jenkins978605c2009-04-27 09:23:39 +0200184 {KE_KEY, 0x11, KEY_WLAN },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100185 {KE_KEY, 0x12, KEY_PROG1 },
186 {KE_KEY, 0x13, KEY_MUTE },
187 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
188 {KE_KEY, 0x15, KEY_VOLUMEUP },
Matthew Garrettb5f6f262009-01-20 16:17:46 +0100189 {KE_KEY, 0x1a, KEY_COFFEE },
190 {KE_KEY, 0x1b, KEY_ZOOM },
191 {KE_KEY, 0x1c, KEY_PROG2 },
192 {KE_KEY, 0x1d, KEY_PROG3 },
Darren Salt64b86b62009-04-27 09:23:38 +0200193 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
194 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100195 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
196 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
197 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
198 {KE_END, 0},
199};
200
Eric Coopere59f8792008-03-13 12:55:46 +0100201/*
202 * The hotkey driver declaration
203 */
204static int eeepc_hotk_add(struct acpi_device *device);
205static int eeepc_hotk_remove(struct acpi_device *device, int type);
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600206static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
Eric Coopere59f8792008-03-13 12:55:46 +0100207
208static const struct acpi_device_id eeepc_device_ids[] = {
209 {EEEPC_HOTK_HID, 0},
210 {"", 0},
211};
212MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
213
214static struct acpi_driver eeepc_hotk_driver = {
215 .name = EEEPC_HOTK_NAME,
216 .class = EEEPC_HOTK_CLASS,
Alan Jenkinseacec302009-12-03 07:44:55 +0000217 .owner = THIS_MODULE,
Eric Coopere59f8792008-03-13 12:55:46 +0100218 .ids = eeepc_device_ids,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600219 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Eric Coopere59f8792008-03-13 12:55:46 +0100220 .ops = {
221 .add = eeepc_hotk_add,
222 .remove = eeepc_hotk_remove,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600223 .notify = eeepc_hotk_notify,
Eric Coopere59f8792008-03-13 12:55:46 +0100224 },
225};
226
Corentin Chary2b121bc2009-06-25 13:25:36 +0200227/* PCI hotplug ops */
228static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
229
230static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
231 .owner = THIS_MODULE,
232 .get_adapter_status = eeepc_get_adapter_status,
233 .get_power_status = eeepc_get_adapter_status,
234};
235
Corentin Charya5fa4292008-03-13 12:56:37 +0100236/* The backlight device /sys/class/backlight */
237static struct backlight_device *eeepc_backlight_device;
238
Corentin Charye1faa9d2008-03-13 12:57:18 +0100239/* The hwmon device */
240static struct device *eeepc_hwmon_device;
241
Corentin Charya5fa4292008-03-13 12:56:37 +0100242/*
243 * The backlight class declaration
244 */
245static int read_brightness(struct backlight_device *bd);
246static int update_bl_status(struct backlight_device *bd);
247static struct backlight_ops eeepcbl_ops = {
248 .get_brightness = read_brightness,
249 .update_status = update_bl_status,
250};
251
Eric Coopere59f8792008-03-13 12:55:46 +0100252MODULE_AUTHOR("Corentin Chary, Eric Cooper");
253MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
254MODULE_LICENSE("GPL");
255
256/*
257 * ACPI Helpers
258 */
259static int write_acpi_int(acpi_handle handle, const char *method, int val,
260 struct acpi_buffer *output)
261{
262 struct acpi_object_list params;
263 union acpi_object in_obj;
264 acpi_status status;
265
266 params.count = 1;
267 params.pointer = &in_obj;
268 in_obj.type = ACPI_TYPE_INTEGER;
269 in_obj.integer.value = val;
270
271 status = acpi_evaluate_object(handle, (char *)method, &params, output);
272 return (status == AE_OK ? 0 : -1);
273}
274
275static int read_acpi_int(acpi_handle handle, const char *method, int *val)
276{
277 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400278 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100279
280 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
281 if (ACPI_FAILURE(status)) {
282 *val = -1;
283 return -1;
284 } else {
285 *val = result;
286 return 0;
287 }
288}
289
290static int set_acpi(int cm, int value)
291{
Alan Jenkins13f70022009-12-03 07:44:59 +0000292 const char *method = cm_setv[cm];
293
294 if (method == NULL)
295 return -ENODEV;
296 if ((ehotk->cm_supported & (0x1 << cm)) == 0)
297 return -ENODEV;
298
299 if (write_acpi_int(ehotk->handle, method, value, NULL))
300 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100301 return 0;
302}
303
304static int get_acpi(int cm)
305{
Alan Jenkins13f70022009-12-03 07:44:59 +0000306 const char *method = cm_getv[cm];
307 int value;
308
309 if (method == NULL)
310 return -ENODEV;
311 if ((ehotk->cm_supported & (0x1 << cm)) == 0)
312 return -ENODEV;
313
314 if (read_acpi_int(ehotk->handle, method, &value))
315 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100316 return value;
317}
318
319/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100320 * Backlight
321 */
322static int read_brightness(struct backlight_device *bd)
323{
324 return get_acpi(CM_ASL_PANELBRIGHT);
325}
326
327static int set_brightness(struct backlight_device *bd, int value)
328{
329 value = max(0, min(15, value));
330 return set_acpi(CM_ASL_PANELBRIGHT, value);
331}
332
333static int update_bl_status(struct backlight_device *bd)
334{
335 return set_brightness(bd, bd->props.brightness);
336}
337
338/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100339 * Rfkill helpers
340 */
341
Johannes Berg19d337d2009-06-02 13:01:37 +0200342static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100343{
344 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200345 return false;
346 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100347}
348
Johannes Berg19d337d2009-06-02 13:01:37 +0200349static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100350{
Johannes Berg19d337d2009-06-02 13:01:37 +0200351 unsigned long asl = (unsigned long)data;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200352 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100353}
354
Johannes Berg19d337d2009-06-02 13:01:37 +0200355static const struct rfkill_ops eeepc_rfkill_ops = {
356 .set_block = eeepc_rfkill_set,
357};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100358
Rakib Mullickdcb73ee2009-10-13 00:13:32 +0200359static void __devinit eeepc_enable_camera(void)
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000360{
361 /*
362 * If the following call to set_acpi() fails, it's because there's no
363 * camera so we can ignore the error.
364 */
Luca Niccoli80f0c892009-10-16 22:22:47 +0200365 if (get_acpi(CM_ASL_CAMERA) == 0)
366 set_acpi(CM_ASL_CAMERA, 1);
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000367}
368
Matthew Garretta195dcd2008-08-19 12:13:20 +0100369/*
Eric Coopere59f8792008-03-13 12:55:46 +0100370 * Sys helpers
371 */
372static int parse_arg(const char *buf, unsigned long count, int *val)
373{
374 if (!count)
375 return 0;
376 if (sscanf(buf, "%i", val) != 1)
377 return -EINVAL;
378 return count;
379}
380
381static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
382{
383 int rv, value;
384
385 rv = parse_arg(buf, count, &value);
386 if (rv > 0)
Corentin Charyf36509e2009-06-25 13:25:40 +0200387 value = set_acpi(cm, value);
388 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000389 return -EIO;
Eric Coopere59f8792008-03-13 12:55:46 +0100390 return rv;
391}
392
393static ssize_t show_sys_acpi(int cm, char *buf)
394{
Corentin Charyf36509e2009-06-25 13:25:40 +0200395 int value = get_acpi(cm);
396
397 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000398 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200399 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100400}
401
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000402#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Eric Coopere59f8792008-03-13 12:55:46 +0100403 static ssize_t show_##_name(struct device *dev, \
404 struct device_attribute *attr, \
405 char *buf) \
406 { \
407 return show_sys_acpi(_cm, buf); \
408 } \
409 static ssize_t store_##_name(struct device *dev, \
410 struct device_attribute *attr, \
411 const char *buf, size_t count) \
412 { \
413 return store_sys_acpi(_cm, buf, count); \
414 } \
415 static struct device_attribute dev_attr_##_name = { \
416 .attr = { \
417 .name = __stringify(_name), \
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000418 .mode = _mode }, \
Eric Coopere59f8792008-03-13 12:55:46 +0100419 .show = show_##_name, \
420 .store = store_##_name, \
421 }
422
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000423EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
424EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
425EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000426
427struct eeepc_cpufv {
428 int num;
429 int cur;
430};
431
432static int get_cpufv(struct eeepc_cpufv *c)
433{
434 c->cur = get_acpi(CM_ASL_CPUFV);
435 c->num = (c->cur >> 8) & 0xff;
436 c->cur &= 0xff;
437 if (c->cur < 0 || c->num <= 0 || c->num > 12)
438 return -ENODEV;
439 return 0;
440}
441
442static ssize_t show_available_cpufv(struct device *dev,
443 struct device_attribute *attr,
444 char *buf)
445{
446 struct eeepc_cpufv c;
447 int i;
448 ssize_t len = 0;
449
450 if (get_cpufv(&c))
451 return -ENODEV;
452 for (i = 0; i < c.num; i++)
453 len += sprintf(buf + len, "%d ", i);
454 len += sprintf(buf + len, "\n");
455 return len;
456}
457
458static ssize_t show_cpufv(struct device *dev,
459 struct device_attribute *attr,
460 char *buf)
461{
462 struct eeepc_cpufv c;
463
464 if (get_cpufv(&c))
465 return -ENODEV;
466 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
467}
468
469static ssize_t store_cpufv(struct device *dev,
470 struct device_attribute *attr,
471 const char *buf, size_t count)
472{
473 struct eeepc_cpufv c;
474 int rv, value;
475
476 if (get_cpufv(&c))
477 return -ENODEV;
478 rv = parse_arg(buf, count, &value);
479 if (rv < 0)
480 return rv;
481 if (!rv || value < 0 || value >= c.num)
482 return -EINVAL;
483 set_acpi(CM_ASL_CPUFV, value);
484 return rv;
485}
486
487static struct device_attribute dev_attr_cpufv = {
488 .attr = {
489 .name = "cpufv",
490 .mode = 0644 },
491 .show = show_cpufv,
492 .store = store_cpufv
493};
494
495static struct device_attribute dev_attr_available_cpufv = {
496 .attr = {
497 .name = "available_cpufv",
498 .mode = 0444 },
499 .show = show_available_cpufv
500};
Eric Coopere59f8792008-03-13 12:55:46 +0100501
502static struct attribute *platform_attributes[] = {
503 &dev_attr_camera.attr,
504 &dev_attr_cardr.attr,
505 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200506 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000507 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100508 NULL
509};
510
511static struct attribute_group platform_attribute_group = {
512 .attrs = platform_attributes
513};
514
515/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000516 * LEDs
517 */
518/*
519 * These functions actually update the LED's, and are called from a
520 * workqueue. By doing this as separate work rather than when the LED
521 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
522 * potentially bad time, such as a timer interrupt.
523 */
524static int tpd_led_wk;
525
526static void tpd_led_update(struct work_struct *ignored)
527{
528 int value = tpd_led_wk;
529 set_acpi(CM_ASL_TPD, value);
530}
531
532static struct workqueue_struct *led_workqueue;
533static DECLARE_WORK(tpd_led_work, tpd_led_update);
534
535static void tpd_led_set(struct led_classdev *led_cdev,
536 enum led_brightness value)
537{
538 tpd_led_wk = (value > 0) ? 1 : 0;
539 queue_work(led_workqueue, &tpd_led_work);
540}
541
542static struct led_classdev tpd_led = {
543 .name = "eeepc::touchpad",
544 .brightness_set = tpd_led_set,
545 .max_brightness = 1
546};
547
548/*
Eric Coopere59f8792008-03-13 12:55:46 +0100549 * Hotkey functions
550 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100551static struct key_entry *eepc_get_entry_by_scancode(int code)
552{
553 struct key_entry *key;
554
555 for (key = eeepc_keymap; key->type != KE_END; key++)
556 if (code == key->code)
557 return key;
558
559 return NULL;
560}
561
562static struct key_entry *eepc_get_entry_by_keycode(int code)
563{
564 struct key_entry *key;
565
566 for (key = eeepc_keymap; key->type != KE_END; key++)
567 if (code == key->keycode && key->type == KE_KEY)
568 return key;
569
570 return NULL;
571}
572
573static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
574{
575 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
576
577 if (key && key->type == KE_KEY) {
578 *keycode = key->keycode;
579 return 0;
580 }
581
582 return -EINVAL;
583}
584
585static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
586{
587 struct key_entry *key;
588 int old_keycode;
589
590 if (keycode < 0 || keycode > KEY_MAX)
591 return -EINVAL;
592
593 key = eepc_get_entry_by_scancode(scancode);
594 if (key && key->type == KE_KEY) {
595 old_keycode = key->keycode;
596 key->keycode = keycode;
597 set_bit(keycode, dev->keybit);
598 if (!eepc_get_entry_by_keycode(old_keycode))
599 clear_bit(old_keycode, dev->keybit);
600 return 0;
601 }
602
603 return -EINVAL;
604}
605
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200606static void cmsg_quirk(int cm, const char *name)
607{
608 int dummy;
609
610 /* Some BIOSes do not report cm although it is avaliable.
611 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
612 if (!(ehotk->cm_supported & (1 << cm))
613 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
614 pr_info("%s (%x) not reported by BIOS,"
615 " enabling anyway\n", name, 1 << cm);
616 ehotk->cm_supported |= 1 << cm;
617 }
618}
619
620static void cmsg_quirks(void)
621{
622 cmsg_quirk(CM_ASL_LID, "LID");
623 cmsg_quirk(CM_ASL_TYPE, "TYPE");
624 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
625 cmsg_quirk(CM_ASL_TPD, "TPD");
626}
627
Eric Coopere59f8792008-03-13 12:55:46 +0100628static int eeepc_hotk_check(void)
629{
630 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
631 int result;
632
633 result = acpi_bus_get_status(ehotk->device);
634 if (result)
635 return result;
636 if (ehotk->device->status.present) {
637 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
638 &buffer)) {
Joe Perches19b53282009-06-25 13:25:37 +0200639 pr_err("Hotkey initialization failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100640 return -ENODEV;
641 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200642 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
Eric Coopere59f8792008-03-13 12:55:46 +0100643 }
644 /* get control methods supported */
645 if (read_acpi_int(ehotk->handle, "CMSG"
646 , &ehotk->cm_supported)) {
Joe Perches19b53282009-06-25 13:25:37 +0200647 pr_err("Get control methods supported failed\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100648 return -ENODEV;
649 } else {
Corentin Charydbfa3ba2009-06-25 13:25:41 +0200650 cmsg_quirks();
Joe Perches19b53282009-06-25 13:25:37 +0200651 pr_info("Get control methods supported: 0x%x\n",
652 ehotk->cm_supported);
Eric Coopere59f8792008-03-13 12:55:46 +0100653 }
654 } else {
Joe Perches19b53282009-06-25 13:25:37 +0200655 pr_err("Hotkey device not present, aborting\n");
Eric Coopere59f8792008-03-13 12:55:46 +0100656 return -EINVAL;
657 }
658 return 0;
659}
660
Darren Salt64b86b62009-04-27 09:23:38 +0200661static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100662{
Darren Salt64b86b62009-04-27 09:23:38 +0200663 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100664 struct backlight_device *bd = eeepc_backlight_device;
Alan Jenkinsa2a1d362009-12-03 07:45:00 +0000665 int old = bd->props.brightness;
666
667 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
668
669 return old;
Corentin Charya5fa4292008-03-13 12:56:37 +0100670}
671
Corentin Chary2b121bc2009-06-25 13:25:36 +0200672static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
673 u8 *value)
674{
675 int val = get_acpi(CM_ASL_WLAN);
676
677 if (val == 1 || val == 0)
678 *value = val;
679 else
680 return -EINVAL;
681
682 return 0;
683}
684
Corentin Chary58ce48a2009-10-16 22:22:46 +0200685static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100686{
687 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000688 struct pci_bus *bus;
Corentin Chary58ce48a2009-10-16 22:22:46 +0200689 bool blocked = eeepc_wlan_rfkill_blocked();
Matthew Garrett57402942009-01-20 16:17:48 +0100690
Corentin Chary58ce48a2009-10-16 22:22:46 +0200691 if (ehotk->wlan_rfkill)
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000692 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000693
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000694 mutex_lock(&ehotk->hotplug_lock);
695
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000696 if (ehotk->hotplug_slot) {
697 bus = pci_find_bus(0, 1);
698 if (!bus) {
699 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000700 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100701 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000702
703 if (!blocked) {
704 dev = pci_get_slot(bus, 0);
705 if (dev) {
706 /* Device already present */
707 pci_dev_put(dev);
708 goto out_unlock;
709 }
710 dev = pci_scan_single_device(bus, 0);
711 if (dev) {
712 pci_bus_assign_resources(bus);
713 if (pci_bus_add_device(dev))
714 pr_err("Unable to hotplug wifi\n");
715 }
716 } else {
717 dev = pci_get_slot(bus, 0);
718 if (dev) {
719 pci_remove_bus_device(dev);
720 pci_dev_put(dev);
721 }
Matthew Garrett57402942009-01-20 16:17:48 +0100722 }
723 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000724
725out_unlock:
726 mutex_unlock(&ehotk->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100727}
728
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100729static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
730{
731 if (event != ACPI_NOTIFY_BUS_CHECK)
732 return;
733
Corentin Chary58ce48a2009-10-16 22:22:46 +0200734 eeepc_rfkill_hotplug();
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100735}
736
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600737static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100738{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100739 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100740 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200741 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100742
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600743 if (event > ACPI_MAX_SYS_NOTIFY)
744 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100745 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200746 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100747 count = ehotk->event_count[event % 128]++;
748 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100749 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
750 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100751 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100752 if (ehotk->inputdev) {
Alan Jenkinsa2a1d362009-12-03 07:45:00 +0000753 /* brightness-change events need special
754 * handling for conversion to key events
755 */
756 if (brn < 0)
757 brn = event;
758 else
759 brn += NOTIFY_BRN_MIN;
760 if (event < brn)
761 event = NOTIFY_BRN_MIN; /* brightness down */
762 else if (event > brn)
763 event = NOTIFY_BRN_MIN + 2; /* ... up */
764 else
765 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
766
Matthew Garretta195dcd2008-08-19 12:13:20 +0100767 key = eepc_get_entry_by_scancode(event);
768 if (key) {
769 switch (key->type) {
770 case KE_KEY:
771 input_report_key(ehotk->inputdev, key->keycode,
772 1);
773 input_sync(ehotk->inputdev);
774 input_report_key(ehotk->inputdev, key->keycode,
775 0);
776 input_sync(ehotk->inputdev);
777 break;
778 }
779 }
780 }
Eric Coopere59f8792008-03-13 12:55:46 +0100781}
782
Matthew Garrett57402942009-01-20 16:17:48 +0100783static int eeepc_register_rfkill_notifier(char *node)
784{
785 acpi_status status = AE_OK;
786 acpi_handle handle;
787
788 status = acpi_get_handle(NULL, node, &handle);
789
790 if (ACPI_SUCCESS(status)) {
791 status = acpi_install_notify_handler(handle,
792 ACPI_SYSTEM_NOTIFY,
793 eeepc_rfkill_notify,
794 NULL);
795 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200796 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100797 } else
798 return -ENODEV;
799
800 return 0;
801}
802
803static void eeepc_unregister_rfkill_notifier(char *node)
804{
805 acpi_status status = AE_OK;
806 acpi_handle handle;
807
808 status = acpi_get_handle(NULL, node, &handle);
809
810 if (ACPI_SUCCESS(status)) {
811 status = acpi_remove_notify_handler(handle,
812 ACPI_SYSTEM_NOTIFY,
813 eeepc_rfkill_notify);
814 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200815 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100816 node);
817 }
818}
819
Corentin Chary2b121bc2009-06-25 13:25:36 +0200820static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
821{
822 kfree(hotplug_slot->info);
823 kfree(hotplug_slot);
824}
825
826static int eeepc_setup_pci_hotplug(void)
827{
828 int ret = -ENOMEM;
829 struct pci_bus *bus = pci_find_bus(0, 1);
830
831 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200832 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200833 return -ENODEV;
834 }
835
836 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
837 if (!ehotk->hotplug_slot)
838 goto error_slot;
839
840 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
841 GFP_KERNEL);
842 if (!ehotk->hotplug_slot->info)
843 goto error_info;
844
845 ehotk->hotplug_slot->private = ehotk;
846 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
847 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
848 eeepc_get_adapter_status(ehotk->hotplug_slot,
849 &ehotk->hotplug_slot->info->adapter_status);
850
851 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
852 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200853 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200854 goto error_register;
855 }
856
857 return 0;
858
859error_register:
860 kfree(ehotk->hotplug_slot->info);
861error_info:
862 kfree(ehotk->hotplug_slot);
863 ehotk->hotplug_slot = NULL;
864error_slot:
865 return ret;
866}
867
Alan Jenkinsc200da52009-08-28 12:56:40 +0000868static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100869{
Corentin Chary7de39382009-06-25 13:25:38 +0200870 if (ehotk->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100871 bool wlan;
872
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000873 /*
874 * Work around bios bug - acpi _PTS turns off the wireless led
875 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000876 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100877 */
878 wlan = get_acpi(CM_ASL_WLAN);
879 set_acpi(CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100880 }
881
Alan Jenkinsc200da52009-08-28 12:56:40 +0000882 return 0;
883}
884
885static int eeepc_hotk_restore(struct device *device)
886{
887 /* Refresh both wlan rfkill state and pci hotplug */
888 if (ehotk->wlan_rfkill)
Corentin Chary58ce48a2009-10-16 22:22:46 +0200889 eeepc_rfkill_hotplug();
Alan Jenkinsc200da52009-08-28 12:56:40 +0000890
Corentin Chary7de39382009-06-25 13:25:38 +0200891 if (ehotk->bluetooth_rfkill)
892 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100893 get_acpi(CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa4746102009-08-28 12:56:38 +0000894 if (ehotk->wwan3g_rfkill)
895 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
896 get_acpi(CM_ASL_3G) != 1);
Corentin Charyd1ec9c32009-08-28 12:56:41 +0000897 if (ehotk->wimax_rfkill)
898 rfkill_set_sw_state(ehotk->wimax_rfkill,
899 get_acpi(CM_ASL_WIMAX) != 1);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100900
901 return 0;
902}
903
Eric Coopere59f8792008-03-13 12:55:46 +0100904/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100905 * Hwmon
906 */
907static int eeepc_get_fan_pwm(void)
908{
909 int value = 0;
910
911 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200912 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100913 return (value);
914}
915
916static void eeepc_set_fan_pwm(int value)
917{
Corentin Chary04dcd842008-10-09 15:33:57 +0200918 value = SENSORS_LIMIT(value, 0, 255);
919 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100920 ec_write(EEEPC_EC_SC02, value);
921}
922
923static int eeepc_get_fan_rpm(void)
924{
925 int high = 0;
926 int low = 0;
927
928 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
929 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
930 return (high << 8 | low);
931}
932
933static int eeepc_get_fan_ctrl(void)
934{
935 int value = 0;
936
937 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000938 if (value & 0x02)
939 return 1; /* manual */
940 else
941 return 2; /* automatic */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100942}
943
944static void eeepc_set_fan_ctrl(int manual)
945{
946 int value = 0;
947
948 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000949 if (manual == 1)
Corentin Charye1faa9d2008-03-13 12:57:18 +0100950 value |= 0x02;
951 else
952 value &= ~0x02;
953 ec_write(EEEPC_EC_SFB3, value);
954}
955
956static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
957{
958 int rv, value;
959
960 rv = parse_arg(buf, count, &value);
961 if (rv > 0)
962 set(value);
963 return rv;
964}
965
966static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
967{
968 return sprintf(buf, "%d\n", get());
969}
970
971#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
972 static ssize_t show_##_name(struct device *dev, \
973 struct device_attribute *attr, \
974 char *buf) \
975 { \
976 return show_sys_hwmon(_set, buf); \
977 } \
978 static ssize_t store_##_name(struct device *dev, \
979 struct device_attribute *attr, \
980 const char *buf, size_t count) \
981 { \
982 return store_sys_hwmon(_get, buf, count); \
983 } \
984 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
985
986EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200987EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100988 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
989EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
990 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
991
Corentin Chary04dcd842008-10-09 15:33:57 +0200992static ssize_t
993show_name(struct device *dev, struct device_attribute *attr, char *buf)
994{
995 return sprintf(buf, "eeepc\n");
996}
997static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
998
Corentin Charye1faa9d2008-03-13 12:57:18 +0100999static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +02001000 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001001 &sensor_dev_attr_fan1_input.dev_attr.attr,
1002 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001003 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001004 NULL
1005};
1006
1007static struct attribute_group hwmon_attribute_group = {
1008 .attrs = hwmon_attributes
1009};
1010
1011/*
Eric Coopere59f8792008-03-13 12:55:46 +01001012 * exit/init
1013 */
Corentin Charya5fa4292008-03-13 12:56:37 +01001014static void eeepc_backlight_exit(void)
1015{
1016 if (eeepc_backlight_device)
1017 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +01001018 eeepc_backlight_device = NULL;
1019}
1020
1021static void eeepc_rfkill_exit(void)
1022{
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001023 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
Corentin Chary7de39382009-06-25 13:25:38 +02001024 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
1025 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001026 if (ehotk->wlan_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001027 rfkill_unregister(ehotk->wlan_rfkill);
Alan Jenkinsa8258062009-08-29 10:28:30 +02001028 rfkill_destroy(ehotk->wlan_rfkill);
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001029 ehotk->wlan_rfkill = NULL;
1030 }
1031 /*
1032 * Refresh pci hotplug in case the rfkill state was changed after
1033 * eeepc_unregister_rfkill_notifier()
1034 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001035 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001036 if (ehotk->hotplug_slot)
1037 pci_hp_deregister(ehotk->hotplug_slot);
1038
Alan Jenkinsa8258062009-08-29 10:28:30 +02001039 if (ehotk->bluetooth_rfkill) {
Corentin Chary7de39382009-06-25 13:25:38 +02001040 rfkill_unregister(ehotk->bluetooth_rfkill);
Alan Jenkinsa8258062009-08-29 10:28:30 +02001041 rfkill_destroy(ehotk->bluetooth_rfkill);
1042 ehotk->bluetooth_rfkill = NULL;
1043 }
1044 if (ehotk->wwan3g_rfkill) {
Corentin Chary3cd530b2009-06-25 13:25:42 +02001045 rfkill_unregister(ehotk->wwan3g_rfkill);
Alan Jenkinsa8258062009-08-29 10:28:30 +02001046 rfkill_destroy(ehotk->wwan3g_rfkill);
1047 ehotk->wwan3g_rfkill = NULL;
1048 }
1049 if (ehotk->wimax_rfkill) {
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001050 rfkill_unregister(ehotk->wimax_rfkill);
Alan Jenkinsa8258062009-08-29 10:28:30 +02001051 rfkill_destroy(ehotk->wimax_rfkill);
1052 ehotk->wimax_rfkill = NULL;
1053 }
Corentin Charya9df80c2009-01-20 16:17:40 +01001054}
1055
1056static void eeepc_input_exit(void)
1057{
1058 if (ehotk->inputdev)
1059 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +01001060}
1061
Corentin Charye1faa9d2008-03-13 12:57:18 +01001062static void eeepc_hwmon_exit(void)
1063{
1064 struct device *hwmon;
1065
1066 hwmon = eeepc_hwmon_device;
1067 if (!hwmon)
1068 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001069 sysfs_remove_group(&hwmon->kobj,
1070 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001071 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001072 eeepc_hwmon_device = NULL;
1073}
1074
Corentin Chary3c0eb512009-12-03 07:44:52 +00001075static void eeepc_led_exit(void)
1076{
Corentin Chary3c0eb512009-12-03 07:44:52 +00001077 if (tpd_led.dev)
1078 led_classdev_unregister(&tpd_led);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001079 if (led_workqueue)
1080 destroy_workqueue(led_workqueue);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001081}
1082
Corentin Chary7de39382009-06-25 13:25:38 +02001083static int eeepc_new_rfkill(struct rfkill **rfkill,
1084 const char *name, struct device *dev,
1085 enum rfkill_type type, int cm)
1086{
1087 int result;
1088
Corentin Charyf36509e2009-06-25 13:25:40 +02001089 result = get_acpi(cm);
1090 if (result < 0)
1091 return result;
Corentin Chary7de39382009-06-25 13:25:38 +02001092
1093 *rfkill = rfkill_alloc(name, dev, type,
1094 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1095
1096 if (!*rfkill)
1097 return -EINVAL;
1098
1099 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1100 result = rfkill_register(*rfkill);
1101 if (result) {
1102 rfkill_destroy(*rfkill);
1103 *rfkill = NULL;
1104 return result;
1105 }
1106 return 0;
1107}
1108
1109
1110static int eeepc_rfkill_init(struct device *dev)
1111{
1112 int result = 0;
1113
Alan Jenkinsdcf443b2009-08-28 12:56:33 +00001114 mutex_init(&ehotk->hotplug_lock);
Alan Jenkins73345462009-06-29 09:40:07 +01001115
Corentin Chary7de39382009-06-25 13:25:38 +02001116 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1117 "eeepc-wlan", dev,
1118 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1119
1120 if (result && result != -ENODEV)
1121 goto exit;
1122
1123 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1124 "eeepc-bluetooth", dev,
1125 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1126
1127 if (result && result != -ENODEV)
1128 goto exit;
1129
Corentin Chary3cd530b2009-06-25 13:25:42 +02001130 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1131 "eeepc-wwan3g", dev,
1132 RFKILL_TYPE_WWAN, CM_ASL_3G);
1133
1134 if (result && result != -ENODEV)
1135 goto exit;
1136
Corentin Charyd1ec9c32009-08-28 12:56:41 +00001137 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1138 "eeepc-wimax", dev,
1139 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1140
1141 if (result && result != -ENODEV)
1142 goto exit;
1143
Corentin Chary7de39382009-06-25 13:25:38 +02001144 result = eeepc_setup_pci_hotplug();
1145 /*
1146 * If we get -EBUSY then something else is handling the PCI hotplug -
1147 * don't fail in this case
1148 */
1149 if (result == -EBUSY)
1150 result = 0;
1151
Alan Jenkins52cc96b2009-08-29 10:28:31 +02001152 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001153 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1154 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1155 /*
1156 * Refresh pci hotplug in case the rfkill state was changed during
1157 * setup.
1158 */
Corentin Chary58ce48a2009-10-16 22:22:46 +02001159 eeepc_rfkill_hotplug();
Alan Jenkins07e84aa2009-08-28 12:56:34 +00001160
Corentin Chary7de39382009-06-25 13:25:38 +02001161exit:
1162 if (result && result != -ENODEV)
1163 eeepc_rfkill_exit();
1164 return result;
1165}
1166
Corentin Charya5fa4292008-03-13 12:56:37 +01001167static int eeepc_backlight_init(struct device *dev)
1168{
1169 struct backlight_device *bd;
1170
1171 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1172 NULL, &eeepcbl_ops);
1173 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001174 pr_err("Could not register eeepc backlight device\n");
Corentin Charya5fa4292008-03-13 12:56:37 +01001175 eeepc_backlight_device = NULL;
1176 return PTR_ERR(bd);
1177 }
1178 eeepc_backlight_device = bd;
1179 bd->props.max_brightness = 15;
1180 bd->props.brightness = read_brightness(NULL);
1181 bd->props.power = FB_BLANK_UNBLANK;
1182 backlight_update_status(bd);
1183 return 0;
1184}
1185
Corentin Charye1faa9d2008-03-13 12:57:18 +01001186static int eeepc_hwmon_init(struct device *dev)
1187{
1188 struct device *hwmon;
1189 int result;
1190
1191 hwmon = hwmon_device_register(dev);
1192 if (IS_ERR(hwmon)) {
Joe Perches19b53282009-06-25 13:25:37 +02001193 pr_err("Could not register eeepc hwmon device\n");
Corentin Charye1faa9d2008-03-13 12:57:18 +01001194 eeepc_hwmon_device = NULL;
1195 return PTR_ERR(hwmon);
1196 }
1197 eeepc_hwmon_device = hwmon;
1198 result = sysfs_create_group(&hwmon->kobj,
1199 &hwmon_attribute_group);
1200 if (result)
1201 eeepc_hwmon_exit();
1202 return result;
1203}
1204
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001205static int eeepc_input_init(struct device *dev)
1206{
1207 const struct key_entry *key;
1208 int result;
1209
1210 ehotk->inputdev = input_allocate_device();
1211 if (!ehotk->inputdev) {
1212 pr_info("Unable to allocate input device\n");
1213 return -ENOMEM;
1214 }
1215 ehotk->inputdev->name = "Asus EeePC extra buttons";
1216 ehotk->inputdev->dev.parent = dev;
1217 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1218 ehotk->inputdev->id.bustype = BUS_HOST;
1219 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1220 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1221
1222 for (key = eeepc_keymap; key->type != KE_END; key++) {
1223 switch (key->type) {
1224 case KE_KEY:
1225 set_bit(EV_KEY, ehotk->inputdev->evbit);
1226 set_bit(key->keycode, ehotk->inputdev->keybit);
1227 break;
1228 }
1229 }
1230 result = input_register_device(ehotk->inputdev);
1231 if (result) {
1232 pr_info("Unable to register input device\n");
1233 input_free_device(ehotk->inputdev);
1234 return result;
1235 }
1236 return 0;
1237}
1238
Corentin Chary3c0eb512009-12-03 07:44:52 +00001239static int eeepc_led_init(struct device *dev)
1240{
1241 int rv;
1242
1243 if (get_acpi(CM_ASL_TPD) == -ENODEV)
1244 return 0;
1245
Corentin Chary3c0eb512009-12-03 07:44:52 +00001246 led_workqueue = create_singlethread_workqueue("led_workqueue");
1247 if (!led_workqueue)
1248 return -ENOMEM;
1249
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001250 rv = led_classdev_register(dev, &tpd_led);
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001251 if (rv) {
1252 destroy_workqueue(led_workqueue);
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001253 return rv;
Alan Jenkinsdc56ad92009-12-03 07:44:58 +00001254 }
Alan Jenkins2b56f1c2009-12-03 07:44:57 +00001255
Corentin Chary3c0eb512009-12-03 07:44:52 +00001256 return 0;
1257}
1258
Rakib Mullickdcb73ee2009-10-13 00:13:32 +02001259static int __devinit eeepc_hotk_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001260{
1261 struct device *dev;
1262 int result;
1263
Alan Jenkins1e779852009-08-28 12:56:35 +00001264 pr_notice(EEEPC_HOTK_NAME "\n");
1265 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1266 if (!ehotk)
1267 return -ENOMEM;
1268 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1269 ehotk->handle = device->handle;
1270 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1271 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1272 device->driver_data = ehotk;
1273 ehotk->device = device;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001274
Alan Jenkins1e779852009-08-28 12:56:35 +00001275 result = eeepc_hotk_check();
1276 if (result)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001277 goto fail_platform_driver;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001278 eeepc_enable_camera();
1279
Eric Coopere59f8792008-03-13 12:55:46 +01001280 /* Register platform stuff */
1281 result = platform_driver_register(&platform_driver);
1282 if (result)
1283 goto fail_platform_driver;
1284 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1285 if (!platform_device) {
1286 result = -ENOMEM;
1287 goto fail_platform_device1;
1288 }
1289 result = platform_device_add(platform_device);
1290 if (result)
1291 goto fail_platform_device2;
1292 result = sysfs_create_group(&platform_device->dev.kobj,
1293 &platform_attribute_group);
1294 if (result)
1295 goto fail_sysfs;
Corentin Chary7de39382009-06-25 13:25:38 +02001296
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001297 dev = &platform_device->dev;
1298
1299 if (!acpi_video_backlight_support()) {
1300 result = eeepc_backlight_init(dev);
1301 if (result)
1302 goto fail_backlight;
1303 } else
1304 pr_info("Backlight controlled by ACPI video "
1305 "driver\n");
1306
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001307 result = eeepc_input_init(dev);
1308 if (result)
1309 goto fail_input;
1310
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001311 result = eeepc_hwmon_init(dev);
1312 if (result)
1313 goto fail_hwmon;
1314
Corentin Chary3c0eb512009-12-03 07:44:52 +00001315 result = eeepc_led_init(dev);
1316 if (result)
1317 goto fail_led;
1318
Corentin Chary7de39382009-06-25 13:25:38 +02001319 result = eeepc_rfkill_init(dev);
1320 if (result)
1321 goto fail_rfkill;
1322
Eric Coopere59f8792008-03-13 12:55:46 +01001323 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001324
Corentin Chary7de39382009-06-25 13:25:38 +02001325fail_rfkill:
Corentin Chary3c0eb512009-12-03 07:44:52 +00001326 eeepc_led_exit();
1327fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001328 eeepc_hwmon_exit();
1329fail_hwmon:
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001330 eeepc_input_exit();
1331fail_input:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001332 eeepc_backlight_exit();
1333fail_backlight:
Corentin Chary7de39382009-06-25 13:25:38 +02001334 sysfs_remove_group(&platform_device->dev.kobj,
1335 &platform_attribute_group);
Eric Coopere59f8792008-03-13 12:55:46 +01001336fail_sysfs:
1337 platform_device_del(platform_device);
1338fail_platform_device2:
1339 platform_device_put(platform_device);
1340fail_platform_device1:
1341 platform_driver_unregister(&platform_driver);
1342fail_platform_driver:
Alan Jenkins1e779852009-08-28 12:56:35 +00001343 kfree(ehotk);
1344
Eric Coopere59f8792008-03-13 12:55:46 +01001345 return result;
1346}
1347
Alan Jenkins1e779852009-08-28 12:56:35 +00001348static int eeepc_hotk_remove(struct acpi_device *device, int type)
1349{
Alan Jenkins1e779852009-08-28 12:56:35 +00001350 eeepc_backlight_exit();
1351 eeepc_rfkill_exit();
1352 eeepc_input_exit();
1353 eeepc_hwmon_exit();
Corentin Chary3c0eb512009-12-03 07:44:52 +00001354 eeepc_led_exit();
Alan Jenkins1e779852009-08-28 12:56:35 +00001355 sysfs_remove_group(&platform_device->dev.kobj,
1356 &platform_attribute_group);
1357 platform_device_unregister(platform_device);
1358 platform_driver_unregister(&platform_driver);
1359
1360 kfree(ehotk);
1361 return 0;
1362}
1363
1364static int __init eeepc_laptop_init(void)
1365{
1366 int result;
1367
Alan Jenkins1e779852009-08-28 12:56:35 +00001368 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1369 if (result < 0)
1370 return result;
1371 if (!ehotk) {
1372 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1373 return -ENODEV;
1374 }
1375 return 0;
1376}
1377
1378static void __exit eeepc_laptop_exit(void)
1379{
1380 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1381}
1382
Eric Coopere59f8792008-03-13 12:55:46 +01001383module_init(eeepc_laptop_init);
1384module_exit(eeepc_laptop_exit);