blob: f3f74a9c109e95e42a4012f888eb71c80ff258f1 [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Eric Coopere59f8792008-03-13 12:55:46 +01002/*
Alan Jenkinsa7624b62009-12-03 07:45:08 +00003 * eeepc-laptop.c - Asus Eee PC extras
Eric Coopere59f8792008-03-13 12:55:46 +01004 *
5 * Based on asus_acpi.c as patched for the Eee PC by Asus:
6 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
7 * Based on eee.c from eeepc-linux
Eric Coopere59f8792008-03-13 12:55:46 +01008 */
9
Joe Perches19b53282009-06-25 13:25:37 +020010#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
Eric Coopere59f8792008-03-13 12:55:46 +010012#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/types.h>
16#include <linux/platform_device.h>
Corentin Charya5fa4292008-03-13 12:56:37 +010017#include <linux/backlight.h>
18#include <linux/fb.h>
Corentin Charye1faa9d2008-03-13 12:57:18 +010019#include <linux/hwmon.h>
20#include <linux/hwmon-sysfs.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Lv Zheng8b484632013-12-03 08:49:16 +080022#include <linux/acpi.h>
Eric Coopere59f8792008-03-13 12:55:46 +010023#include <linux/uaccess.h>
Matthew Garretta195dcd2008-08-19 12:13:20 +010024#include <linux/input.h>
Dmitry Torokhov642e0442010-01-06 22:07:39 +010025#include <linux/input/sparse-keymap.h>
Matthew Garretta195dcd2008-08-19 12:13:20 +010026#include <linux/rfkill.h>
Matthew Garrett57402942009-01-20 16:17:48 +010027#include <linux/pci.h>
Corentin Chary2b121bc2009-06-25 13:25:36 +020028#include <linux/pci_hotplug.h>
Corentin Chary3c0eb512009-12-03 07:44:52 +000029#include <linux/leds.h>
Alan Jenkinsda8ba012010-01-06 22:07:37 +010030#include <linux/dmi.h>
Hans de Goede21db4b12015-06-16 16:28:02 +020031#include <acpi/video.h>
Eric Coopere59f8792008-03-13 12:55:46 +010032
33#define EEEPC_LAPTOP_VERSION "0.1"
Alan Jenkinsa7624b62009-12-03 07:45:08 +000034#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
35#define EEEPC_LAPTOP_FILE "eeepc"
Eric Coopere59f8792008-03-13 12:55:46 +010036
Alan Jenkinsa7624b62009-12-03 07:45:08 +000037#define EEEPC_ACPI_CLASS "hotkey"
38#define EEEPC_ACPI_DEVICE_NAME "Hotkey"
39#define EEEPC_ACPI_HID "ASUS010"
Eric Coopere59f8792008-03-13 12:55:46 +010040
Alan Jenkins52bbe3c2009-12-03 07:45:07 +000041MODULE_AUTHOR("Corentin Chary, Eric Cooper");
Alan Jenkinsa7624b62009-12-03 07:45:08 +000042MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +000043MODULE_LICENSE("GPL");
Eric Coopere59f8792008-03-13 12:55:46 +010044
Corentin Chary322a1352010-01-06 22:07:40 +010045static bool hotplug_disabled;
46
Axel Linbfa47962010-07-20 15:19:55 -070047module_param(hotplug_disabled, bool, 0444);
Corentin Chary322a1352010-01-06 22:07:40 +010048MODULE_PARM_DESC(hotplug_disabled,
49 "Disable hotplug for wireless device. "
50 "If your laptop need that, please report to "
51 "acpi4asus-user@lists.sourceforge.net.");
52
Eric Coopere59f8792008-03-13 12:55:46 +010053/*
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 } },
Pau Espin Pedrol999ddbd2017-02-10 18:48:37 +0100144 { KE_IGNORE, 0x50, { KEY_RESERVED } }, /* AC plugged */
145 { KE_IGNORE, 0x51, { KEY_RESERVED } }, /* AC unplugged */
Dmitry Torokhov642e0442010-01-06 22:07:39 +0100146 { KE_END, 0 },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100147};
148
Corentin Charya5fa4292008-03-13 12:56:37 +0100149/*
Eric Coopere59f8792008-03-13 12:55:46 +0100150 * This is the main structure, we can use it to store useful information
Corentin Charya5fa4292008-03-13 12:56:37 +0100151 */
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000152struct eeepc_laptop {
Alan Jenkins854c7832009-12-03 07:45:09 +0000153 acpi_handle handle; /* the handle of the acpi device */
Eric Coopere59f8792008-03-13 12:55:46 +0100154 u32 cm_supported; /* the control methods supported
155 by this BIOS */
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100156 bool cpufv_disabled;
Corentin Chary10ae4b52010-01-06 22:07:38 +0100157 bool hotplug_disabled;
Eric Coopere59f8792008-03-13 12:55:46 +0100158 u16 event_count[128]; /* count for each event */
Corentin Charya5fa4292008-03-13 12:56:37 +0100159
Alan Jenkins854c7832009-12-03 07:45:09 +0000160 struct platform_device *platform_device;
Corentin Chary71e687d2010-08-24 09:30:44 +0200161 struct acpi_device *device; /* the device we are in */
Alan Jenkins854c7832009-12-03 07:45:09 +0000162 struct backlight_device *backlight_device;
163
Eric Coopere59f8792008-03-13 12:55:46 +0100164 struct input_dev *inputdev;
Alan Jenkins854c7832009-12-03 07:45:09 +0000165
Eric Coopere59f8792008-03-13 12:55:46 +0100166 struct rfkill *wlan_rfkill;
167 struct rfkill *bluetooth_rfkill;
168 struct rfkill *wwan3g_rfkill;
169 struct rfkill *wimax_rfkill;
Alan Jenkins854c7832009-12-03 07:45:09 +0000170
Lukas Wunner125450f2018-09-08 09:59:01 +0200171 struct hotplug_slot hotplug_slot;
Eric Coopere59f8792008-03-13 12:55:46 +0100172 struct mutex hotplug_lock;
Alan Jenkins854c7832009-12-03 07:45:09 +0000173
174 struct led_classdev tpd_led;
175 int tpd_led_wk;
176 struct workqueue_struct *led_workqueue;
177 struct work_struct tpd_led_work;
Eric Coopere59f8792008-03-13 12:55:46 +0100178};
Eric Coopere59f8792008-03-13 12:55:46 +0100179
180/*
181 * ACPI Helpers
182 */
Alan Jenkins6b188a72009-12-03 07:45:02 +0000183static int write_acpi_int(acpi_handle handle, const char *method, int val)
Eric Coopere59f8792008-03-13 12:55:46 +0100184{
Eric Coopere59f8792008-03-13 12:55:46 +0100185 acpi_status status;
186
Zhang Ruifca41992013-09-03 08:31:51 +0800187 status = acpi_execute_simple_method(handle, (char *)method, val);
Eric Coopere59f8792008-03-13 12:55:46 +0100188
Eric Coopere59f8792008-03-13 12:55:46 +0100189 return (status == AE_OK ? 0 : -1);
190}
191
192static int read_acpi_int(acpi_handle handle, const char *method, int *val)
193{
194 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400195 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100196
197 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
198 if (ACPI_FAILURE(status)) {
199 *val = -1;
200 return -1;
201 } else {
202 *val = result;
203 return 0;
204 }
205}
206
Alan Jenkins854c7832009-12-03 07:45:09 +0000207static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value)
Eric Coopere59f8792008-03-13 12:55:46 +0100208{
Alan Jenkins13f70022009-12-03 07:44:59 +0000209 const char *method = cm_setv[cm];
210
211 if (method == NULL)
212 return -ENODEV;
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000213 if ((eeepc->cm_supported & (0x1 << cm)) == 0)
Alan Jenkins13f70022009-12-03 07:44:59 +0000214 return -ENODEV;
215
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000216 if (write_acpi_int(eeepc->handle, method, value))
Joe Perches22441ff2011-03-29 15:21:38 -0700217 pr_warn("Error writing %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100218 return 0;
219}
220
Alan Jenkins854c7832009-12-03 07:45:09 +0000221static int get_acpi(struct eeepc_laptop *eeepc, int cm)
Eric Coopere59f8792008-03-13 12:55:46 +0100222{
Alan Jenkins13f70022009-12-03 07:44:59 +0000223 const char *method = cm_getv[cm];
224 int value;
225
226 if (method == NULL)
227 return -ENODEV;
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000228 if ((eeepc->cm_supported & (0x1 << cm)) == 0)
Alan Jenkins13f70022009-12-03 07:44:59 +0000229 return -ENODEV;
230
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000231 if (read_acpi_int(eeepc->handle, method, &value))
Joe Perches22441ff2011-03-29 15:21:38 -0700232 pr_warn("Error reading %s\n", method);
Eric Coopere59f8792008-03-13 12:55:46 +0100233 return value;
234}
235
Corentin Charyf90be872009-12-03 07:45:14 +0000236static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,
237 acpi_handle *handle)
Alan Jenkins854c7832009-12-03 07:45:09 +0000238{
239 const char *method = cm_setv[cm];
240 acpi_status status;
241
242 if (method == NULL)
243 return -ENODEV;
244 if ((eeepc->cm_supported & (0x1 << cm)) == 0)
245 return -ENODEV;
246
247 status = acpi_get_handle(eeepc->handle, (char *)method,
Corentin Charyf90be872009-12-03 07:45:14 +0000248 handle);
Alan Jenkins854c7832009-12-03 07:45:09 +0000249 if (status != AE_OK) {
Joe Perches22441ff2011-03-29 15:21:38 -0700250 pr_warn("Error finding %s\n", method);
Alan Jenkins854c7832009-12-03 07:45:09 +0000251 return -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +0100252 }
253 return 0;
254}
255
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000256
Matthew Garretta195dcd2008-08-19 12:13:20 +0100257/*
Eric Coopere59f8792008-03-13 12:55:46 +0100258 * Sys helpers
259 */
Paul Bolle95369a72014-09-17 21:02:51 +0200260static int parse_arg(const char *buf, int *val)
Eric Coopere59f8792008-03-13 12:55:46 +0100261{
Eric Coopere59f8792008-03-13 12:55:46 +0100262 if (sscanf(buf, "%i", val) != 1)
263 return -EINVAL;
Paul Bolle95369a72014-09-17 21:02:51 +0200264 return 0;
Eric Coopere59f8792008-03-13 12:55:46 +0100265}
266
Alan Jenkins854c7832009-12-03 07:45:09 +0000267static ssize_t store_sys_acpi(struct device *dev, int cm,
268 const char *buf, size_t count)
Eric Coopere59f8792008-03-13 12:55:46 +0100269{
Alan Jenkins854c7832009-12-03 07:45:09 +0000270 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Eric Coopere59f8792008-03-13 12:55:46 +0100271 int rv, value;
272
Paul Bolle95369a72014-09-17 21:02:51 +0200273 rv = parse_arg(buf, &value);
274 if (rv < 0)
275 return rv;
276 rv = set_acpi(eeepc, cm, value);
277 if (rv < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000278 return -EIO;
Paul Bolle95369a72014-09-17 21:02:51 +0200279 return count;
Eric Coopere59f8792008-03-13 12:55:46 +0100280}
281
Alan Jenkins854c7832009-12-03 07:45:09 +0000282static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
Eric Coopere59f8792008-03-13 12:55:46 +0100283{
Alan Jenkins854c7832009-12-03 07:45:09 +0000284 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
285 int value = get_acpi(eeepc, cm);
Corentin Charyf36509e2009-06-25 13:25:40 +0200286
287 if (value < 0)
Alan Jenkins6dff29b2009-12-03 07:44:45 +0000288 return -EIO;
Corentin Charyf36509e2009-06-25 13:25:40 +0200289 return sprintf(buf, "%d\n", value);
Eric Coopere59f8792008-03-13 12:55:46 +0100290}
291
Frans Klaver97971322014-09-17 23:47:22 +0200292#define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \
Frans Klaver8c72fc82014-09-17 23:47:20 +0200293 static ssize_t _name##_show(struct device *dev, \
Eric Coopere59f8792008-03-13 12:55:46 +0100294 struct device_attribute *attr, \
295 char *buf) \
296 { \
Alan Jenkins854c7832009-12-03 07:45:09 +0000297 return show_sys_acpi(dev, _cm, buf); \
Frans Klaver97971322014-09-17 23:47:22 +0200298 }
299
300#define EEEPC_ACPI_STORE_FUNC(_name, _cm) \
Frans Klaver8c72fc82014-09-17 23:47:20 +0200301 static ssize_t _name##_store(struct device *dev, \
Eric Coopere59f8792008-03-13 12:55:46 +0100302 struct device_attribute *attr, \
303 const char *buf, size_t count) \
304 { \
Alan Jenkins854c7832009-12-03 07:45:09 +0000305 return store_sys_acpi(dev, _cm, buf, count); \
Frans Klaver97971322014-09-17 23:47:22 +0200306 }
307
Frans Klaver6fe3a772014-09-17 23:47:23 +0200308#define EEEPC_CREATE_DEVICE_ATTR_RW(_name, _cm) \
Frans Klaver97971322014-09-17 23:47:22 +0200309 EEEPC_ACPI_SHOW_FUNC(_name, _cm) \
310 EEEPC_ACPI_STORE_FUNC(_name, _cm) \
Frans Klaver6fe3a772014-09-17 23:47:23 +0200311 static DEVICE_ATTR_RW(_name)
Eric Coopere59f8792008-03-13 12:55:46 +0100312
Frans Klaver6fe3a772014-09-17 23:47:23 +0200313#define EEEPC_CREATE_DEVICE_ATTR_WO(_name, _cm) \
314 EEEPC_ACPI_STORE_FUNC(_name, _cm) \
315 static DEVICE_ATTR_WO(_name)
316
317EEEPC_CREATE_DEVICE_ATTR_RW(camera, CM_ASL_CAMERA);
318EEEPC_CREATE_DEVICE_ATTR_RW(cardr, CM_ASL_CARDREADER);
319EEEPC_CREATE_DEVICE_ATTR_WO(disp, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000320
321struct eeepc_cpufv {
322 int num;
323 int cur;
324};
325
Alan Jenkins854c7832009-12-03 07:45:09 +0000326static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000327{
Alan Jenkins854c7832009-12-03 07:45:09 +0000328 c->cur = get_acpi(eeepc, CM_ASL_CPUFV);
Frans Klavera5c155b2014-09-17 23:47:26 +0200329 if (c->cur < 0)
330 return -ENODEV;
331
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000332 c->num = (c->cur >> 8) & 0xff;
333 c->cur &= 0xff;
Frans Klavera5c155b2014-09-17 23:47:26 +0200334 if (c->num == 0 || c->num > 12)
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000335 return -ENODEV;
336 return 0;
337}
338
Frans Klaver8c72fc82014-09-17 23:47:20 +0200339static ssize_t available_cpufv_show(struct device *dev,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000340 struct device_attribute *attr,
341 char *buf)
342{
Alan Jenkins854c7832009-12-03 07:45:09 +0000343 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000344 struct eeepc_cpufv c;
345 int i;
346 ssize_t len = 0;
347
Alan Jenkins854c7832009-12-03 07:45:09 +0000348 if (get_cpufv(eeepc, &c))
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000349 return -ENODEV;
350 for (i = 0; i < c.num; i++)
351 len += sprintf(buf + len, "%d ", i);
352 len += sprintf(buf + len, "\n");
353 return len;
354}
355
Frans Klaver8c72fc82014-09-17 23:47:20 +0200356static ssize_t cpufv_show(struct device *dev,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000357 struct device_attribute *attr,
358 char *buf)
359{
Alan Jenkins854c7832009-12-03 07:45:09 +0000360 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000361 struct eeepc_cpufv c;
362
Alan Jenkins854c7832009-12-03 07:45:09 +0000363 if (get_cpufv(eeepc, &c))
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000364 return -ENODEV;
365 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
366}
367
Frans Klaver8c72fc82014-09-17 23:47:20 +0200368static ssize_t cpufv_store(struct device *dev,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000369 struct device_attribute *attr,
370 const char *buf, size_t count)
371{
Alan Jenkins854c7832009-12-03 07:45:09 +0000372 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000373 struct eeepc_cpufv c;
374 int rv, value;
375
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100376 if (eeepc->cpufv_disabled)
377 return -EPERM;
Alan Jenkins854c7832009-12-03 07:45:09 +0000378 if (get_cpufv(eeepc, &c))
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000379 return -ENODEV;
Paul Bolle95369a72014-09-17 21:02:51 +0200380 rv = parse_arg(buf, &value);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000381 if (rv < 0)
382 return rv;
Paul Bolle95369a72014-09-17 21:02:51 +0200383 if (value < 0 || value >= c.num)
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000384 return -EINVAL;
Frans Klaverd4869032014-09-17 23:47:27 +0200385 rv = set_acpi(eeepc, CM_ASL_CPUFV, value);
386 if (rv)
387 return rv;
Paul Bolle95369a72014-09-17 21:02:51 +0200388 return count;
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000389}
390
Frans Klaver8c72fc82014-09-17 23:47:20 +0200391static ssize_t cpufv_disabled_show(struct device *dev,
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100392 struct device_attribute *attr,
393 char *buf)
394{
395 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
396
397 return sprintf(buf, "%d\n", eeepc->cpufv_disabled);
398}
399
Frans Klaver8c72fc82014-09-17 23:47:20 +0200400static ssize_t cpufv_disabled_store(struct device *dev,
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100401 struct device_attribute *attr,
402 const char *buf, size_t count)
403{
404 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
405 int rv, value;
406
Paul Bolle95369a72014-09-17 21:02:51 +0200407 rv = parse_arg(buf, &value);
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100408 if (rv < 0)
409 return rv;
410
411 switch (value) {
412 case 0:
413 if (eeepc->cpufv_disabled)
Frans Klaver9f662b22014-10-22 21:12:37 +0200414 pr_warn("cpufv enabled (not officially supported on this model)\n");
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100415 eeepc->cpufv_disabled = false;
Paul Bolle95369a72014-09-17 21:02:51 +0200416 return count;
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100417 case 1:
418 return -EPERM;
419 default:
420 return -EINVAL;
421 }
422}
423
424
Frans Klaverbb382db2014-09-17 23:47:21 +0200425static DEVICE_ATTR_RW(cpufv);
426static DEVICE_ATTR_RO(available_cpufv);
427static DEVICE_ATTR_RW(cpufv_disabled);
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100428
Eric Coopere59f8792008-03-13 12:55:46 +0100429static struct attribute *platform_attributes[] = {
430 &dev_attr_camera.attr,
431 &dev_attr_cardr.attr,
432 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200433 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000434 &dev_attr_available_cpufv.attr,
Alan Jenkinsda8ba012010-01-06 22:07:37 +0100435 &dev_attr_cpufv_disabled.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100436 NULL
437};
438
Arvind Yadav130bbe62017-06-23 14:40:51 +0530439static const struct attribute_group platform_attribute_group = {
Eric Coopere59f8792008-03-13 12:55:46 +0100440 .attrs = platform_attributes
441};
442
Alan Jenkins854c7832009-12-03 07:45:09 +0000443static int eeepc_platform_init(struct eeepc_laptop *eeepc)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100444{
Eric Coopere59f8792008-03-13 12:55:46 +0100445 int result;
446
Alan Jenkins854c7832009-12-03 07:45:09 +0000447 eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
448 if (!eeepc->platform_device)
Alan Jenkins9db106b2009-12-03 07:45:06 +0000449 return -ENOMEM;
Alan Jenkins854c7832009-12-03 07:45:09 +0000450 platform_set_drvdata(eeepc->platform_device, eeepc);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000451
Alan Jenkins854c7832009-12-03 07:45:09 +0000452 result = platform_device_add(eeepc->platform_device);
Eric Coopere59f8792008-03-13 12:55:46 +0100453 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +0000454 goto fail_platform_device;
455
Alan Jenkins854c7832009-12-03 07:45:09 +0000456 result = sysfs_create_group(&eeepc->platform_device->dev.kobj,
Alan Jenkins9db106b2009-12-03 07:45:06 +0000457 &platform_attribute_group);
458 if (result)
459 goto fail_sysfs;
Eric Coopere59f8792008-03-13 12:55:46 +0100460 return 0;
Alan Jenkins9db106b2009-12-03 07:45:06 +0000461
462fail_sysfs:
Alan Jenkins854c7832009-12-03 07:45:09 +0000463 platform_device_del(eeepc->platform_device);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000464fail_platform_device:
Alan Jenkins854c7832009-12-03 07:45:09 +0000465 platform_device_put(eeepc->platform_device);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000466 return result;
Eric Coopere59f8792008-03-13 12:55:46 +0100467}
468
Alan Jenkins854c7832009-12-03 07:45:09 +0000469static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
Corentin Charya5fa4292008-03-13 12:56:37 +0100470{
Alan Jenkins854c7832009-12-03 07:45:09 +0000471 sysfs_remove_group(&eeepc->platform_device->dev.kobj,
Alan Jenkins9db106b2009-12-03 07:45:06 +0000472 &platform_attribute_group);
Alan Jenkins854c7832009-12-03 07:45:09 +0000473 platform_device_unregister(eeepc->platform_device);
Alan Jenkins9db106b2009-12-03 07:45:06 +0000474}
475
Eric Coopere59f8792008-03-13 12:55:46 +0100476/*
Corentin Chary3c0eb512009-12-03 07:44:52 +0000477 * LEDs
478 */
479/*
480 * These functions actually update the LED's, and are called from a
481 * workqueue. By doing this as separate work rather than when the LED
482 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
483 * potentially bad time, such as a timer interrupt.
484 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000485static void tpd_led_update(struct work_struct *work)
Joe Perches447a5642018-03-21 15:09:32 -0700486{
Alan Jenkins854c7832009-12-03 07:45:09 +0000487 struct eeepc_laptop *eeepc;
Corentin Chary3c0eb512009-12-03 07:44:52 +0000488
Alan Jenkins854c7832009-12-03 07:45:09 +0000489 eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
490
491 set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);
Corentin Chary3c0eb512009-12-03 07:44:52 +0000492}
493
Corentin Chary3c0eb512009-12-03 07:44:52 +0000494static void tpd_led_set(struct led_classdev *led_cdev,
495 enum led_brightness value)
496{
Alan Jenkins854c7832009-12-03 07:45:09 +0000497 struct eeepc_laptop *eeepc;
498
499 eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
500
501 eeepc->tpd_led_wk = (value > 0) ? 1 : 0;
502 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
Corentin Chary3c0eb512009-12-03 07:44:52 +0000503}
504
Corentin Chary62a75d832010-11-29 08:14:13 +0100505static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
506{
507 struct eeepc_laptop *eeepc;
508
509 eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
510
511 return get_acpi(eeepc, CM_ASL_TPD);
512}
513
Alan Jenkins854c7832009-12-03 07:45:09 +0000514static int eeepc_led_init(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000515{
516 int rv;
517
Alan Jenkins854c7832009-12-03 07:45:09 +0000518 if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000519 return 0;
520
Alan Jenkins854c7832009-12-03 07:45:09 +0000521 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
522 if (!eeepc->led_workqueue)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000523 return -ENOMEM;
Alan Jenkins854c7832009-12-03 07:45:09 +0000524 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000525
Alan Jenkins854c7832009-12-03 07:45:09 +0000526 eeepc->tpd_led.name = "eeepc::touchpad";
527 eeepc->tpd_led.brightness_set = tpd_led_set;
Corentin Chary62a75d832010-11-29 08:14:13 +0100528 if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */
Frans Klaver39a3e172014-09-17 23:47:19 +0200529 eeepc->tpd_led.brightness_get = tpd_led_get;
Alan Jenkins854c7832009-12-03 07:45:09 +0000530 eeepc->tpd_led.max_brightness = 1;
531
532 rv = led_classdev_register(&eeepc->platform_device->dev,
533 &eeepc->tpd_led);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000534 if (rv) {
Alan Jenkins854c7832009-12-03 07:45:09 +0000535 destroy_workqueue(eeepc->led_workqueue);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000536 return rv;
Darren Salt64b86b62009-04-27 09:23:38 +0200537 }
Corentin Chary2b121bc2009-06-25 13:25:36 +0200538
539 return 0;
540}
541
Alan Jenkins854c7832009-12-03 07:45:09 +0000542static void eeepc_led_exit(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000543{
Axel Lin5d6afd152011-08-08 17:14:19 +0800544 if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
Alan Jenkins854c7832009-12-03 07:45:09 +0000545 led_classdev_unregister(&eeepc->tpd_led);
546 if (eeepc->led_workqueue)
547 destroy_workqueue(eeepc->led_workqueue);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000548}
549
550
Corentin Chary3c0eb512009-12-03 07:44:52 +0000551/*
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000552 * PCI hotplug (for wlan rfkill)
Eric Coopere59f8792008-03-13 12:55:46 +0100553 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000554static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
Eric Coopere59f8792008-03-13 12:55:46 +0100555{
Alan Jenkins854c7832009-12-03 07:45:09 +0000556 if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000557 return false;
558 return true;
Corentin Charya5fa4292008-03-13 12:56:37 +0100559}
560
Matthew Garrett14fdb152011-05-09 10:44:01 -0400561static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
Matthew Garrett57402942009-01-20 16:17:48 +0100562{
Matthew Garrett14fdb152011-05-09 10:44:01 -0400563 struct pci_dev *port;
Matthew Garrett57402942009-01-20 16:17:48 +0100564 struct pci_dev *dev;
Alan Jenkins6d418392009-08-28 12:56:32 +0000565 struct pci_bus *bus;
Alan Jenkins854c7832009-12-03 07:45:09 +0000566 bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
Alan Jenkinsbc9d24a2010-02-22 16:03:58 +0000567 bool absent;
568 u32 l;
Matthew Garrett57402942009-01-20 16:17:48 +0100569
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000570 if (eeepc->wlan_rfkill)
571 rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
Alan Jenkins6d418392009-08-28 12:56:32 +0000572
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000573 mutex_lock(&eeepc->hotplug_lock);
Rafael J. Wysocki8b9ec1d2014-01-10 15:27:08 +0100574 pci_lock_rescan_remove();
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000575
Lukas Wunner125450f2018-09-08 09:59:01 +0200576 if (!eeepc->hotplug_slot.ops)
Frans Klaver248d4902014-10-22 21:12:36 +0200577 goto out_unlock;
Matthew Garrett14fdb152011-05-09 10:44:01 -0400578
Frans Klaver248d4902014-10-22 21:12:36 +0200579 port = acpi_get_pci_dev(handle);
580 if (!port) {
581 pr_warning("Unable to find port\n");
582 goto out_unlock;
Matthew Garrett57402942009-01-20 16:17:48 +0100583 }
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000584
Frans Klaver248d4902014-10-22 21:12:36 +0200585 bus = port->subordinate;
586
587 if (!bus) {
588 pr_warn("Unable to find PCI bus 1?\n");
589 goto out_put_dev;
590 }
591
592 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
593 pr_err("Unable to read PCI config space?\n");
594 goto out_put_dev;
595 }
596
597 absent = (l == 0xffffffff);
598
599 if (blocked != absent) {
Frans Klaver9f662b22014-10-22 21:12:37 +0200600 pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
Frans Klaver248d4902014-10-22 21:12:36 +0200601 blocked ? "blocked" : "unblocked",
602 absent ? "absent" : "present");
Frans Klaver9f662b22014-10-22 21:12:37 +0200603 pr_warn("skipped wireless hotplug as probably inappropriate for this model\n");
Frans Klaver248d4902014-10-22 21:12:36 +0200604 goto out_put_dev;
605 }
606
607 if (!blocked) {
608 dev = pci_get_slot(bus, 0);
609 if (dev) {
610 /* Device already present */
611 pci_dev_put(dev);
612 goto out_put_dev;
613 }
614 dev = pci_scan_single_device(bus, 0);
615 if (dev) {
616 pci_bus_assign_resources(bus);
617 pci_bus_add_device(dev);
618 }
619 } else {
620 dev = pci_get_slot(bus, 0);
621 if (dev) {
622 pci_stop_and_remove_bus_device(dev);
623 pci_dev_put(dev);
624 }
625 }
626out_put_dev:
627 pci_dev_put(port);
628
Alan Jenkinsdcf443b2009-08-28 12:56:33 +0000629out_unlock:
Rafael J. Wysocki8b9ec1d2014-01-10 15:27:08 +0100630 pci_unlock_rescan_remove();
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000631 mutex_unlock(&eeepc->hotplug_lock);
Matthew Garrett57402942009-01-20 16:17:48 +0100632}
633
Matthew Garrett14fdb152011-05-09 10:44:01 -0400634static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node)
635{
636 acpi_status status = AE_OK;
637 acpi_handle handle;
638
639 status = acpi_get_handle(NULL, node, &handle);
640
641 if (ACPI_SUCCESS(status))
642 eeepc_rfkill_hotplug(eeepc, handle);
643}
644
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100645static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
646{
Alan Jenkins854c7832009-12-03 07:45:09 +0000647 struct eeepc_laptop *eeepc = data;
648
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100649 if (event != ACPI_NOTIFY_BUS_CHECK)
650 return;
651
Matthew Garrett14fdb152011-05-09 10:44:01 -0400652 eeepc_rfkill_hotplug(eeepc, handle);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100653}
654
Alan Jenkins854c7832009-12-03 07:45:09 +0000655static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
656 char *node)
Eric Coopere59f8792008-03-13 12:55:46 +0100657{
Alan Jenkins854c7832009-12-03 07:45:09 +0000658 acpi_status status;
Matthew Garrett57402942009-01-20 16:17:48 +0100659 acpi_handle handle;
660
661 status = acpi_get_handle(NULL, node, &handle);
662
Frans Klaver557b4542014-09-21 00:22:17 +0200663 if (ACPI_FAILURE(status))
Matthew Garrett57402942009-01-20 16:17:48 +0100664 return -ENODEV;
665
Frans Klaver557b4542014-09-21 00:22:17 +0200666 status = acpi_install_notify_handler(handle,
667 ACPI_SYSTEM_NOTIFY,
668 eeepc_rfkill_notify,
669 eeepc);
670 if (ACPI_FAILURE(status))
671 pr_warn("Failed to register notify on %s\n", node);
672
673 /*
674 * Refresh pci hotplug in case the rfkill state was
675 * changed during setup.
676 */
677 eeepc_rfkill_hotplug(eeepc, handle);
Matthew Garrett57402942009-01-20 16:17:48 +0100678 return 0;
679}
680
Alan Jenkins854c7832009-12-03 07:45:09 +0000681static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
682 char *node)
Matthew Garrett57402942009-01-20 16:17:48 +0100683{
684 acpi_status status = AE_OK;
685 acpi_handle handle;
686
687 status = acpi_get_handle(NULL, node, &handle);
688
Frans Klaver557b4542014-09-21 00:22:17 +0200689 if (ACPI_FAILURE(status))
690 return;
691
692 status = acpi_remove_notify_handler(handle,
693 ACPI_SYSTEM_NOTIFY,
694 eeepc_rfkill_notify);
695 if (ACPI_FAILURE(status))
696 pr_err("Error removing rfkill notify handler %s\n",
697 node);
698 /*
699 * Refresh pci hotplug in case the rfkill
700 * state was changed after
701 * eeepc_unregister_rfkill_notifier()
702 */
703 eeepc_rfkill_hotplug(eeepc, handle);
Matthew Garrett57402942009-01-20 16:17:48 +0100704}
705
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000706static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
707 u8 *value)
708{
Lukas Wunner125450f2018-09-08 09:59:01 +0200709 struct eeepc_laptop *eeepc;
710 int val;
711
712 eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot);
713 val = get_acpi(eeepc, CM_ASL_WLAN);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000714
715 if (val == 1 || val == 0)
716 *value = val;
717 else
718 return -EINVAL;
719
720 return 0;
721}
722
Lukas Wunner81c4b5b2018-09-08 09:59:01 +0200723static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000724 .get_adapter_status = eeepc_get_adapter_status,
725 .get_power_status = eeepc_get_adapter_status,
726};
727
Alan Jenkins854c7832009-12-03 07:45:09 +0000728static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
Corentin Chary2b121bc2009-06-25 13:25:36 +0200729{
730 int ret = -ENOMEM;
731 struct pci_bus *bus = pci_find_bus(0, 1);
732
733 if (!bus) {
Joe Perches19b53282009-06-25 13:25:37 +0200734 pr_err("Unable to find wifi PCI bus\n");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200735 return -ENODEV;
736 }
737
Lukas Wunner125450f2018-09-08 09:59:01 +0200738 eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200739
Lukas Wunner125450f2018-09-08 09:59:01 +0200740 ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
Corentin Chary2b121bc2009-06-25 13:25:36 +0200741 if (ret) {
Joe Perches19b53282009-06-25 13:25:37 +0200742 pr_err("Unable to register hotplug slot - %d\n", ret);
Corentin Chary2b121bc2009-06-25 13:25:36 +0200743 goto error_register;
744 }
745
746 return 0;
747
748error_register:
Lukas Wunner125450f2018-09-08 09:59:01 +0200749 eeepc->hotplug_slot.ops = NULL;
Corentin Chary2b121bc2009-06-25 13:25:36 +0200750 return ret;
751}
752
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000753/*
754 * Rfkill devices
755 */
756static int eeepc_rfkill_set(void *data, bool blocked)
757{
Alan Jenkins854c7832009-12-03 07:45:09 +0000758 acpi_handle handle = data;
759
760 return write_acpi_int(handle, NULL, !blocked);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000761}
762
763static const struct rfkill_ops eeepc_rfkill_ops = {
764 .set_block = eeepc_rfkill_set,
765};
766
Alan Jenkins854c7832009-12-03 07:45:09 +0000767static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
768 struct rfkill **rfkill,
769 const char *name,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000770 enum rfkill_type type, int cm)
771{
Alan Jenkins854c7832009-12-03 07:45:09 +0000772 acpi_handle handle;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000773 int result;
774
Alan Jenkins854c7832009-12-03 07:45:09 +0000775 result = acpi_setter_handle(eeepc, cm, &handle);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000776 if (result < 0)
777 return result;
778
Alan Jenkins854c7832009-12-03 07:45:09 +0000779 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
780 &eeepc_rfkill_ops, handle);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000781
782 if (!*rfkill)
783 return -EINVAL;
784
Alan Jenkins854c7832009-12-03 07:45:09 +0000785 rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000786 result = rfkill_register(*rfkill);
787 if (result) {
788 rfkill_destroy(*rfkill);
789 *rfkill = NULL;
790 return result;
791 }
792 return 0;
793}
794
Frans Klaver792bd2a2014-10-22 21:12:38 +0200795static char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5";
796static char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6";
797static char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7";
798
Alan Jenkins854c7832009-12-03 07:45:09 +0000799static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000800{
Frans Klaver792bd2a2014-10-22 21:12:38 +0200801 eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
802 eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
803 eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000804 if (eeepc->wlan_rfkill) {
805 rfkill_unregister(eeepc->wlan_rfkill);
806 rfkill_destroy(eeepc->wlan_rfkill);
807 eeepc->wlan_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000808 }
Matthew Garrett14fdb152011-05-09 10:44:01 -0400809
Lukas Wunner125450f2018-09-08 09:59:01 +0200810 if (eeepc->hotplug_slot.ops)
811 pci_hp_deregister(&eeepc->hotplug_slot);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000812
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000813 if (eeepc->bluetooth_rfkill) {
814 rfkill_unregister(eeepc->bluetooth_rfkill);
815 rfkill_destroy(eeepc->bluetooth_rfkill);
816 eeepc->bluetooth_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000817 }
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000818 if (eeepc->wwan3g_rfkill) {
819 rfkill_unregister(eeepc->wwan3g_rfkill);
820 rfkill_destroy(eeepc->wwan3g_rfkill);
821 eeepc->wwan3g_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000822 }
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000823 if (eeepc->wimax_rfkill) {
824 rfkill_unregister(eeepc->wimax_rfkill);
825 rfkill_destroy(eeepc->wimax_rfkill);
826 eeepc->wimax_rfkill = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000827 }
828}
829
Alan Jenkins854c7832009-12-03 07:45:09 +0000830static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000831{
832 int result = 0;
833
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000834 mutex_init(&eeepc->hotplug_lock);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000835
Alan Jenkins854c7832009-12-03 07:45:09 +0000836 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
837 "eeepc-wlan", RFKILL_TYPE_WLAN,
838 CM_ASL_WLAN);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000839
840 if (result && result != -ENODEV)
841 goto exit;
842
Alan Jenkins854c7832009-12-03 07:45:09 +0000843 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
844 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
845 CM_ASL_BLUETOOTH);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000846
847 if (result && result != -ENODEV)
848 goto exit;
849
Alan Jenkins854c7832009-12-03 07:45:09 +0000850 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
851 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
852 CM_ASL_3G);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000853
854 if (result && result != -ENODEV)
855 goto exit;
856
Alan Jenkins854c7832009-12-03 07:45:09 +0000857 result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
858 "eeepc-wimax", RFKILL_TYPE_WIMAX,
859 CM_ASL_WIMAX);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000860
861 if (result && result != -ENODEV)
862 goto exit;
863
Corentin Chary10ae4b52010-01-06 22:07:38 +0100864 if (eeepc->hotplug_disabled)
865 return 0;
866
Alan Jenkins854c7832009-12-03 07:45:09 +0000867 result = eeepc_setup_pci_hotplug(eeepc);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000868 /*
869 * If we get -EBUSY then something else is handling the PCI hotplug -
870 * don't fail in this case
871 */
872 if (result == -EBUSY)
873 result = 0;
874
Frans Klaver792bd2a2014-10-22 21:12:38 +0200875 eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
876 eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
877 eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000878
879exit:
880 if (result && result != -ENODEV)
Alan Jenkins854c7832009-12-03 07:45:09 +0000881 eeepc_rfkill_exit(eeepc);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000882 return result;
883}
884
885/*
886 * Platform driver - hibernate/resume callbacks
887 */
Alan Jenkinsc200da52009-08-28 12:56:40 +0000888static int eeepc_hotk_thaw(struct device *device)
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100889{
Alan Jenkins854c7832009-12-03 07:45:09 +0000890 struct eeepc_laptop *eeepc = dev_get_drvdata(device);
891
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000892 if (eeepc->wlan_rfkill) {
Frans Klaverefef8722014-10-22 21:12:43 +0200893 int wlan;
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100894
Alan Jenkinsc1edd992009-08-28 12:56:39 +0000895 /*
896 * Work around bios bug - acpi _PTS turns off the wireless led
897 * during suspend. Normally it restores it on resume, but
Alan Jenkinsc200da52009-08-28 12:56:40 +0000898 * we should kick it ourselves in case hibernation is aborted.
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100899 */
Alan Jenkins854c7832009-12-03 07:45:09 +0000900 wlan = get_acpi(eeepc, CM_ASL_WLAN);
Frans Klaverefef8722014-10-22 21:12:43 +0200901 if (wlan >= 0)
902 set_acpi(eeepc, CM_ASL_WLAN, wlan);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100903 }
904
Alan Jenkinsc200da52009-08-28 12:56:40 +0000905 return 0;
906}
907
908static int eeepc_hotk_restore(struct device *device)
909{
Alan Jenkins854c7832009-12-03 07:45:09 +0000910 struct eeepc_laptop *eeepc = dev_get_drvdata(device);
Alan Jenkinsc200da52009-08-28 12:56:40 +0000911
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100912 /* Refresh both wlan rfkill state and pci hotplug */
Matthew Garrett14fdb152011-05-09 10:44:01 -0400913 if (eeepc->wlan_rfkill) {
Frans Klaver792bd2a2014-10-22 21:12:38 +0200914 eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1);
915 eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2);
916 eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3);
Matthew Garrett14fdb152011-05-09 10:44:01 -0400917 }
Corentin Charye1faa9d2008-03-13 12:57:18 +0100918
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000919 if (eeepc->bluetooth_rfkill)
920 rfkill_set_sw_state(eeepc->bluetooth_rfkill,
Alan Jenkins854c7832009-12-03 07:45:09 +0000921 get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000922 if (eeepc->wwan3g_rfkill)
923 rfkill_set_sw_state(eeepc->wwan3g_rfkill,
Alan Jenkins854c7832009-12-03 07:45:09 +0000924 get_acpi(eeepc, CM_ASL_3G) != 1);
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000925 if (eeepc->wimax_rfkill)
926 rfkill_set_sw_state(eeepc->wimax_rfkill,
Alan Jenkins854c7832009-12-03 07:45:09 +0000927 get_acpi(eeepc, CM_ASL_WIMAX) != 1);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100928
929 return 0;
930}
931
Len Brown9a3bff22009-12-15 22:34:48 -0500932static const struct dev_pm_ops eeepc_pm_ops = {
Alan Jenkins854c7832009-12-03 07:45:09 +0000933 .thaw = eeepc_hotk_thaw,
934 .restore = eeepc_hotk_restore,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000935};
936
937static struct platform_driver platform_driver = {
938 .driver = {
Alan Jenkinsa7624b62009-12-03 07:45:08 +0000939 .name = EEEPC_LAPTOP_FILE,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000940 .pm = &eeepc_pm_ops,
941 }
942};
943
Eric Coopere59f8792008-03-13 12:55:46 +0100944/*
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000945 * Hwmon device
Eric Coopere59f8792008-03-13 12:55:46 +0100946 */
Alan Jenkins52bbe3c2009-12-03 07:45:07 +0000947
948#define EEEPC_EC_SC00 0x61
949#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
950#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
951#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
952
953#define EEEPC_EC_SFB0 0xD0
954#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */
955
Frans Klaver148a5dd2014-10-22 21:12:42 +0200956static inline int eeepc_pwm_to_lmsensors(int value)
957{
958 return value * 255 / 100;
959}
960
961static inline int eeepc_lmsensors_to_pwm(int value)
962{
963 value = clamp_val(value, 0, 255);
964 return value * 100 / 255;
965}
966
Corentin Charye1faa9d2008-03-13 12:57:18 +0100967static int eeepc_get_fan_pwm(void)
968{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000969 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100970
Alan Jenkins463b4e42009-12-03 07:45:03 +0000971 ec_read(EEEPC_EC_FAN_PWM, &value);
Frans Klaver148a5dd2014-10-22 21:12:42 +0200972 return eeepc_pwm_to_lmsensors(value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100973}
974
975static void eeepc_set_fan_pwm(int value)
976{
Frans Klaver148a5dd2014-10-22 21:12:42 +0200977 value = eeepc_lmsensors_to_pwm(value);
Alan Jenkins463b4e42009-12-03 07:45:03 +0000978 ec_write(EEEPC_EC_FAN_PWM, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100979}
980
981static int eeepc_get_fan_rpm(void)
982{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000983 u8 high = 0;
984 u8 low = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100985
Alan Jenkins463b4e42009-12-03 07:45:03 +0000986 ec_read(EEEPC_EC_FAN_HRPM, &high);
987 ec_read(EEEPC_EC_FAN_LRPM, &low);
988 return high << 8 | low;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100989}
990
Frans Klavera5de6812014-10-22 21:12:41 +0200991#define EEEPC_EC_FAN_CTRL_BIT 0x02
992#define EEEPC_FAN_CTRL_MANUAL 1
993#define EEEPC_FAN_CTRL_AUTO 2
994
Corentin Charye1faa9d2008-03-13 12:57:18 +0100995static int eeepc_get_fan_ctrl(void)
996{
Alan Jenkins463b4e42009-12-03 07:45:03 +0000997 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100998
Alan Jenkins463b4e42009-12-03 07:45:03 +0000999 ec_read(EEEPC_EC_FAN_CTRL, &value);
Frans Klavera5de6812014-10-22 21:12:41 +02001000 if (value & EEEPC_EC_FAN_CTRL_BIT)
1001 return EEEPC_FAN_CTRL_MANUAL;
Alan Jenkins48718682009-12-03 07:44:56 +00001002 else
Frans Klavera5de6812014-10-22 21:12:41 +02001003 return EEEPC_FAN_CTRL_AUTO;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001004}
1005
1006static void eeepc_set_fan_ctrl(int manual)
1007{
Alan Jenkins463b4e42009-12-03 07:45:03 +00001008 u8 value = 0;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001009
Alan Jenkins463b4e42009-12-03 07:45:03 +00001010 ec_read(EEEPC_EC_FAN_CTRL, &value);
Frans Klavera5de6812014-10-22 21:12:41 +02001011 if (manual == EEEPC_FAN_CTRL_MANUAL)
1012 value |= EEEPC_EC_FAN_CTRL_BIT;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001013 else
Frans Klavera5de6812014-10-22 21:12:41 +02001014 value &= ~EEEPC_EC_FAN_CTRL_BIT;
Alan Jenkins463b4e42009-12-03 07:45:03 +00001015 ec_write(EEEPC_EC_FAN_CTRL, value);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001016}
1017
1018static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
1019{
1020 int rv, value;
1021
Paul Bolle95369a72014-09-17 21:02:51 +02001022 rv = parse_arg(buf, &value);
1023 if (rv < 0)
1024 return rv;
1025 set(value);
1026 return count;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001027}
1028
1029static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
1030{
1031 return sprintf(buf, "%d\n", get());
1032}
1033
Frans Klaver28ac85f2014-09-17 23:47:24 +02001034#define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \
Frans Klaver48d4a5b2014-09-17 23:47:25 +02001035 static ssize_t _name##_show(struct device *dev, \
Corentin Charye1faa9d2008-03-13 12:57:18 +01001036 struct device_attribute *attr, \
1037 char *buf) \
1038 { \
Paul Bolle1ec9d392014-06-17 14:06:30 +02001039 return show_sys_hwmon(_get, buf); \
Frans Klaver28ac85f2014-09-17 23:47:24 +02001040 }
1041
1042#define EEEPC_SENSOR_STORE_FUNC(_name, _set) \
Frans Klaver48d4a5b2014-09-17 23:47:25 +02001043 static ssize_t _name##_store(struct device *dev, \
Corentin Charye1faa9d2008-03-13 12:57:18 +01001044 struct device_attribute *attr, \
1045 const char *buf, size_t count) \
1046 { \
Paul Bolle1ec9d392014-06-17 14:06:30 +02001047 return store_sys_hwmon(_set, buf, count); \
Frans Klaver28ac85f2014-09-17 23:47:24 +02001048 }
1049
Frans Klaver48d4a5b2014-09-17 23:47:25 +02001050#define EEEPC_CREATE_SENSOR_ATTR_RW(_name, _get, _set) \
Frans Klaver28ac85f2014-09-17 23:47:24 +02001051 EEEPC_SENSOR_SHOW_FUNC(_name, _get) \
1052 EEEPC_SENSOR_STORE_FUNC(_name, _set) \
Frans Klaver48d4a5b2014-09-17 23:47:25 +02001053 static DEVICE_ATTR_RW(_name)
Corentin Charye1faa9d2008-03-13 12:57:18 +01001054
Frans Klaver48d4a5b2014-09-17 23:47:25 +02001055#define EEEPC_CREATE_SENSOR_ATTR_RO(_name, _get) \
1056 EEEPC_SENSOR_SHOW_FUNC(_name, _get) \
1057 static DEVICE_ATTR_RO(_name)
1058
1059EEEPC_CREATE_SENSOR_ATTR_RO(fan1_input, eeepc_get_fan_rpm);
1060EEEPC_CREATE_SENSOR_ATTR_RW(pwm1, eeepc_get_fan_pwm,
1061 eeepc_set_fan_pwm);
1062EEEPC_CREATE_SENSOR_ATTR_RW(pwm1_enable, eeepc_get_fan_ctrl,
1063 eeepc_set_fan_ctrl);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001064
Guenter Roeckf0c34c92013-11-23 11:03:21 -08001065static struct attribute *hwmon_attrs[] = {
1066 &dev_attr_pwm1.attr,
1067 &dev_attr_fan1_input.attr,
1068 &dev_attr_pwm1_enable.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +01001069 NULL
1070};
Guenter Roeckf0c34c92013-11-23 11:03:21 -08001071ATTRIBUTE_GROUPS(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +01001072
Alan Jenkins854c7832009-12-03 07:45:09 +00001073static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
Corentin Chary7de39382009-06-25 13:25:38 +02001074{
Guenter Roeckf0c34c92013-11-23 11:03:21 -08001075 struct device *dev = &eeepc->platform_device->dev;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001076 struct device *hwmon;
Corentin Chary7de39382009-06-25 13:25:38 +02001077
Guenter Roeckf0c34c92013-11-23 11:03:21 -08001078 hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL,
1079 hwmon_groups);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001080 if (IS_ERR(hwmon)) {
1081 pr_err("Could not register eeepc hwmon device\n");
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001082 return PTR_ERR(hwmon);
Corentin Chary7de39382009-06-25 13:25:38 +02001083 }
Guenter Roeckf0c34c92013-11-23 11:03:21 -08001084 return 0;
Corentin Chary7de39382009-06-25 13:25:38 +02001085}
1086
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001087/*
1088 * Backlight device
1089 */
1090static int read_brightness(struct backlight_device *bd)
Corentin Charya5fa4292008-03-13 12:56:37 +01001091{
Alan Jenkins854c7832009-12-03 07:45:09 +00001092 struct eeepc_laptop *eeepc = bl_get_data(bd);
1093
1094 return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001095}
Corentin Charya5fa4292008-03-13 12:56:37 +01001096
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001097static int set_brightness(struct backlight_device *bd, int value)
1098{
Alan Jenkins854c7832009-12-03 07:45:09 +00001099 struct eeepc_laptop *eeepc = bl_get_data(bd);
1100
1101 return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001102}
Corentin Charya5fa4292008-03-13 12:56:37 +01001103
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001104static int update_bl_status(struct backlight_device *bd)
1105{
1106 return set_brightness(bd, bd->props.brightness);
1107}
Corentin Charya9df80c2009-01-20 16:17:40 +01001108
Lionel Debrouxacc24722010-11-16 14:14:02 +01001109static const struct backlight_ops eeepcbl_ops = {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001110 .get_brightness = read_brightness,
1111 .update_status = update_bl_status,
1112};
Eric Coopere59f8792008-03-13 12:55:46 +01001113
Alan Jenkins854c7832009-12-03 07:45:09 +00001114static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001115{
Alan Jenkins854c7832009-12-03 07:45:09 +00001116 struct backlight_device *bd = eeepc->backlight_device;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001117 int old = bd->props.brightness;
Eric Coopere59f8792008-03-13 12:55:46 +01001118
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001119 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
Eric Coopere59f8792008-03-13 12:55:46 +01001120
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001121 return old;
Eric Coopere59f8792008-03-13 12:55:46 +01001122}
1123
Alan Jenkins854c7832009-12-03 07:45:09 +00001124static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
Corentin Charya5fa4292008-03-13 12:56:37 +01001125{
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001126 struct backlight_properties props;
Corentin Charya5fa4292008-03-13 12:56:37 +01001127 struct backlight_device *bd;
1128
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001129 memset(&props, 0, sizeof(struct backlight_properties));
Matthew Garrettbb7ca742011-03-22 16:30:21 -07001130 props.type = BACKLIGHT_PLATFORM;
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001131 props.max_brightness = 15;
Alan Jenkins854c7832009-12-03 07:45:09 +00001132 bd = backlight_device_register(EEEPC_LAPTOP_FILE,
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001133 &eeepc->platform_device->dev, eeepc,
1134 &eeepcbl_ops, &props);
Corentin Charya5fa4292008-03-13 12:56:37 +01001135 if (IS_ERR(bd)) {
Joe Perches19b53282009-06-25 13:25:37 +02001136 pr_err("Could not register eeepc backlight device\n");
Alan Jenkins854c7832009-12-03 07:45:09 +00001137 eeepc->backlight_device = NULL;
Corentin Charya5fa4292008-03-13 12:56:37 +01001138 return PTR_ERR(bd);
1139 }
Alan Jenkins854c7832009-12-03 07:45:09 +00001140 eeepc->backlight_device = bd;
Alan Jenkins854c7832009-12-03 07:45:09 +00001141 bd->props.brightness = read_brightness(bd);
Corentin Charya5fa4292008-03-13 12:56:37 +01001142 bd->props.power = FB_BLANK_UNBLANK;
1143 backlight_update_status(bd);
1144 return 0;
1145}
1146
Alan Jenkins854c7832009-12-03 07:45:09 +00001147static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
Corentin Charye1faa9d2008-03-13 12:57:18 +01001148{
Markus Elfring00981812014-11-24 20:30:29 +01001149 backlight_device_unregister(eeepc->backlight_device);
Alan Jenkins854c7832009-12-03 07:45:09 +00001150 eeepc->backlight_device = NULL;
Corentin Charye1faa9d2008-03-13 12:57:18 +01001151}
1152
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001153
1154/*
1155 * Input device (i.e. hotkeys)
1156 */
Alan Jenkins854c7832009-12-03 07:45:09 +00001157static int eeepc_input_init(struct eeepc_laptop *eeepc)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001158{
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001159 struct input_dev *input;
1160 int error;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001161
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001162 input = input_allocate_device();
Joe Perchesb222cca2013-10-23 12:14:52 -07001163 if (!input)
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001164 return -ENOMEM;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001165
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001166 input->name = "Asus EeePC extra buttons";
1167 input->phys = EEEPC_LAPTOP_FILE "/input0";
1168 input->id.bustype = BUS_HOST;
1169 input->dev.parent = &eeepc->platform_device->dev;
1170
1171 error = sparse_keymap_setup(input, eeepc_keymap, NULL);
1172 if (error) {
1173 pr_err("Unable to setup input device keymap\n");
1174 goto err_free_dev;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001175 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001176
1177 error = input_register_device(input);
1178 if (error) {
1179 pr_err("Unable to register input device\n");
Michał Kępieńde3c91c2017-03-09 13:11:42 +01001180 goto err_free_dev;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001181 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001182
1183 eeepc->inputdev = input;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001184 return 0;
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001185
Corentin Chary71e687d2010-08-24 09:30:44 +02001186err_free_dev:
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001187 input_free_device(input);
1188 return error;
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001189}
1190
Alan Jenkins854c7832009-12-03 07:45:09 +00001191static void eeepc_input_exit(struct eeepc_laptop *eeepc)
Eric Coopere59f8792008-03-13 12:55:46 +01001192{
Michał Kępieńde3c91c2017-03-09 13:11:42 +01001193 if (eeepc->inputdev)
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001194 input_unregister_device(eeepc->inputdev);
Corentin Chary71e687d2010-08-24 09:30:44 +02001195 eeepc->inputdev = NULL;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001196}
Corentin Chary3c0eb512009-12-03 07:44:52 +00001197
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001198/*
1199 * ACPI driver
1200 */
Corentin Charyce6c4682011-12-15 08:27:32 +01001201static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
1202{
1203 if (!eeepc->inputdev)
Frans Klaverfb1d97a2014-11-02 22:14:58 +01001204 return;
Corentin Charyce6c4682011-12-15 08:27:32 +01001205 if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))
1206 pr_info("Unknown key %x pressed\n", event);
1207}
1208
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001209static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001210{
Alan Jenkins854c7832009-12-03 07:45:09 +00001211 struct eeepc_laptop *eeepc = acpi_driver_data(device);
Frans Klaverfb1d97a2014-11-02 22:14:58 +01001212 int old_brightness, new_brightness;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001213 u16 count;
Corentin Chary3c0eb512009-12-03 07:44:52 +00001214
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001215 if (event > ACPI_MAX_SYS_NOTIFY)
1216 return;
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001217 count = eeepc->event_count[event % 128]++;
Alan Jenkins854c7832009-12-03 07:45:09 +00001218 acpi_bus_generate_netlink_event(device->pnp.device_class,
1219 dev_name(&device->dev), event,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001220 count);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001221
Alan Jenkins325fb8e2009-12-03 07:45:15 +00001222 /* Brightness events are special */
Frans Klaverfb1d97a2014-11-02 22:14:58 +01001223 if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) {
Corentin Charyce6c4682011-12-15 08:27:32 +01001224 eeepc_input_notify(eeepc, event);
Frans Klaverfb1d97a2014-11-02 22:14:58 +01001225 return;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001226 }
Frans Klaverfb1d97a2014-11-02 22:14:58 +01001227
1228 /* Ignore them completely if the acpi video driver is used */
1229 if (!eeepc->backlight_device)
1230 return;
1231
1232 /* Update the backlight device. */
1233 old_brightness = eeepc_backlight_notify(eeepc);
1234
1235 /* Convert event to keypress (obsolescent hack) */
1236 new_brightness = event - NOTIFY_BRN_MIN;
1237
1238 if (new_brightness < old_brightness) {
1239 event = NOTIFY_BRN_MIN; /* brightness down */
1240 } else if (new_brightness > old_brightness) {
1241 event = NOTIFY_BRN_MAX; /* brightness up */
1242 } else {
1243 /*
1244 * no change in brightness - already at min/max,
1245 * event will be desired value (or else ignored)
1246 */
1247 }
1248 eeepc_input_notify(eeepc, event);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001249}
1250
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001251static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
1252{
1253 const char *model;
1254
Corentin Chary10ae4b52010-01-06 22:07:38 +01001255 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1256 if (!model)
1257 return;
1258
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001259 /*
1260 * Blacklist for setting cpufv (cpu speed).
1261 *
1262 * EeePC 4G ("701") implements CFVS, but it is not supported
1263 * by the pre-installed OS, and the original option to change it
1264 * in the BIOS setup screen was removed in later versions.
1265 *
1266 * Judging by the lack of "Super Hybrid Engine" on Asus product pages,
1267 * this applies to all "701" models (4G/4G Surf/2G Surf).
1268 *
1269 * So Asus made a deliberate decision not to support it on this model.
1270 * We have several reports that using it can cause the system to hang
1271 *
1272 * The hang has also been reported on a "702" (Model name "8G"?).
1273 *
1274 * We avoid dmi_check_system() / dmi_match(), because they use
1275 * substring matching. We don't want to affect the "701SD"
1276 * and "701SDX" models, because they do support S.H.E.
1277 */
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001278 if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
1279 eeepc->cpufv_disabled = true;
Frans Klaver9f662b22014-10-22 21:12:37 +02001280 pr_info("model %s does not officially support setting cpu speed\n",
1281 model);
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001282 pr_info("cpufv disabled to avoid instability\n");
1283 }
Corentin Chary10ae4b52010-01-06 22:07:38 +01001284
1285 /*
1286 * Blacklist for wlan hotplug
1287 *
1288 * Eeepc 1005HA doesn't work like others models and don't need the
1289 * hotplug code. In fact, current hotplug code seems to unplug another
1290 * device...
1291 */
Alan Jenkinsced69c52010-02-20 11:02:24 +00001292 if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||
1293 strcmp(model, "1005PE") == 0) {
Corentin Chary10ae4b52010-01-06 22:07:38 +01001294 eeepc->hotplug_disabled = true;
1295 pr_info("wlan hotplug disabled\n");
1296 }
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001297}
1298
Alan Jenkins854c7832009-12-03 07:45:09 +00001299static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001300{
1301 int dummy;
1302
Lucas De Marchic8440332011-03-17 17:18:22 -03001303 /* Some BIOSes do not report cm although it is available.
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001304 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001305 if (!(eeepc->cm_supported & (1 << cm))
1306 && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
Frans Klaver9f662b22014-10-22 21:12:37 +02001307 pr_info("%s (%x) not reported by BIOS, enabling anyway\n",
1308 name, 1 << cm);
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001309 eeepc->cm_supported |= 1 << cm;
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001310 }
1311}
1312
Alan Jenkins854c7832009-12-03 07:45:09 +00001313static void cmsg_quirks(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001314{
Alan Jenkins854c7832009-12-03 07:45:09 +00001315 cmsg_quirk(eeepc, CM_ASL_LID, "LID");
1316 cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");
1317 cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");
1318 cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001319}
1320
Greg Kroah-Hartmanb859f152012-12-21 13:18:33 -08001321static int eeepc_acpi_init(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001322{
1323 unsigned int init_flags;
Eric Coopere59f8792008-03-13 12:55:46 +01001324 int result;
1325
Corentin Chary71e687d2010-08-24 09:30:44 +02001326 result = acpi_bus_get_status(eeepc->device);
Alan Jenkins1e779852009-08-28 12:56:35 +00001327 if (result)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001328 return result;
Corentin Chary71e687d2010-08-24 09:30:44 +02001329 if (!eeepc->device->status.present) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001330 pr_err("Hotkey device not present, aborting\n");
1331 return -ENODEV;
Eric Coopere59f8792008-03-13 12:55:46 +01001332 }
Corentin Chary7de39382009-06-25 13:25:38 +02001333
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001334 init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1335 pr_notice("Hotkey init flags 0x%x\n", init_flags);
1336
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001337 if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001338 pr_err("Hotkey initialization failed\n");
1339 return -ENODEV;
1340 }
1341
1342 /* get control methods supported */
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001343 if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001344 pr_err("Get control methods supported failed\n");
1345 return -ENODEV;
1346 }
Alan Jenkins854c7832009-12-03 07:45:09 +00001347 cmsg_quirks(eeepc);
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001348 pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001349
Corentin Chary3c0eb512009-12-03 07:44:52 +00001350 return 0;
1351}
1352
Greg Kroah-Hartmanb859f152012-12-21 13:18:33 -08001353static void eeepc_enable_camera(struct eeepc_laptop *eeepc)
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001354{
1355 /*
1356 * If the following call to set_acpi() fails, it's because there's no
1357 * camera so we can ignore the error.
1358 */
Alan Jenkins854c7832009-12-03 07:45:09 +00001359 if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
1360 set_acpi(eeepc, CM_ASL_CAMERA, 1);
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001361}
1362
Alan Jenkins854c7832009-12-03 07:45:09 +00001363static bool eeepc_device_present;
1364
Greg Kroah-Hartmanb859f152012-12-21 13:18:33 -08001365static int eeepc_acpi_add(struct acpi_device *device)
Eric Coopere59f8792008-03-13 12:55:46 +01001366{
Alan Jenkins854c7832009-12-03 07:45:09 +00001367 struct eeepc_laptop *eeepc;
Eric Coopere59f8792008-03-13 12:55:46 +01001368 int result;
1369
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001370 pr_notice(EEEPC_LAPTOP_NAME "\n");
1371 eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
1372 if (!eeepc)
Eric Coopere59f8792008-03-13 12:55:46 +01001373 return -ENOMEM;
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001374 eeepc->handle = device->handle;
1375 strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
1376 strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
1377 device->driver_data = eeepc;
Corentin Chary71e687d2010-08-24 09:30:44 +02001378 eeepc->device = device;
Eric Coopere59f8792008-03-13 12:55:46 +01001379
Corentin Chary322a1352010-01-06 22:07:40 +01001380 eeepc->hotplug_disabled = hotplug_disabled;
1381
Alan Jenkinsda8ba012010-01-06 22:07:37 +01001382 eeepc_dmi_check(eeepc);
1383
Corentin Chary71e687d2010-08-24 09:30:44 +02001384 result = eeepc_acpi_init(eeepc);
Eric Coopere59f8792008-03-13 12:55:46 +01001385 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +00001386 goto fail_platform;
Alan Jenkins854c7832009-12-03 07:45:09 +00001387 eeepc_enable_camera(eeepc);
Eric Coopere59f8792008-03-13 12:55:46 +01001388
Alan Jenkins854c7832009-12-03 07:45:09 +00001389 /*
1390 * Register the platform device first. It is used as a parent for the
1391 * sub-devices below.
1392 *
1393 * Note that if there are multiple instances of this ACPI device it
1394 * will bail out, because the platform device is registered with a
1395 * fixed name. Of course it doesn't make sense to have more than one,
1396 * and machine-specific scripts find the fixed name convenient. But
1397 * It's also good for us to exclude multiple instances because both
1398 * our hwmon and our wlan rfkill subdevice use global ACPI objects
1399 * (the EC and the wlan PCI slot respectively).
1400 */
1401 result = eeepc_platform_init(eeepc);
Eric Coopere59f8792008-03-13 12:55:46 +01001402 if (result)
Alan Jenkins9db106b2009-12-03 07:45:06 +00001403 goto fail_platform;
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001404
Hans de Goede21db4b12015-06-16 16:28:02 +02001405 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
Alan Jenkins854c7832009-12-03 07:45:09 +00001406 result = eeepc_backlight_init(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001407 if (result)
1408 goto fail_backlight;
Frans Klaver39a3e172014-09-17 23:47:19 +02001409 }
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001410
Alan Jenkins854c7832009-12-03 07:45:09 +00001411 result = eeepc_input_init(eeepc);
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001412 if (result)
1413 goto fail_input;
1414
Alan Jenkins854c7832009-12-03 07:45:09 +00001415 result = eeepc_hwmon_init(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001416 if (result)
1417 goto fail_hwmon;
1418
Alan Jenkins854c7832009-12-03 07:45:09 +00001419 result = eeepc_led_init(eeepc);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001420 if (result)
1421 goto fail_led;
1422
Alan Jenkins854c7832009-12-03 07:45:09 +00001423 result = eeepc_rfkill_init(eeepc);
Corentin Chary7de39382009-06-25 13:25:38 +02001424 if (result)
1425 goto fail_rfkill;
1426
Alan Jenkins854c7832009-12-03 07:45:09 +00001427 eeepc_device_present = true;
Eric Coopere59f8792008-03-13 12:55:46 +01001428 return 0;
Alan Jenkins1e779852009-08-28 12:56:35 +00001429
Corentin Chary7de39382009-06-25 13:25:38 +02001430fail_rfkill:
Alan Jenkins854c7832009-12-03 07:45:09 +00001431 eeepc_led_exit(eeepc);
Corentin Chary3c0eb512009-12-03 07:44:52 +00001432fail_led:
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001433fail_hwmon:
Alan Jenkins854c7832009-12-03 07:45:09 +00001434 eeepc_input_exit(eeepc);
Alan Jenkinsf2a9d5e2009-08-28 12:56:36 +00001435fail_input:
Alan Jenkins854c7832009-12-03 07:45:09 +00001436 eeepc_backlight_exit(eeepc);
Corentin Chary1ddec2f2009-06-25 13:25:39 +02001437fail_backlight:
Alan Jenkins854c7832009-12-03 07:45:09 +00001438 eeepc_platform_exit(eeepc);
Alan Jenkins9db106b2009-12-03 07:45:06 +00001439fail_platform:
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001440 kfree(eeepc);
Alan Jenkins1e779852009-08-28 12:56:35 +00001441
Eric Coopere59f8792008-03-13 12:55:46 +01001442 return result;
1443}
1444
Rafael J. Wysocki51fac832013-01-24 00:24:48 +01001445static int eeepc_acpi_remove(struct acpi_device *device)
Alan Jenkins1e779852009-08-28 12:56:35 +00001446{
Alan Jenkins854c7832009-12-03 07:45:09 +00001447 struct eeepc_laptop *eeepc = acpi_driver_data(device);
Alan Jenkins1e779852009-08-28 12:56:35 +00001448
Alan Jenkins854c7832009-12-03 07:45:09 +00001449 eeepc_backlight_exit(eeepc);
1450 eeepc_rfkill_exit(eeepc);
1451 eeepc_input_exit(eeepc);
Alan Jenkins854c7832009-12-03 07:45:09 +00001452 eeepc_led_exit(eeepc);
1453 eeepc_platform_exit(eeepc);
Alan Jenkins1e779852009-08-28 12:56:35 +00001454
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001455 kfree(eeepc);
Alan Jenkins1e779852009-08-28 12:56:35 +00001456 return 0;
1457}
1458
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001459
1460static const struct acpi_device_id eeepc_device_ids[] = {
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001461 {EEEPC_ACPI_HID, 0},
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001462 {"", 0},
1463};
1464MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
1465
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001466static struct acpi_driver eeepc_acpi_driver = {
1467 .name = EEEPC_LAPTOP_NAME,
1468 .class = EEEPC_ACPI_CLASS,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001469 .owner = THIS_MODULE,
1470 .ids = eeepc_device_ids,
1471 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1472 .ops = {
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001473 .add = eeepc_acpi_add,
1474 .remove = eeepc_acpi_remove,
1475 .notify = eeepc_acpi_notify,
Alan Jenkins52bbe3c2009-12-03 07:45:07 +00001476 },
1477};
1478
1479
Alan Jenkins1e779852009-08-28 12:56:35 +00001480static int __init eeepc_laptop_init(void)
1481{
1482 int result;
1483
Alan Jenkins22072e92009-12-03 07:45:05 +00001484 result = platform_driver_register(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001485 if (result < 0)
1486 return result;
Alan Jenkins22072e92009-12-03 07:45:05 +00001487
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001488 result = acpi_bus_register_driver(&eeepc_acpi_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001489 if (result < 0)
1490 goto fail_acpi_driver;
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001491
Alan Jenkins854c7832009-12-03 07:45:09 +00001492 if (!eeepc_device_present) {
Alan Jenkins22072e92009-12-03 07:45:05 +00001493 result = -ENODEV;
1494 goto fail_no_device;
Alan Jenkins1e779852009-08-28 12:56:35 +00001495 }
Dmitry Torokhov642e0442010-01-06 22:07:39 +01001496
Alan Jenkins1e779852009-08-28 12:56:35 +00001497 return 0;
Alan Jenkins22072e92009-12-03 07:45:05 +00001498
1499fail_no_device:
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001500 acpi_bus_unregister_driver(&eeepc_acpi_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001501fail_acpi_driver:
1502 platform_driver_unregister(&platform_driver);
1503 return result;
Alan Jenkins1e779852009-08-28 12:56:35 +00001504}
1505
1506static void __exit eeepc_laptop_exit(void)
1507{
Alan Jenkinsa7624b62009-12-03 07:45:08 +00001508 acpi_bus_unregister_driver(&eeepc_acpi_driver);
Alan Jenkins22072e92009-12-03 07:45:05 +00001509 platform_driver_unregister(&platform_driver);
Alan Jenkins1e779852009-08-28 12:56:35 +00001510}
1511
Eric Coopere59f8792008-03-13 12:55:46 +01001512module_init(eeepc_laptop_init);
1513module_exit(eeepc_laptop_exit);