blob: 07d7978c558f6e64145b4029733c7e695d68e8c0 [file] [log] [blame]
Eric Coopere59f8792008-03-13 12:55:46 +01001/*
Alan Jenkinsa7624b62009-12-03 07:45:08 +00002 * eeepc-laptop.c - Asus Eee PC extras
Eric Coopere59f8792008-03-13 12:55:46 +01003 *
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>
Dmitry Torokhov642e0442010-01-06 22:07:39 +010034#include <linux/input/sparse-keymap.h>
Matthew Garretta195dcd2008-08-19 12:13:20 +010035#include <linux/rfkill.h>
Matthew Garrett57402942009-01-20 16:17:48 +010036#include <linux/pci.h>
Corentin Chary2b121bc2009-06-25 13:25:36 +020037#include <linux/pci_hotplug.h>
Corentin Chary3c0eb512009-12-03 07:44:52 +000038#include <linux/leds.h>
Alan Jenkinsda8ba012010-01-06 22:07:37 +010039#include <linux/dmi.h>
Eric Coopere59f8792008-03-13 12:55:46 +010040
41#define EEEPC_LAPTOP_VERSION "0.1"
Alan Jenkinsa7624b62009-12-03 07:45:08 +000042#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
43#define EEEPC_LAPTOP_FILE "eeepc"
Eric Coopere59f8792008-03-13 12:55:46 +010044
Alan Jenkinsa7624b62009-12-03 07:45:08 +000045#define EEEPC_ACPI_CLASS "hotkey"
46#define EEEPC_ACPI_DEVICE_NAME "Hotkey"
47#define EEEPC_ACPI_HID "ASUS010"
Eric Coopere59f8792008-03-13 12:55:46 +010048
Alan Jenkins52bbe3c2009-12-03 07:45:07 +000049MODULE_AUTHOR("Corentin Chary, Eric Cooper");
Alan Jenkinsa7624b62009-12-03 07:45:08 +000050MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +000051MODULE_LICENSE("GPL");
Eric Coopere59f8792008-03-13 12:55:46 +010052
53/*
54 * Definitions for Asus EeePC
55 */
Corentin Charya5fa4292008-03-13 12:56:37 +010056#define NOTIFY_BRN_MIN 0x20
57#define NOTIFY_BRN_MAX 0x2f
Eric Coopere59f8792008-03-13 12:55:46 +010058
59enum {
60 DISABLE_ASL_WLAN = 0x0001,
61 DISABLE_ASL_BLUETOOTH = 0x0002,
62 DISABLE_ASL_IRDA = 0x0004,
63 DISABLE_ASL_CAMERA = 0x0008,
64 DISABLE_ASL_TV = 0x0010,
65 DISABLE_ASL_GPS = 0x0020,
66 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
67 DISABLE_ASL_MODEM = 0x0080,
Corentin Charyb7b700d2009-06-16 19:28:52 +000068 DISABLE_ASL_CARDREADER = 0x0100,
69 DISABLE_ASL_3G = 0x0200,
70 DISABLE_ASL_WIMAX = 0x0400,
71 DISABLE_ASL_HWCF = 0x0800
Eric Coopere59f8792008-03-13 12:55:46 +010072};
73
74enum {
75 CM_ASL_WLAN = 0,
76 CM_ASL_BLUETOOTH,
77 CM_ASL_IRDA,
78 CM_ASL_1394,
79 CM_ASL_CAMERA,
80 CM_ASL_TV,
81 CM_ASL_GPS,
82 CM_ASL_DVDROM,
83 CM_ASL_DISPLAYSWITCH,
84 CM_ASL_PANELBRIGHT,
85 CM_ASL_BIOSFLASH,
86 CM_ASL_ACPIFLASH,
87 CM_ASL_CPUFV,
88 CM_ASL_CPUTEMPERATURE,
89 CM_ASL_FANCPU,
90 CM_ASL_FANCHASSIS,
91 CM_ASL_USBPORT1,
92 CM_ASL_USBPORT2,
93 CM_ASL_USBPORT3,
94 CM_ASL_MODEM,
95 CM_ASL_CARDREADER,
Corentin Charyb7b700d2009-06-16 19:28:52 +000096 CM_ASL_3G,
97 CM_ASL_WIMAX,
98 CM_ASL_HWCF,
99 CM_ASL_LID,
100 CM_ASL_TYPE,
101 CM_ASL_PANELPOWER, /*P901*/
102 CM_ASL_TPD
Eric Coopere59f8792008-03-13 12:55:46 +0100103};
104
Adrian Bunk14109462008-06-25 19:25:47 +0300105static const char *cm_getv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000106 "WLDG", "BTHG", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100107 "CAMG", NULL, NULL, NULL,
108 NULL, "PBLG", NULL, NULL,
109 "CFVG", NULL, NULL, NULL,
110 "USBG", NULL, NULL, "MODG",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000111 "CRDG", "M3GG", "WIMG", "HWCF",
112 "LIDG", "TYPE", "PBPG", "TPDG"
Eric Coopere59f8792008-03-13 12:55:46 +0100113};
114
Adrian Bunk14109462008-06-25 19:25:47 +0300115static const char *cm_setv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000116 "WLDS", "BTHS", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100117 "CAMS", NULL, NULL, NULL,
118 "SDSP", "PBLS", "HDPS", NULL,
119 "CFVS", NULL, NULL, NULL,
120 "USBG", NULL, NULL, "MODS",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000121 "CRDS", "M3GS", "WIMS", NULL,
122 NULL, NULL, "PBPS", "TPDS"
Eric Coopere59f8792008-03-13 12:55:46 +0100123};
124
Alan Jenkins854c7832009-12-03 07:45:09 +0000125static const struct key_entry eeepc_keymap[] = {
Dmitry Torokhov642e0442010-01-06 22:07:39 +0100126 { KE_KEY, 0x10, { KEY_WLAN } },
127 { KE_KEY, 0x11, { KEY_WLAN } },
128 { KE_KEY, 0x12, { KEY_PROG1 } },
129 { KE_KEY, 0x13, { KEY_MUTE } },
130 { KE_KEY, 0x14, { KEY_VOLUMEDOWN } },
131 { KE_KEY, 0x15, { KEY_VOLUMEUP } },
132 { KE_KEY, 0x16, { KEY_DISPLAY_OFF } },
133 { KE_KEY, 0x1a, { KEY_COFFEE } },
134 { KE_KEY, 0x1b, { KEY_ZOOM } },
135 { KE_KEY, 0x1c, { KEY_PROG2 } },
136 { KE_KEY, 0x1d, { KEY_PROG3 } },
137 { KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } },
138 { KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } },
139 { KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } },
140 { KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } },
141 { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } },
142 { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */
143 { KE_KEY, 0x38, { KEY_F14 } },
144 { KE_END, 0 },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100145};
146
Corentin Charya5fa4292008-03-13 12:56:37 +0100147/*
Eric Coopere59f8792008-03-13 12:55:46 +0100148 * This is the main structure, we can use it to store useful information
Corentin Charya5fa4292008-03-13 12:56:37 +0100149 */
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000150struct eeepc_laptop {
Alan Jenkins854c7832009-12-03 07:45:09 +0000151 acpi_handle handle; /* the handle of the acpi device */
Eric Coopere59f8792008-03-13 12:55:46 +0100152 u32 cm_supported; /* the control methods supported
153 by this BIOS */
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100154 bool cpufv_disabled;
Corentin Chary10ae4b52010-01-06 22:07:38 +0100155 bool hotplug_disabled;
Eric Coopere59f8792008-03-13 12:55:46 +0100156 u16 event_count[128]; /* count for each event */
Corentin Charya5fa4292008-03-13 12:56:37 +0100157
Alan Jenkins854c7832009-12-03 07:45:09 +0000158 struct platform_device *platform_device;
159 struct device *hwmon_device;
160 struct backlight_device *backlight_device;
161
Eric Coopere59f8792008-03-13 12:55:46 +0100162 struct input_dev *inputdev;
Alan Jenkins854c7832009-12-03 07:45:09 +0000163 struct key_entry *keymap;
164
Eric Coopere59f8792008-03-13 12:55:46 +0100165 struct rfkill *wlan_rfkill;
166 struct rfkill *bluetooth_rfkill;
167 struct rfkill *wwan3g_rfkill;
168 struct rfkill *wimax_rfkill;
Alan Jenkins854c7832009-12-03 07:45:09 +0000169
Eric Coopere59f8792008-03-13 12:55:46 +0100170 struct hotplug_slot *hotplug_slot;
171 struct mutex hotplug_lock;
Alan Jenkins854c7832009-12-03 07:45:09 +0000172
173 struct led_classdev tpd_led;
174 int tpd_led_wk;
175 struct workqueue_struct *led_workqueue;
176 struct work_struct tpd_led_work;
Eric Coopere59f8792008-03-13 12:55:46 +0100177};
Eric Coopere59f8792008-03-13 12:55:46 +0100178
179/*
180 * ACPI Helpers
181 */
Alan Jenkins6b188a72009-12-03 07:45:02 +0000182static int write_acpi_int(acpi_handle handle, const char *method, int val)
Eric Coopere59f8792008-03-13 12:55:46 +0100183{
184 struct acpi_object_list params;
185 union acpi_object in_obj;
186 acpi_status status;
187
188 params.count = 1;
189 params.pointer = &in_obj;
190 in_obj.type = ACPI_TYPE_INTEGER;
191 in_obj.integer.value = val;
192
Alan Jenkins6b188a72009-12-03 07:45:02 +0000193 status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
Eric Coopere59f8792008-03-13 12:55:46 +0100194 return (status == AE_OK ? 0 : -1);
195}
196
197static int read_acpi_int(acpi_handle handle, const char *method, int *val)
198{
199 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400200 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100201
202 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
203 if (ACPI_FAILURE(status)) {
204 *val = -1;
205 return -1;
206 } else {
207 *val = result;
208 return 0;
209 }
210}
211
Alan Jenkins854c7832009-12-03 07:45:09 +0000212static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value)
Eric Coopere59f8792008-03-13 12:55:46 +0100213{
Alan Jenkins13f70022009-12-03 07:44:59 +0000214 const char *method = cm_setv[cm];
215
216 if (method == NULL)
217 return -ENODEV;
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000218 if ((eeepc->cm_supported & (0x1 << cm)) == 0)
Alan Jenkins13f70022009-12-03 07:44:59 +0000219 return -ENODEV;
220
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000221 if (write_acpi_int(eeepc->handle, method, value))
Alan Jenkins13f70022009-12-03 07:44:59 +0000222 pr_warning("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100223 return 0;
224}
225
Alan Jenkins854c7832009-12-03 07:45:09 +0000226static int get_acpi(struct eeepc_laptop *eeepc, int cm)
Eric Coopere59f8792008-03-13 12:55:46 +0100227{
Alan Jenkins13f70022009-12-03 07:44:59 +0000228 const char *method = cm_getv[cm];
229 int value;
230
231 if (method == NULL)
232 return -ENODEV;
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000233 if ((eeepc->cm_supported & (0x1 << cm)) == 0)
Alan Jenkins13f70022009-12-03 07:44:59 +0000234 return -ENODEV;
235
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000236 if (read_acpi_int(eeepc->handle, method, &value))
Alan Jenkins13f70022009-12-03 07:44:59 +0000237 pr_warning("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100238 return value;
239}
240
Corentin Charyf90be872009-12-03 07:45:14 +0000241static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,
242 acpi_handle *handle)
Alan Jenkins854c7832009-12-03 07:45:09 +0000243{
244 const char *method = cm_setv[cm];
245 acpi_status status;
246
247 if (method == NULL)
248 return -ENODEV;
249 if ((eeepc->cm_supported & (0x1 << cm)) == 0)
250 return -ENODEV;
251
252 status = acpi_get_handle(eeepc->handle, (char *)method,
Corentin Charyf90be872009-12-03 07:45:14 +0000253 handle);
Alan Jenkins854c7832009-12-03 07:45:09 +0000254 if (status != AE_OK) {
255 pr_warning("Error finding %s\n", method);
256 return -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100257 }
258 return 0;
259}
260
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000261
Matthew Garretta195dcd2008-08-19 12:13:20 +0100262/*
Eric Coopere59f8792008-03-13 12:55:46 +0100263 * Sys helpers
264 */
265static int parse_arg(const char *buf, unsigned long count, int *val)
266{
267 if (!count)
268 return 0;
269 if (sscanf(buf, "%i", val) != 1)
270 return -EINVAL;
271 return count;
272}
273
Alan Jenkins854c7832009-12-03 07:45:09 +0000274static ssize_t store_sys_acpi(struct device *dev, int cm,
275 const char *buf, size_t count)
Eric Coopere59f8792008-03-13 12:55:46 +0100276{
Alan Jenkins854c7832009-12-03 07:45:09 +0000277 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Eric Coopere59f8792008-03-13 12:55:46 +0100278 int rv, value;
279
280 rv = parse_arg(buf, count, &value);
281 if (rv > 0)
Alan Jenkins854c7832009-12-03 07:45:09 +0000282 value = set_acpi(eeepc, cm, value);
Corentin Charyf36509e2009-06-25 13:25:40 +0200283 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000284 return -EIO;
Eric Coopere59f8792008-03-13 12:55:46 +0100285 return rv;
286}
287
Alan Jenkins854c7832009-12-03 07:45:09 +0000288static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
Eric Coopere59f8792008-03-13 12:55:46 +0100289{
Alan Jenkins854c7832009-12-03 07:45:09 +0000290 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
291 int value = get_acpi(eeepc, cm);
Corentin Charyf36509e2009-06-25 13:25:40 +0200292
293 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000294 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200295 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100296}
297
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000298#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Eric Coopere59f8792008-03-13 12:55:46 +0100299 static ssize_t show_##_name(struct device *dev, \
300 struct device_attribute *attr, \
301 char *buf) \
302 { \
Alan Jenkins854c7832009-12-03 07:45:09 +0000303 return show_sys_acpi(dev, _cm, buf); \
Eric Coopere59f8792008-03-13 12:55:46 +0100304 } \
305 static ssize_t store_##_name(struct device *dev, \
306 struct device_attribute *attr, \
307 const char *buf, size_t count) \
308 { \
Alan Jenkins854c7832009-12-03 07:45:09 +0000309 return store_sys_acpi(dev, _cm, buf, count); \
Eric Coopere59f8792008-03-13 12:55:46 +0100310 } \
311 static struct device_attribute dev_attr_##_name = { \
312 .attr = { \
313 .name = __stringify(_name), \
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000314 .mode = _mode }, \
Eric Coopere59f8792008-03-13 12:55:46 +0100315 .show = show_##_name, \
316 .store = store_##_name, \
317 }
318
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000319EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
320EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
321EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000322
323struct eeepc_cpufv {
324 int num;
325 int cur;
326};
327
Alan Jenkins854c7832009-12-03 07:45:09 +0000328static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000329{
Alan Jenkins854c7832009-12-03 07:45:09 +0000330 c->cur = get_acpi(eeepc, CM_ASL_CPUFV);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000331 c->num = (c->cur >> 8) & 0xff;
332 c->cur &= 0xff;
333 if (c->cur < 0 || c->num <= 0 || c->num > 12)
334 return -ENODEV;
335 return 0;
336}
337
338static ssize_t show_available_cpufv(struct device *dev,
339 struct device_attribute *attr,
340 char *buf)
341{
Alan Jenkins854c7832009-12-03 07:45:09 +0000342 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000343 struct eeepc_cpufv c;
344 int i;
345 ssize_t len = 0;
346
Alan Jenkins854c7832009-12-03 07:45:09 +0000347 if (get_cpufv(eeepc, &c))
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000348 return -ENODEV;
349 for (i = 0; i < c.num; i++)
350 len += sprintf(buf + len, "%d ", i);
351 len += sprintf(buf + len, "\n");
352 return len;
353}
354
355static ssize_t show_cpufv(struct device *dev,
356 struct device_attribute *attr,
357 char *buf)
358{
Alan Jenkins854c7832009-12-03 07:45:09 +0000359 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000360 struct eeepc_cpufv c;
361
Alan Jenkins854c7832009-12-03 07:45:09 +0000362 if (get_cpufv(eeepc, &c))
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000363 return -ENODEV;
364 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
365}
366
367static ssize_t store_cpufv(struct device *dev,
368 struct device_attribute *attr,
369 const char *buf, size_t count)
370{
Alan Jenkins854c7832009-12-03 07:45:09 +0000371 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000372 struct eeepc_cpufv c;
373 int rv, value;
374
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100375 if (eeepc->cpufv_disabled)
376 return -EPERM;
Alan Jenkins854c7832009-12-03 07:45:09 +0000377 if (get_cpufv(eeepc, &c))
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000378 return -ENODEV;
379 rv = parse_arg(buf, count, &value);
380 if (rv < 0)
381 return rv;
382 if (!rv || value < 0 || value >= c.num)
383 return -EINVAL;
Alan Jenkins854c7832009-12-03 07:45:09 +0000384 set_acpi(eeepc, CM_ASL_CPUFV, value);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000385 return rv;
386}
387
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100388static ssize_t show_cpufv_disabled(struct device *dev,
389 struct device_attribute *attr,
390 char *buf)
391{
392 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
393
394 return sprintf(buf, "%d\n", eeepc->cpufv_disabled);
395}
396
397static ssize_t store_cpufv_disabled(struct device *dev,
398 struct device_attribute *attr,
399 const char *buf, size_t count)
400{
401 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
402 int rv, value;
403
404 rv = parse_arg(buf, count, &value);
405 if (rv < 0)
406 return rv;
407
408 switch (value) {
409 case 0:
410 if (eeepc->cpufv_disabled)
411 pr_warning("cpufv enabled (not officially supported "
412 "on this model)\n");
413 eeepc->cpufv_disabled = false;
414 return rv;
415 case 1:
416 return -EPERM;
417 default:
418 return -EINVAL;
419 }
420}
421
422
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000423static struct device_attribute dev_attr_cpufv = {
424 .attr = {
425 .name = "cpufv",
426 .mode = 0644 },
427 .show = show_cpufv,
428 .store = store_cpufv
429};
430
431static struct device_attribute dev_attr_available_cpufv = {
432 .attr = {
433 .name = "available_cpufv",
434 .mode = 0444 },
435 .show = show_available_cpufv
436};
Eric Coopere59f8792008-03-13 12:55:46 +0100437
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100438static struct device_attribute dev_attr_cpufv_disabled = {
439 .attr = {
440 .name = "cpufv_disabled",
441 .mode = 0644 },
442 .show = show_cpufv_disabled,
443 .store = store_cpufv_disabled
444};
445
446
Eric Coopere59f8792008-03-13 12:55:46 +0100447static struct attribute *platform_attributes[] = {
448 &dev_attr_camera.attr,
449 &dev_attr_cardr.attr,
450 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200451 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000452 &dev_attr_available_cpufv.attr,
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100453 &dev_attr_cpufv_disabled.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100454 NULL
455};
456
457static struct attribute_group platform_attribute_group = {
458 .attrs = platform_attributes
459};
460
Alan Jenkins854c7832009-12-03 07:45:09 +0000461static int eeepc_platform_init(struct eeepc_laptop *eeepc)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100462{
Eric Coopere59f8792008-03-13 12:55:46 +0100463 int result;
464
Alan Jenkins854c7832009-12-03 07:45:09 +0000465 eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
466 if (!eeepc->platform_device)
Alan Jenkins9db106b2009-12-03 07:45:06 +0000467 return -ENOMEM;
Alan Jenkins854c7832009-12-03 07:45:09 +0000468 platform_set_drvdata(eeepc->platform_device, eeepc);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000469
Alan Jenkins854c7832009-12-03 07:45:09 +0000470 result = platform_device_add(eeepc->platform_device);
Eric Coopere59f8792008-03-13 12:55:46 +0100471 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +0000472 goto fail_platform_device;
473
Alan Jenkins854c7832009-12-03 07:45:09 +0000474 result = sysfs_create_group(&eeepc->platform_device->dev.kobj,
Alan Jenkins9db106b2009-12-03 07:45:06 +0000475 &platform_attribute_group);
476 if (result)
477 goto fail_sysfs;
Eric Coopere59f8792008-03-13 12:55:46 +0100478 return 0;
Alan Jenkins9db106b2009-12-03 07:45:06 +0000479
480fail_sysfs:
Alan Jenkins854c7832009-12-03 07:45:09 +0000481 platform_device_del(eeepc->platform_device);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000482fail_platform_device:
Alan Jenkins854c7832009-12-03 07:45:09 +0000483 platform_device_put(eeepc->platform_device);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000484 return result;
Eric Coopere59f8792008-03-13 12:55:46 +0100485}
486
Alan Jenkins854c7832009-12-03 07:45:09 +0000487static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
Corentin Charya5fa4292008-03-13 12:56:37 +0100488{
Alan Jenkins854c7832009-12-03 07:45:09 +0000489 sysfs_remove_group(&eeepc->platform_device->dev.kobj,
Alan Jenkins9db106b2009-12-03 07:45:06 +0000490 &platform_attribute_group);
Alan Jenkins854c7832009-12-03 07:45:09 +0000491 platform_device_unregister(eeepc->platform_device);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000492}
493
Eric Coopere59f8792008-03-13 12:55:46 +0100494/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000495 * LEDs
496 */
497/*
498 * These functions actually update the LED's, and are called from a
499 * workqueue. By doing this as separate work rather than when the LED
500 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
501 * potentially bad time, such as a timer interrupt.
502 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000503static void tpd_led_update(struct work_struct *work)
504 {
505 struct eeepc_laptop *eeepc;
Corentin Chary3c0eb512009-12-03 07:44:52 +0000506
Alan Jenkins854c7832009-12-03 07:45:09 +0000507 eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
508
509 set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);
Corentin Chary3c0eb512009-12-03 07:44:52 +0000510}
511
Corentin Chary3c0eb512009-12-03 07:44:52 +0000512static void tpd_led_set(struct led_classdev *led_cdev,
513 enum led_brightness value)
514{
Alan Jenkins854c7832009-12-03 07:45:09 +0000515 struct eeepc_laptop *eeepc;
516
517 eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
518
519 eeepc->tpd_led_wk = (value > 0) ? 1 : 0;
520 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
Corentin Chary3c0eb512009-12-03 07:44:52 +0000521}
522
Alan Jenkins854c7832009-12-03 07:45:09 +0000523static int eeepc_led_init(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000524{
525 int rv;
526
Alan Jenkins854c7832009-12-03 07:45:09 +0000527 if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000528 return 0;
529
Alan Jenkins854c7832009-12-03 07:45:09 +0000530 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
531 if (!eeepc->led_workqueue)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000532 return -ENOMEM;
Alan Jenkins854c7832009-12-03 07:45:09 +0000533 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000534
Alan Jenkins854c7832009-12-03 07:45:09 +0000535 eeepc->tpd_led.name = "eeepc::touchpad";
536 eeepc->tpd_led.brightness_set = tpd_led_set;
537 eeepc->tpd_led.max_brightness = 1;
538
539 rv = led_classdev_register(&eeepc->platform_device->dev,
540 &eeepc->tpd_led);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000541 if (rv) {
Alan Jenkins854c7832009-12-03 07:45:09 +0000542 destroy_workqueue(eeepc->led_workqueue);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000543 return rv;
Darren Salt64b86b62009-04-27 09:23:38 +0200544 }
Corentin Chary2b121bc2009-06-25 13:25:36 +0200545
546 return 0;
547}
548
Alan Jenkins854c7832009-12-03 07:45:09 +0000549static void eeepc_led_exit(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000550{
Alan Jenkins854c7832009-12-03 07:45:09 +0000551 if (eeepc->tpd_led.dev)
552 led_classdev_unregister(&eeepc->tpd_led);
553 if (eeepc->led_workqueue)
554 destroy_workqueue(eeepc->led_workqueue);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000555}
556
557
Corentin Chary3c0eb512009-12-03 07:44:52 +0000558/*
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000559 * PCI hotplug (for wlan rfkill)
Eric Coopere59f8792008-03-13 12:55:46 +0100560 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000561static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
Eric Coopere59f8792008-03-13 12:55:46 +0100562{
Alan Jenkins854c7832009-12-03 07:45:09 +0000563 if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000564 return false;
565 return true;
Corentin Charya5fa4292008-03-13 12:56:37 +0100566}
567
Alan Jenkins854c7832009-12-03 07:45:09 +0000568static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
Matthew Garrett57402942009-01-20 16:17:48 +0100569{
570 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000571 struct pci_bus *bus;
Alan Jenkins854c7832009-12-03 07:45:09 +0000572 bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
Matthew Garrett57402942009-01-20 16:17:48 +0100573
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000574 if (eeepc->wlan_rfkill)
575 rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000576
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000577 mutex_lock(&eeepc->hotplug_lock);
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000578
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000579 if (eeepc->hotplug_slot) {
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000580 bus = pci_find_bus(0, 1);
581 if (!bus) {
582 pr_warning("Unable to find PCI bus 1?\n");
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000583 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100584 }
Alan Jenkins07e84aa2009-08-28 12:56:34 +0000585
586 if (!blocked) {
587 dev = pci_get_slot(bus, 0);
588 if (dev) {
589 /* Device already present */
590 pci_dev_put(dev);
591 goto out_unlock;
592 }
593 dev = pci_scan_single_device(bus, 0);
594 if (dev) {
595 pci_bus_assign_resources(bus);
596 if (pci_bus_add_device(dev))
597 pr_err("Unable to hotplug wifi\n");
598 }
599 } else {
600 dev = pci_get_slot(bus, 0);
601 if (dev) {
602 pci_remove_bus_device(dev);
603 pci_dev_put(dev);
604 }
Matthew Garrett57402942009-01-20 16:17:48 +0100605 }
606 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000607
608out_unlock:
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000609 mutex_unlock(&eeepc->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100610}
611
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100612static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
613{
Alan Jenkins854c7832009-12-03 07:45:09 +0000614 struct eeepc_laptop *eeepc = data;
615
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100616 if (event != ACPI_NOTIFY_BUS_CHECK)
617 return;
618
Alan Jenkins854c7832009-12-03 07:45:09 +0000619 eeepc_rfkill_hotplug(eeepc);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100620}
621
Alan Jenkins854c7832009-12-03 07:45:09 +0000622static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
623 char *node)
Eric Coopere59f8792008-03-13 12:55:46 +0100624{
Alan Jenkins854c7832009-12-03 07:45:09 +0000625 acpi_status status;
Matthew Garrett57402942009-01-20 16:17:48 +0100626 acpi_handle handle;
627
628 status = acpi_get_handle(NULL, node, &handle);
629
630 if (ACPI_SUCCESS(status)) {
631 status = acpi_install_notify_handler(handle,
632 ACPI_SYSTEM_NOTIFY,
633 eeepc_rfkill_notify,
Alan Jenkins854c7832009-12-03 07:45:09 +0000634 eeepc);
Matthew Garrett57402942009-01-20 16:17:48 +0100635 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200636 pr_warning("Failed to register notify on %s\n", node);
Matthew Garrett57402942009-01-20 16:17:48 +0100637 } else
638 return -ENODEV;
639
640 return 0;
641}
642
Alan Jenkins854c7832009-12-03 07:45:09 +0000643static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
644 char *node)
Matthew Garrett57402942009-01-20 16:17:48 +0100645{
646 acpi_status status = AE_OK;
647 acpi_handle handle;
648
649 status = acpi_get_handle(NULL, node, &handle);
650
651 if (ACPI_SUCCESS(status)) {
652 status = acpi_remove_notify_handler(handle,
653 ACPI_SYSTEM_NOTIFY,
654 eeepc_rfkill_notify);
655 if (ACPI_FAILURE(status))
Joe Perches19b53282009-06-25 13:25:37 +0200656 pr_err("Error removing rfkill notify handler %s\n",
Matthew Garrett57402942009-01-20 16:17:48 +0100657 node);
658 }
659}
660
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000661static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
662 u8 *value)
663{
Alan Jenkins854c7832009-12-03 07:45:09 +0000664 struct eeepc_laptop *eeepc = hotplug_slot->private;
665 int val = get_acpi(eeepc, CM_ASL_WLAN);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000666
667 if (val == 1 || val == 0)
668 *value = val;
669 else
670 return -EINVAL;
671
672 return 0;
673}
674
Corentin Chary2b121bc2009-06-25 13:25:36 +0200675static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
676{
677 kfree(hotplug_slot->info);
678 kfree(hotplug_slot);
679}
680
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000681static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
682 .owner = THIS_MODULE,
683 .get_adapter_status = eeepc_get_adapter_status,
684 .get_power_status = eeepc_get_adapter_status,
685};
686
Alan Jenkins854c7832009-12-03 07:45:09 +0000687static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
Corentin Chary2b121bc2009-06-25 13:25:36 +0200688{
689 int ret = -ENOMEM;
690 struct pci_bus *bus = pci_find_bus(0, 1);
691
692 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200693 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200694 return -ENODEV;
695 }
696
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000697 eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
698 if (!eeepc->hotplug_slot)
Corentin Chary2b121bc2009-06-25 13:25:36 +0200699 goto error_slot;
700
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000701 eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
Corentin Chary2b121bc2009-06-25 13:25:36 +0200702 GFP_KERNEL);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000703 if (!eeepc->hotplug_slot->info)
Corentin Chary2b121bc2009-06-25 13:25:36 +0200704 goto error_info;
705
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000706 eeepc->hotplug_slot->private = eeepc;
707 eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
708 eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
709 eeepc_get_adapter_status(eeepc->hotplug_slot,
710 &eeepc->hotplug_slot->info->adapter_status);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200711
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000712 ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200713 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200714 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200715 goto error_register;
716 }
717
718 return 0;
719
720error_register:
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000721 kfree(eeepc->hotplug_slot->info);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200722error_info:
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000723 kfree(eeepc->hotplug_slot);
724 eeepc->hotplug_slot = NULL;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200725error_slot:
726 return ret;
727}
728
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000729/*
730 * Rfkill devices
731 */
732static int eeepc_rfkill_set(void *data, bool blocked)
733{
Alan Jenkins854c7832009-12-03 07:45:09 +0000734 acpi_handle handle = data;
735
736 return write_acpi_int(handle, NULL, !blocked);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000737}
738
739static const struct rfkill_ops eeepc_rfkill_ops = {
740 .set_block = eeepc_rfkill_set,
741};
742
Alan Jenkins854c7832009-12-03 07:45:09 +0000743static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
744 struct rfkill **rfkill,
745 const char *name,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000746 enum rfkill_type type, int cm)
747{
Alan Jenkins854c7832009-12-03 07:45:09 +0000748 acpi_handle handle;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000749 int result;
750
Alan Jenkins854c7832009-12-03 07:45:09 +0000751 result = acpi_setter_handle(eeepc, cm, &handle);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000752 if (result < 0)
753 return result;
754
Alan Jenkins854c7832009-12-03 07:45:09 +0000755 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
756 &eeepc_rfkill_ops, handle);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000757
758 if (!*rfkill)
759 return -EINVAL;
760
Alan Jenkins854c7832009-12-03 07:45:09 +0000761 rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000762 result = rfkill_register(*rfkill);
763 if (result) {
764 rfkill_destroy(*rfkill);
765 *rfkill = NULL;
766 return result;
767 }
768 return 0;
769}
770
Alan Jenkins854c7832009-12-03 07:45:09 +0000771static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000772{
Alan Jenkins854c7832009-12-03 07:45:09 +0000773 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
774 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
775 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000776 if (eeepc->wlan_rfkill) {
777 rfkill_unregister(eeepc->wlan_rfkill);
778 rfkill_destroy(eeepc->wlan_rfkill);
779 eeepc->wlan_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000780 }
781 /*
782 * Refresh pci hotplug in case the rfkill state was changed after
783 * eeepc_unregister_rfkill_notifier()
784 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000785 eeepc_rfkill_hotplug(eeepc);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000786 if (eeepc->hotplug_slot)
787 pci_hp_deregister(eeepc->hotplug_slot);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000788
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000789 if (eeepc->bluetooth_rfkill) {
790 rfkill_unregister(eeepc->bluetooth_rfkill);
791 rfkill_destroy(eeepc->bluetooth_rfkill);
792 eeepc->bluetooth_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000793 }
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000794 if (eeepc->wwan3g_rfkill) {
795 rfkill_unregister(eeepc->wwan3g_rfkill);
796 rfkill_destroy(eeepc->wwan3g_rfkill);
797 eeepc->wwan3g_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000798 }
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000799 if (eeepc->wimax_rfkill) {
800 rfkill_unregister(eeepc->wimax_rfkill);
801 rfkill_destroy(eeepc->wimax_rfkill);
802 eeepc->wimax_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000803 }
804}
805
Alan Jenkins854c7832009-12-03 07:45:09 +0000806static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000807{
808 int result = 0;
809
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000810 mutex_init(&eeepc->hotplug_lock);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000811
Alan Jenkins854c7832009-12-03 07:45:09 +0000812 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
813 "eeepc-wlan", RFKILL_TYPE_WLAN,
814 CM_ASL_WLAN);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000815
816 if (result && result != -ENODEV)
817 goto exit;
818
Alan Jenkins854c7832009-12-03 07:45:09 +0000819 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
820 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
821 CM_ASL_BLUETOOTH);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000822
823 if (result && result != -ENODEV)
824 goto exit;
825
Alan Jenkins854c7832009-12-03 07:45:09 +0000826 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
827 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
828 CM_ASL_3G);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000829
830 if (result && result != -ENODEV)
831 goto exit;
832
Alan Jenkins854c7832009-12-03 07:45:09 +0000833 result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
834 "eeepc-wimax", RFKILL_TYPE_WIMAX,
835 CM_ASL_WIMAX);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000836
837 if (result && result != -ENODEV)
838 goto exit;
839
Corentin Chary10ae4b52010-01-06 22:07:38 +0100840 if (eeepc->hotplug_disabled)
841 return 0;
842
Alan Jenkins854c7832009-12-03 07:45:09 +0000843 result = eeepc_setup_pci_hotplug(eeepc);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000844 /*
845 * If we get -EBUSY then something else is handling the PCI hotplug -
846 * don't fail in this case
847 */
848 if (result == -EBUSY)
849 result = 0;
850
Alan Jenkins854c7832009-12-03 07:45:09 +0000851 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
852 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
853 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000854 /*
855 * Refresh pci hotplug in case the rfkill state was changed during
856 * setup.
857 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000858 eeepc_rfkill_hotplug(eeepc);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000859
860exit:
861 if (result && result != -ENODEV)
Alan Jenkins854c7832009-12-03 07:45:09 +0000862 eeepc_rfkill_exit(eeepc);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000863 return result;
864}
865
866/*
867 * Platform driver - hibernate/resume callbacks
868 */
Alan Jenkinsc200da52009-08-28 12:56:40 +0000869static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100870{
Alan Jenkins854c7832009-12-03 07:45:09 +0000871 struct eeepc_laptop *eeepc = dev_get_drvdata(device);
872
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000873 if (eeepc->wlan_rfkill) {
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100874 bool wlan;
875
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000876 /*
877 * Work around bios bug - acpi _PTS turns off the wireless led
878 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000879 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100880 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000881 wlan = get_acpi(eeepc, CM_ASL_WLAN);
882 set_acpi(eeepc, CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100883 }
884
Alan Jenkinsc200da52009-08-28 12:56:40 +0000885 return 0;
886}
887
888static int eeepc_hotk_restore(struct device *device)
889{
Alan Jenkins854c7832009-12-03 07:45:09 +0000890 struct eeepc_laptop *eeepc = dev_get_drvdata(device);
Alan Jenkinsc200da52009-08-28 12:56:40 +0000891
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100892 /* Refresh both wlan rfkill state and pci hotplug */
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000893 if (eeepc->wlan_rfkill)
Alan Jenkins854c7832009-12-03 07:45:09 +0000894 eeepc_rfkill_hotplug(eeepc);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100895
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000896 if (eeepc->bluetooth_rfkill)
897 rfkill_set_sw_state(eeepc->bluetooth_rfkill,
Alan Jenkins854c7832009-12-03 07:45:09 +0000898 get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000899 if (eeepc->wwan3g_rfkill)
900 rfkill_set_sw_state(eeepc->wwan3g_rfkill,
Alan Jenkins854c7832009-12-03 07:45:09 +0000901 get_acpi(eeepc, CM_ASL_3G) != 1);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000902 if (eeepc->wimax_rfkill)
903 rfkill_set_sw_state(eeepc->wimax_rfkill,
Alan Jenkins854c7832009-12-03 07:45:09 +0000904 get_acpi(eeepc, CM_ASL_WIMAX) != 1);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100905
906 return 0;
907}
908
Len Brown9a3bff22009-12-15 22:34:48 -0500909static const struct dev_pm_ops eeepc_pm_ops = {
Alan Jenkins854c7832009-12-03 07:45:09 +0000910 .thaw = eeepc_hotk_thaw,
911 .restore = eeepc_hotk_restore,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000912};
913
914static struct platform_driver platform_driver = {
915 .driver = {
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000916 .name = EEEPC_LAPTOP_FILE,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000917 .owner = THIS_MODULE,
918 .pm = &eeepc_pm_ops,
919 }
920};
921
Eric Coopere59f8792008-03-13 12:55:46 +0100922/*
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000923 * Hwmon device
Eric Coopere59f8792008-03-13 12:55:46 +0100924 */
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000925
926#define EEEPC_EC_SC00 0x61
927#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
928#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
929#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
930
931#define EEEPC_EC_SFB0 0xD0
932#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */
933
Corentin Charye1faa9d2008-03-13 12:57:18 +0100934static int eeepc_get_fan_pwm(void)
935{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000936 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100937
Alan Jenkins463b4e42009-12-03 07:45:03 +0000938 ec_read(EEEPC_EC_FAN_PWM, &value);
939 return value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100940}
941
942static void eeepc_set_fan_pwm(int value)
943{
944 value = SENSORS_LIMIT(value, 0, 255);
945 value = value * 100 / 255;
Alan Jenkins463b4e42009-12-03 07:45:03 +0000946 ec_write(EEEPC_EC_FAN_PWM, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100947}
948
949static int eeepc_get_fan_rpm(void)
950{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000951 u8 high = 0;
952 u8 low = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100953
Alan Jenkins463b4e42009-12-03 07:45:03 +0000954 ec_read(EEEPC_EC_FAN_HRPM, &high);
955 ec_read(EEEPC_EC_FAN_LRPM, &low);
956 return high << 8 | low;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100957}
958
959static int eeepc_get_fan_ctrl(void)
960{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000961 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100962
Alan Jenkins463b4e42009-12-03 07:45:03 +0000963 ec_read(EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000964 if (value & 0x02)
965 return 1; /* manual */
966 else
967 return 2; /* automatic */
Corentin Charye1faa9d2008-03-13 12:57:18 +0100968}
969
970static void eeepc_set_fan_ctrl(int manual)
971{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000972 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100973
Alan Jenkins463b4e42009-12-03 07:45:03 +0000974 ec_read(EEEPC_EC_FAN_CTRL, &value);
Alan Jenkins48718682009-12-03 07:44:56 +0000975 if (manual == 1)
Corentin Charye1faa9d2008-03-13 12:57:18 +0100976 value |= 0x02;
977 else
978 value &= ~0x02;
Alan Jenkins463b4e42009-12-03 07:45:03 +0000979 ec_write(EEEPC_EC_FAN_CTRL, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100980}
981
982static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
983{
984 int rv, value;
985
986 rv = parse_arg(buf, count, &value);
987 if (rv > 0)
988 set(value);
989 return rv;
990}
991
992static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
993{
994 return sprintf(buf, "%d\n", get());
995}
996
997#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
998 static ssize_t show_##_name(struct device *dev, \
999 struct device_attribute *attr, \
1000 char *buf) \
1001 { \
1002 return show_sys_hwmon(_set, buf); \
1003 } \
1004 static ssize_t store_##_name(struct device *dev, \
1005 struct device_attribute *attr, \
1006 const char *buf, size_t count) \
1007 { \
1008 return store_sys_hwmon(_get, buf, count); \
1009 } \
1010 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
1011
1012EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +02001013EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001014 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
1015EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
1016 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
1017
Corentin Chary04dcd842008-10-09 15:33:57 +02001018static ssize_t
1019show_name(struct device *dev, struct device_attribute *attr, char *buf)
1020{
1021 return sprintf(buf, "eeepc\n");
1022}
1023static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
1024
Corentin Charye1faa9d2008-03-13 12:57:18 +01001025static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +02001026 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001027 &sensor_dev_attr_fan1_input.dev_attr.attr,
1028 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +02001029 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001030 NULL
1031};
1032
1033static struct attribute_group hwmon_attribute_group = {
1034 .attrs = hwmon_attributes
1035};
1036
Alan Jenkins854c7832009-12-03 07:45:09 +00001037static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc)
Corentin Charye1faa9d2008-03-13 12:57:18 +01001038{
1039 struct device *hwmon;
1040
Alan Jenkins854c7832009-12-03 07:45:09 +00001041 hwmon = eeepc->hwmon_device;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001042 if (!hwmon)
Alan Jenkins854c7832009-12-03 07:45:09 +00001043 return;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001044 sysfs_remove_group(&hwmon->kobj,
1045 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -07001046 hwmon_device_unregister(hwmon);
Alan Jenkins854c7832009-12-03 07:45:09 +00001047 eeepc->hwmon_device = NULL;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001048}
1049
Alan Jenkins854c7832009-12-03 07:45:09 +00001050static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
Corentin Chary7de39382009-06-25 13:25:38 +02001051{
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001052 struct device *hwmon;
Corentin Chary7de39382009-06-25 13:25:38 +02001053 int result;
1054
Alan Jenkins854c7832009-12-03 07:45:09 +00001055 hwmon = hwmon_device_register(&eeepc->platform_device->dev);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001056 if (IS_ERR(hwmon)) {
1057 pr_err("Could not register eeepc hwmon device\n");
Alan Jenkins854c7832009-12-03 07:45:09 +00001058 eeepc->hwmon_device = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001059 return PTR_ERR(hwmon);
Corentin Chary7de39382009-06-25 13:25:38 +02001060 }
Alan Jenkins854c7832009-12-03 07:45:09 +00001061 eeepc->hwmon_device = hwmon;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001062 result = sysfs_create_group(&hwmon->kobj,
1063 &hwmon_attribute_group);
1064 if (result)
Alan Jenkins854c7832009-12-03 07:45:09 +00001065 eeepc_hwmon_exit(eeepc);
Corentin Chary7de39382009-06-25 13:25:38 +02001066 return result;
1067}
1068
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001069/*
1070 * Backlight device
1071 */
1072static int read_brightness(struct backlight_device *bd)
Corentin Charya5fa4292008-03-13 12:56:37 +01001073{
Alan Jenkins854c7832009-12-03 07:45:09 +00001074 struct eeepc_laptop *eeepc = bl_get_data(bd);
1075
1076 return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001077}
Corentin Charya5fa4292008-03-13 12:56:37 +01001078
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001079static int set_brightness(struct backlight_device *bd, int value)
1080{
Alan Jenkins854c7832009-12-03 07:45:09 +00001081 struct eeepc_laptop *eeepc = bl_get_data(bd);
1082
1083 return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001084}
Corentin Charya5fa4292008-03-13 12:56:37 +01001085
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001086static int update_bl_status(struct backlight_device *bd)
1087{
1088 return set_brightness(bd, bd->props.brightness);
1089}
Corentin Charya9df80c2009-01-20 16:17:40 +01001090
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001091static struct backlight_ops eeepcbl_ops = {
1092 .get_brightness = read_brightness,
1093 .update_status = update_bl_status,
1094};
Eric Coopere59f8792008-03-13 12:55:46 +01001095
Alan Jenkins854c7832009-12-03 07:45:09 +00001096static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001097{
Alan Jenkins854c7832009-12-03 07:45:09 +00001098 struct backlight_device *bd = eeepc->backlight_device;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001099 int old = bd->props.brightness;
Eric Coopere59f8792008-03-13 12:55:46 +01001100
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001101 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
Eric Coopere59f8792008-03-13 12:55:46 +01001102
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001103 return old;
Eric Coopere59f8792008-03-13 12:55:46 +01001104}
1105
Alan Jenkins854c7832009-12-03 07:45:09 +00001106static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
Corentin Charya5fa4292008-03-13 12:56:37 +01001107{
1108 struct backlight_device *bd;
1109
Alan Jenkins854c7832009-12-03 07:45:09 +00001110 bd = backlight_device_register(EEEPC_LAPTOP_FILE,
1111 &eeepc->platform_device->dev,
1112 eeepc, &eeepcbl_ops);
Corentin Charya5fa4292008-03-13 12:56:37 +01001113 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001114 pr_err("Could not register eeepc backlight device\n");
Alan Jenkins854c7832009-12-03 07:45:09 +00001115 eeepc->backlight_device = NULL;
Corentin Charya5fa4292008-03-13 12:56:37 +01001116 return PTR_ERR(bd);
1117 }
Alan Jenkins854c7832009-12-03 07:45:09 +00001118 eeepc->backlight_device = bd;
Corentin Charya5fa4292008-03-13 12:56:37 +01001119 bd->props.max_brightness = 15;
Alan Jenkins854c7832009-12-03 07:45:09 +00001120 bd->props.brightness = read_brightness(bd);
Corentin Charya5fa4292008-03-13 12:56:37 +01001121 bd->props.power = FB_BLANK_UNBLANK;
1122 backlight_update_status(bd);
1123 return 0;
1124}
1125
Alan Jenkins854c7832009-12-03 07:45:09 +00001126static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
Corentin Charye1faa9d2008-03-13 12:57:18 +01001127{
Alan Jenkins854c7832009-12-03 07:45:09 +00001128 if (eeepc->backlight_device)
1129 backlight_device_unregister(eeepc->backlight_device);
1130 eeepc->backlight_device = NULL;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001131}
1132
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001133
1134/*
1135 * Input device (i.e. hotkeys)
1136 */
Alan Jenkins854c7832009-12-03 07:45:09 +00001137static int eeepc_input_init(struct eeepc_laptop *eeepc)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001138{
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001139 struct input_dev *input;
1140 int error;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001141
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001142 input = input_allocate_device();
1143 if (!input) {
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001144 pr_info("Unable to allocate input device\n");
1145 return -ENOMEM;
1146 }
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001147
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001148 input->name = "Asus EeePC extra buttons";
1149 input->phys = EEEPC_LAPTOP_FILE "/input0";
1150 input->id.bustype = BUS_HOST;
1151 input->dev.parent = &eeepc->platform_device->dev;
1152
1153 error = sparse_keymap_setup(input, eeepc_keymap, NULL);
1154 if (error) {
1155 pr_err("Unable to setup input device keymap\n");
1156 goto err_free_dev;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001157 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001158
1159 error = input_register_device(input);
1160 if (error) {
1161 pr_err("Unable to register input device\n");
1162 goto err_free_keymap;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001163 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001164
1165 eeepc->inputdev = input;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001166 return 0;
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001167
1168 err_free_keymap:
1169 sparse_keymap_free(input);
1170 err_free_dev:
1171 input_free_device(input);
1172 return error;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001173}
1174
Alan Jenkins854c7832009-12-03 07:45:09 +00001175static void eeepc_input_exit(struct eeepc_laptop *eeepc)
Eric Coopere59f8792008-03-13 12:55:46 +01001176{
Alan Jenkins854c7832009-12-03 07:45:09 +00001177 if (eeepc->inputdev) {
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001178 input_unregister_device(eeepc->inputdev);
Alan Jenkins854c7832009-12-03 07:45:09 +00001179 kfree(eeepc->keymap);
1180 }
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001181}
Corentin Chary3c0eb512009-12-03 07:44:52 +00001182
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001183/*
1184 * ACPI driver
1185 */
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001186static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001187{
Alan Jenkins854c7832009-12-03 07:45:09 +00001188 struct eeepc_laptop *eeepc = acpi_driver_data(device);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001189 u16 count;
Corentin Chary3c0eb512009-12-03 07:44:52 +00001190
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001191 if (event > ACPI_MAX_SYS_NOTIFY)
1192 return;
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001193 count = eeepc->event_count[event % 128]++;
Alan Jenkins854c7832009-12-03 07:45:09 +00001194 acpi_bus_generate_proc_event(device, event, count);
1195 acpi_bus_generate_netlink_event(device->pnp.device_class,
1196 dev_name(&device->dev), event,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001197 count);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001198
Alan Jenkins325fb8e2009-12-03 07:45:15 +00001199 /* Brightness events are special */
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001200 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001201
Alan Jenkins325fb8e2009-12-03 07:45:15 +00001202 /* Ignore them completely if the acpi video driver is used */
1203 if (eeepc->backlight_device != NULL) {
1204 int old_brightness, new_brightness;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001205
Alan Jenkins325fb8e2009-12-03 07:45:15 +00001206 /* Update the backlight device. */
1207 old_brightness = eeepc_backlight_notify(eeepc);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001208
Alan Jenkins325fb8e2009-12-03 07:45:15 +00001209 /* Convert event to keypress (obsolescent hack) */
1210 new_brightness = event - NOTIFY_BRN_MIN;
1211
1212 if (new_brightness < old_brightness) {
1213 event = NOTIFY_BRN_MIN; /* brightness down */
1214 } else if (new_brightness > old_brightness) {
1215 event = NOTIFY_BRN_MAX; /* brightness up */
1216 } else {
1217 /*
1218 * no change in brightness - already at min/max,
1219 * event will be desired value (or else ignored)
1220 */
1221 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001222 sparse_keymap_report_event(eeepc->inputdev, event,
1223 1, true);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001224 }
Alan Jenkins325fb8e2009-12-03 07:45:15 +00001225 } else {
1226 /* Everything else is a bona-fide keypress event */
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001227 sparse_keymap_report_event(eeepc->inputdev, event, 1, true);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001228 }
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001229}
1230
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001231static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
1232{
1233 const char *model;
1234
Corentin Chary10ae4b52010-01-06 22:07:38 +01001235 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1236 if (!model)
1237 return;
1238
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001239 /*
1240 * Blacklist for setting cpufv (cpu speed).
1241 *
1242 * EeePC 4G ("701") implements CFVS, but it is not supported
1243 * by the pre-installed OS, and the original option to change it
1244 * in the BIOS setup screen was removed in later versions.
1245 *
1246 * Judging by the lack of "Super Hybrid Engine" on Asus product pages,
1247 * this applies to all "701" models (4G/4G Surf/2G Surf).
1248 *
1249 * So Asus made a deliberate decision not to support it on this model.
1250 * We have several reports that using it can cause the system to hang
1251 *
1252 * The hang has also been reported on a "702" (Model name "8G"?).
1253 *
1254 * We avoid dmi_check_system() / dmi_match(), because they use
1255 * substring matching. We don't want to affect the "701SD"
1256 * and "701SDX" models, because they do support S.H.E.
1257 */
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001258 if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
1259 eeepc->cpufv_disabled = true;
1260 pr_info("model %s does not officially support setting cpu "
1261 "speed\n", model);
1262 pr_info("cpufv disabled to avoid instability\n");
1263 }
Corentin Chary10ae4b52010-01-06 22:07:38 +01001264
1265 /*
1266 * Blacklist for wlan hotplug
1267 *
1268 * Eeepc 1005HA doesn't work like others models and don't need the
1269 * hotplug code. In fact, current hotplug code seems to unplug another
1270 * device...
1271 */
1272 if (strcmp(model, "1005HA") == 0) {
1273 eeepc->hotplug_disabled = true;
1274 pr_info("wlan hotplug disabled\n");
1275 }
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001276}
1277
Alan Jenkins854c7832009-12-03 07:45:09 +00001278static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001279{
1280 int dummy;
1281
1282 /* Some BIOSes do not report cm although it is avaliable.
1283 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001284 if (!(eeepc->cm_supported & (1 << cm))
1285 && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001286 pr_info("%s (%x) not reported by BIOS,"
1287 " enabling anyway\n", name, 1 << cm);
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001288 eeepc->cm_supported |= 1 << cm;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001289 }
1290}
1291
Alan Jenkins854c7832009-12-03 07:45:09 +00001292static void cmsg_quirks(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001293{
Alan Jenkins854c7832009-12-03 07:45:09 +00001294 cmsg_quirk(eeepc, CM_ASL_LID, "LID");
1295 cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");
1296 cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");
1297 cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001298}
1299
Corentin Charyf90be872009-12-03 07:45:14 +00001300static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
1301 struct acpi_device *device)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001302{
1303 unsigned int init_flags;
Eric Coopere59f8792008-03-13 12:55:46 +01001304 int result;
1305
Alan Jenkins854c7832009-12-03 07:45:09 +00001306 result = acpi_bus_get_status(device);
Alan Jenkins1e779852009-08-28 12:56:35 +00001307 if (result)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001308 return result;
Alan Jenkins854c7832009-12-03 07:45:09 +00001309 if (!device->status.present) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001310 pr_err("Hotkey device not present, aborting\n");
1311 return -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +01001312 }
Corentin Chary7de39382009-06-25 13:25:38 +02001313
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001314 init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1315 pr_notice("Hotkey init flags 0x%x\n", init_flags);
1316
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001317 if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001318 pr_err("Hotkey initialization failed\n");
1319 return -ENODEV;
1320 }
1321
1322 /* get control methods supported */
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001323 if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001324 pr_err("Get control methods supported failed\n");
1325 return -ENODEV;
1326 }
Alan Jenkins854c7832009-12-03 07:45:09 +00001327 cmsg_quirks(eeepc);
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001328 pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001329
Corentin Chary3c0eb512009-12-03 07:44:52 +00001330 return 0;
1331}
1332
Alan Jenkins854c7832009-12-03 07:45:09 +00001333static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001334{
1335 /*
1336 * If the following call to set_acpi() fails, it's because there's no
1337 * camera so we can ignore the error.
1338 */
Alan Jenkins854c7832009-12-03 07:45:09 +00001339 if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
1340 set_acpi(eeepc, CM_ASL_CAMERA, 1);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001341}
1342
Alan Jenkins854c7832009-12-03 07:45:09 +00001343static bool eeepc_device_present;
1344
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001345static int __devinit eeepc_acpi_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001346{
Alan Jenkins854c7832009-12-03 07:45:09 +00001347 struct eeepc_laptop *eeepc;
Eric Coopere59f8792008-03-13 12:55:46 +01001348 int result;
1349
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001350 pr_notice(EEEPC_LAPTOP_NAME "\n");
1351 eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
1352 if (!eeepc)
Eric Coopere59f8792008-03-13 12:55:46 +01001353 return -ENOMEM;
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001354 eeepc->handle = device->handle;
1355 strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
1356 strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
1357 device->driver_data = eeepc;
Eric Coopere59f8792008-03-13 12:55:46 +01001358
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001359 eeepc_dmi_check(eeepc);
1360
Alan Jenkins854c7832009-12-03 07:45:09 +00001361 result = eeepc_acpi_init(eeepc, device);
Eric Coopere59f8792008-03-13 12:55:46 +01001362 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +00001363 goto fail_platform;
Alan Jenkins854c7832009-12-03 07:45:09 +00001364 eeepc_enable_camera(eeepc);
Eric Coopere59f8792008-03-13 12:55:46 +01001365
Alan Jenkins854c7832009-12-03 07:45:09 +00001366 /*
1367 * Register the platform device first. It is used as a parent for the
1368 * sub-devices below.
1369 *
1370 * Note that if there are multiple instances of this ACPI device it
1371 * will bail out, because the platform device is registered with a
1372 * fixed name. Of course it doesn't make sense to have more than one,
1373 * and machine-specific scripts find the fixed name convenient. But
1374 * It's also good for us to exclude multiple instances because both
1375 * our hwmon and our wlan rfkill subdevice use global ACPI objects
1376 * (the EC and the wlan PCI slot respectively).
1377 */
1378 result = eeepc_platform_init(eeepc);
Eric Coopere59f8792008-03-13 12:55:46 +01001379 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +00001380 goto fail_platform;
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001381
1382 if (!acpi_video_backlight_support()) {
Alan Jenkins854c7832009-12-03 07:45:09 +00001383 result = eeepc_backlight_init(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001384 if (result)
1385 goto fail_backlight;
1386 } else
Alan Jenkins9db106b2009-12-03 07:45:06 +00001387 pr_info("Backlight controlled by ACPI video driver\n");
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001388
Alan Jenkins854c7832009-12-03 07:45:09 +00001389 result = eeepc_input_init(eeepc);
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001390 if (result)
1391 goto fail_input;
1392
Alan Jenkins854c7832009-12-03 07:45:09 +00001393 result = eeepc_hwmon_init(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001394 if (result)
1395 goto fail_hwmon;
1396
Alan Jenkins854c7832009-12-03 07:45:09 +00001397 result = eeepc_led_init(eeepc);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001398 if (result)
1399 goto fail_led;
1400
Alan Jenkins854c7832009-12-03 07:45:09 +00001401 result = eeepc_rfkill_init(eeepc);
Corentin Chary7de39382009-06-25 13:25:38 +02001402 if (result)
1403 goto fail_rfkill;
1404
Alan Jenkins854c7832009-12-03 07:45:09 +00001405 eeepc_device_present = true;
Eric Coopere59f8792008-03-13 12:55:46 +01001406 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001407
Corentin Chary7de39382009-06-25 13:25:38 +02001408fail_rfkill:
Alan Jenkins854c7832009-12-03 07:45:09 +00001409 eeepc_led_exit(eeepc);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001410fail_led:
Alan Jenkins854c7832009-12-03 07:45:09 +00001411 eeepc_hwmon_exit(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001412fail_hwmon:
Alan Jenkins854c7832009-12-03 07:45:09 +00001413 eeepc_input_exit(eeepc);
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001414fail_input:
Alan Jenkins854c7832009-12-03 07:45:09 +00001415 eeepc_backlight_exit(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001416fail_backlight:
Alan Jenkins854c7832009-12-03 07:45:09 +00001417 eeepc_platform_exit(eeepc);
Alan Jenkins9db106b2009-12-03 07:45:06 +00001418fail_platform:
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001419 kfree(eeepc);
Alan Jenkins1e779852009-08-28 12:56:35 +00001420
Eric Coopere59f8792008-03-13 12:55:46 +01001421 return result;
1422}
1423
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001424static int eeepc_acpi_remove(struct acpi_device *device, int type)
Alan Jenkins1e779852009-08-28 12:56:35 +00001425{
Alan Jenkins854c7832009-12-03 07:45:09 +00001426 struct eeepc_laptop *eeepc = acpi_driver_data(device);
Alan Jenkins1e779852009-08-28 12:56:35 +00001427
Alan Jenkins854c7832009-12-03 07:45:09 +00001428 eeepc_backlight_exit(eeepc);
1429 eeepc_rfkill_exit(eeepc);
1430 eeepc_input_exit(eeepc);
1431 eeepc_hwmon_exit(eeepc);
1432 eeepc_led_exit(eeepc);
1433 eeepc_platform_exit(eeepc);
Alan Jenkins1e779852009-08-28 12:56:35 +00001434
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001435 kfree(eeepc);
Alan Jenkins1e779852009-08-28 12:56:35 +00001436 return 0;
1437}
1438
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001439
1440static const struct acpi_device_id eeepc_device_ids[] = {
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001441 {EEEPC_ACPI_HID, 0},
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001442 {"", 0},
1443};
1444MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
1445
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001446static struct acpi_driver eeepc_acpi_driver = {
1447 .name = EEEPC_LAPTOP_NAME,
1448 .class = EEEPC_ACPI_CLASS,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001449 .owner = THIS_MODULE,
1450 .ids = eeepc_device_ids,
1451 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1452 .ops = {
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001453 .add = eeepc_acpi_add,
1454 .remove = eeepc_acpi_remove,
1455 .notify = eeepc_acpi_notify,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001456 },
1457};
1458
1459
Alan Jenkins1e779852009-08-28 12:56:35 +00001460static int __init eeepc_laptop_init(void)
1461{
1462 int result;
1463
Alan Jenkins22072e92009-12-03 07:45:05 +00001464 result = platform_driver_register(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001465 if (result < 0)
1466 return result;
Alan Jenkins22072e92009-12-03 07:45:05 +00001467
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001468 result = acpi_bus_register_driver(&eeepc_acpi_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001469 if (result < 0)
1470 goto fail_acpi_driver;
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001471
Alan Jenkins854c7832009-12-03 07:45:09 +00001472 if (!eeepc_device_present) {
Alan Jenkins22072e92009-12-03 07:45:05 +00001473 result = -ENODEV;
1474 goto fail_no_device;
Alan Jenkins1e779852009-08-28 12:56:35 +00001475 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001476
Alan Jenkins1e779852009-08-28 12:56:35 +00001477 return 0;
Alan Jenkins22072e92009-12-03 07:45:05 +00001478
1479fail_no_device:
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001480 acpi_bus_unregister_driver(&eeepc_acpi_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001481fail_acpi_driver:
1482 platform_driver_unregister(&platform_driver);
1483 return result;
Alan Jenkins1e779852009-08-28 12:56:35 +00001484}
1485
1486static void __exit eeepc_laptop_exit(void)
1487{
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001488 acpi_bus_unregister_driver(&eeepc_acpi_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001489 platform_driver_unregister(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001490}
1491
Eric Coopere59f8792008-03-13 12:55:46 +01001492module_init(eeepc_laptop_init);
1493module_exit(eeepc_laptop_exit);