eeepc-laptop.c 30.7 KB
Newer Older
E
Eric Cooper 已提交
1
/*
A
Alan Jenkins 已提交
2
 *  eeepc-laptop.c - Asus Eee PC extras
E
Eric Cooper 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 *  Based on asus_acpi.c as patched for the Eee PC by Asus:
 *  ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
 *  Based on eee.c from eeepc-linux
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

19 20
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

E
Eric Cooper 已提交
21 22 23 24 25
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/platform_device.h>
C
Corentin Chary 已提交
26 27
#include <linux/backlight.h>
#include <linux/fb.h>
28 29
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
E
Eric Cooper 已提交
30 31 32
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/uaccess.h>
33 34
#include <linux/input.h>
#include <linux/rfkill.h>
35
#include <linux/pci.h>
36
#include <linux/pci_hotplug.h>
C
Corentin Chary 已提交
37
#include <linux/leds.h>
E
Eric Cooper 已提交
38 39

#define EEEPC_LAPTOP_VERSION	"0.1"
A
Alan Jenkins 已提交
40 41
#define EEEPC_LAPTOP_NAME	"Eee PC Hotkey Driver"
#define EEEPC_LAPTOP_FILE	"eeepc"
E
Eric Cooper 已提交
42

A
Alan Jenkins 已提交
43 44 45
#define EEEPC_ACPI_CLASS	"hotkey"
#define EEEPC_ACPI_DEVICE_NAME	"Hotkey"
#define EEEPC_ACPI_HID		"ASUS010"
E
Eric Cooper 已提交
46

A
Alan Jenkins 已提交
47
MODULE_AUTHOR("Corentin Chary, Eric Cooper");
A
Alan Jenkins 已提交
48
MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
A
Alan Jenkins 已提交
49
MODULE_LICENSE("GPL");
E
Eric Cooper 已提交
50 51 52 53

/*
 * Definitions for Asus EeePC
 */
C
Corentin Chary 已提交
54 55
#define NOTIFY_BRN_MIN	0x20
#define NOTIFY_BRN_MAX	0x2f
E
Eric Cooper 已提交
56 57 58 59 60 61 62 63 64 65

enum {
	DISABLE_ASL_WLAN = 0x0001,
	DISABLE_ASL_BLUETOOTH = 0x0002,
	DISABLE_ASL_IRDA = 0x0004,
	DISABLE_ASL_CAMERA = 0x0008,
	DISABLE_ASL_TV = 0x0010,
	DISABLE_ASL_GPS = 0x0020,
	DISABLE_ASL_DISPLAYSWITCH = 0x0040,
	DISABLE_ASL_MODEM = 0x0080,
66 67 68 69
	DISABLE_ASL_CARDREADER = 0x0100,
	DISABLE_ASL_3G = 0x0200,
	DISABLE_ASL_WIMAX = 0x0400,
	DISABLE_ASL_HWCF = 0x0800
E
Eric Cooper 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
};

enum {
	CM_ASL_WLAN = 0,
	CM_ASL_BLUETOOTH,
	CM_ASL_IRDA,
	CM_ASL_1394,
	CM_ASL_CAMERA,
	CM_ASL_TV,
	CM_ASL_GPS,
	CM_ASL_DVDROM,
	CM_ASL_DISPLAYSWITCH,
	CM_ASL_PANELBRIGHT,
	CM_ASL_BIOSFLASH,
	CM_ASL_ACPIFLASH,
	CM_ASL_CPUFV,
	CM_ASL_CPUTEMPERATURE,
	CM_ASL_FANCPU,
	CM_ASL_FANCHASSIS,
	CM_ASL_USBPORT1,
	CM_ASL_USBPORT2,
	CM_ASL_USBPORT3,
	CM_ASL_MODEM,
	CM_ASL_CARDREADER,
94 95 96 97 98 99 100
	CM_ASL_3G,
	CM_ASL_WIMAX,
	CM_ASL_HWCF,
	CM_ASL_LID,
	CM_ASL_TYPE,
	CM_ASL_PANELPOWER,	/*P901*/
	CM_ASL_TPD
E
Eric Cooper 已提交
101 102
};

A
Adrian Bunk 已提交
103
static const char *cm_getv[] = {
104
	"WLDG", "BTHG", NULL, NULL,
E
Eric Cooper 已提交
105 106 107 108
	"CAMG", NULL, NULL, NULL,
	NULL, "PBLG", NULL, NULL,
	"CFVG", NULL, NULL, NULL,
	"USBG", NULL, NULL, "MODG",
109 110
	"CRDG", "M3GG", "WIMG", "HWCF",
	"LIDG",	"TYPE", "PBPG",	"TPDG"
E
Eric Cooper 已提交
111 112
};

A
Adrian Bunk 已提交
113
static const char *cm_setv[] = {
114
	"WLDS", "BTHS", NULL, NULL,
E
Eric Cooper 已提交
115 116 117 118
	"CAMS", NULL, NULL, NULL,
	"SDSP", "PBLS", "HDPS", NULL,
	"CFVS", NULL, NULL, NULL,
	"USBG", NULL, NULL, "MODS",
119 120
	"CRDS", "M3GS", "WIMS", NULL,
	NULL, NULL, "PBPS", "TPDS"
E
Eric Cooper 已提交
121 122
};

123 124 125 126 127 128 129 130 131 132 133
struct key_entry {
	char type;
	u8 code;
	u16 keycode;
};

enum { KE_KEY, KE_END };

static struct key_entry eeepc_keymap[] = {
	/* Sleep already handled via generic ACPI code */
	{KE_KEY, 0x10, KEY_WLAN },
134
	{KE_KEY, 0x11, KEY_WLAN },
135 136 137 138
	{KE_KEY, 0x12, KEY_PROG1 },
	{KE_KEY, 0x13, KEY_MUTE },
	{KE_KEY, 0x14, KEY_VOLUMEDOWN },
	{KE_KEY, 0x15, KEY_VOLUMEUP },
139 140 141 142
	{KE_KEY, 0x1a, KEY_COFFEE },
	{KE_KEY, 0x1b, KEY_ZOOM },
	{KE_KEY, 0x1c, KEY_PROG2 },
	{KE_KEY, 0x1d, KEY_PROG3 },
A
Alan Jenkins 已提交
143 144
	{KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
	{KE_KEY, NOTIFY_BRN_MAX, KEY_BRIGHTNESSUP },
145 146 147 148 149 150
	{KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
	{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
	{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
	{KE_END, 0},
};

A
Alan Jenkins 已提交
151

E
Eric Cooper 已提交
152
/*
A
Alan Jenkins 已提交
153
 * This is the main structure, we can use it to store useful information
E
Eric Cooper 已提交
154
 */
A
Alan Jenkins 已提交
155
struct eeepc_laptop {
A
Alan Jenkins 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168
	struct acpi_device *device;	/* the device we are in */
	acpi_handle handle;		/* the handle of the hotk device */
	u32 cm_supported;		/* the control methods supported
					   by this BIOS */
	u16 event_count[128];		/* count for each event */
	struct input_dev *inputdev;
	u16 *keycode_map;
	struct rfkill *wlan_rfkill;
	struct rfkill *bluetooth_rfkill;
	struct rfkill *wwan3g_rfkill;
	struct rfkill *wimax_rfkill;
	struct hotplug_slot *hotplug_slot;
	struct mutex hotplug_lock;
E
Eric Cooper 已提交
169 170
};

A
Alan Jenkins 已提交
171
/* The actual device the driver binds to */
A
Alan Jenkins 已提交
172
static struct eeepc_laptop *eeepc;
173

A
Alan Jenkins 已提交
174 175
/* The platform device */
static struct platform_device *platform_device;
176

C
Corentin Chary 已提交
177 178 179
/* The backlight device /sys/class/backlight */
static struct backlight_device *eeepc_backlight_device;

180 181 182
/* The hwmon device */
static struct device *eeepc_hwmon_device;

E
Eric Cooper 已提交
183 184 185 186

/*
 * ACPI Helpers
 */
187
static int write_acpi_int(acpi_handle handle, const char *method, int val)
E
Eric Cooper 已提交
188 189 190 191 192 193 194 195 196 197
{
	struct acpi_object_list params;
	union acpi_object in_obj;
	acpi_status status;

	params.count = 1;
	params.pointer = &in_obj;
	in_obj.type = ACPI_TYPE_INTEGER;
	in_obj.integer.value = val;

198
	status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
E
Eric Cooper 已提交
199 200 201 202 203 204
	return (status == AE_OK ? 0 : -1);
}

static int read_acpi_int(acpi_handle handle, const char *method, int *val)
{
	acpi_status status;
205
	unsigned long long result;
E
Eric Cooper 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218

	status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
	if (ACPI_FAILURE(status)) {
		*val = -1;
		return -1;
	} else {
		*val = result;
		return 0;
	}
}

static int set_acpi(int cm, int value)
{
219 220 221 222
	const char *method = cm_setv[cm];

	if (method == NULL)
		return -ENODEV;
A
Alan Jenkins 已提交
223
	if ((eeepc->cm_supported & (0x1 << cm)) == 0)
224 225
		return -ENODEV;

A
Alan Jenkins 已提交
226
	if (write_acpi_int(eeepc->handle, method, value))
227
		pr_warning("Error writing %s\n", method);
E
Eric Cooper 已提交
228 229 230 231 232
	return 0;
}

static int get_acpi(int cm)
{
233 234 235 236 237
	const char *method = cm_getv[cm];
	int value;

	if (method == NULL)
		return -ENODEV;
A
Alan Jenkins 已提交
238
	if ((eeepc->cm_supported & (0x1 << cm)) == 0)
239 240
		return -ENODEV;

A
Alan Jenkins 已提交
241
	if (read_acpi_int(eeepc->handle, method, &value))
242
		pr_warning("Error reading %s\n", method);
E
Eric Cooper 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
	return value;
}

/*
 * Sys helpers
 */
static int parse_arg(const char *buf, unsigned long count, int *val)
{
	if (!count)
		return 0;
	if (sscanf(buf, "%i", val) != 1)
		return -EINVAL;
	return count;
}

static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
{
	int rv, value;

	rv = parse_arg(buf, count, &value);
	if (rv > 0)
264 265
		value = set_acpi(cm, value);
	if (value < 0)
266
		return -EIO;
E
Eric Cooper 已提交
267 268 269 270 271
	return rv;
}

static ssize_t show_sys_acpi(int cm, char *buf)
{
272 273 274
	int value = get_acpi(cm);

	if (value < 0)
275
		return -EIO;
276
	return sprintf(buf, "%d\n", value);
E
Eric Cooper 已提交
277 278
}

279
#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm)			\
E
Eric Cooper 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
	static ssize_t show_##_name(struct device *dev,			\
				    struct device_attribute *attr,	\
				    char *buf)				\
	{								\
		return show_sys_acpi(_cm, buf);				\
	}								\
	static ssize_t store_##_name(struct device *dev,		\
				     struct device_attribute *attr,	\
				     const char *buf, size_t count)	\
	{								\
		return store_sys_acpi(_cm, buf, count);			\
	}								\
	static struct device_attribute dev_attr_##_name = {		\
		.attr = {						\
			.name = __stringify(_name),			\
295
			.mode = _mode },				\
E
Eric Cooper 已提交
296 297 298 299
		.show   = show_##_name,					\
		.store  = store_##_name,				\
	}

300 301 302
EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
C
Corentin Chary 已提交
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

struct eeepc_cpufv {
	int num;
	int cur;
};

static int get_cpufv(struct eeepc_cpufv *c)
{
	c->cur = get_acpi(CM_ASL_CPUFV);
	c->num = (c->cur >> 8) & 0xff;
	c->cur &= 0xff;
	if (c->cur < 0 || c->num <= 0 || c->num > 12)
		return -ENODEV;
	return 0;
}

static ssize_t show_available_cpufv(struct device *dev,
				    struct device_attribute *attr,
				    char *buf)
{
	struct eeepc_cpufv c;
	int i;
	ssize_t len = 0;

	if (get_cpufv(&c))
		return -ENODEV;
	for (i = 0; i < c.num; i++)
		len += sprintf(buf + len, "%d ", i);
	len += sprintf(buf + len, "\n");
	return len;
}

static ssize_t show_cpufv(struct device *dev,
			  struct device_attribute *attr,
			  char *buf)
{
	struct eeepc_cpufv c;

	if (get_cpufv(&c))
		return -ENODEV;
	return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
}

static ssize_t store_cpufv(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t count)
{
	struct eeepc_cpufv c;
	int rv, value;

	if (get_cpufv(&c))
		return -ENODEV;
	rv = parse_arg(buf, count, &value);
	if (rv < 0)
		return rv;
	if (!rv || value < 0 || value >= c.num)
		return -EINVAL;
	set_acpi(CM_ASL_CPUFV, value);
	return rv;
}

static struct device_attribute dev_attr_cpufv = {
	.attr = {
		.name = "cpufv",
		.mode = 0644 },
	.show   = show_cpufv,
	.store  = store_cpufv
};

static struct device_attribute dev_attr_available_cpufv = {
	.attr = {
		.name = "available_cpufv",
		.mode = 0444 },
	.show   = show_available_cpufv
};
E
Eric Cooper 已提交
378 379 380 381 382

static struct attribute *platform_attributes[] = {
	&dev_attr_camera.attr,
	&dev_attr_cardr.attr,
	&dev_attr_disp.attr,
383
	&dev_attr_cpufv.attr,
C
Corentin Chary 已提交
384
	&dev_attr_available_cpufv.attr,
E
Eric Cooper 已提交
385 386 387 388 389 390 391
	NULL
};

static struct attribute_group platform_attribute_group = {
	.attrs = platform_attributes
};

392 393 394 395
static int eeepc_platform_init(void)
{
	int result;

A
Alan Jenkins 已提交
396
	platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
	if (!platform_device)
		return -ENOMEM;

	result = platform_device_add(platform_device);
	if (result)
		goto fail_platform_device;

	result = sysfs_create_group(&platform_device->dev.kobj,
				    &platform_attribute_group);
	if (result)
		goto fail_sysfs;
	return 0;

fail_sysfs:
	platform_device_del(platform_device);
fail_platform_device:
	platform_device_put(platform_device);
	return result;
}

static void eeepc_platform_exit(void)
{
	sysfs_remove_group(&platform_device->dev.kobj,
			   &platform_attribute_group);
	platform_device_unregister(platform_device);
}

C
Corentin Chary 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
/*
 * LEDs
 */
/*
 * These functions actually update the LED's, and are called from a
 * workqueue. By doing this as separate work rather than when the LED
 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
 * potentially bad time, such as a timer interrupt.
 */
static int tpd_led_wk;

static void tpd_led_update(struct work_struct *ignored)
{
	int value = tpd_led_wk;
	set_acpi(CM_ASL_TPD, value);
}

static struct workqueue_struct *led_workqueue;
static DECLARE_WORK(tpd_led_work, tpd_led_update);

static void tpd_led_set(struct led_classdev *led_cdev,
			enum led_brightness value)
{
	tpd_led_wk = (value > 0) ? 1 : 0;
	queue_work(led_workqueue, &tpd_led_work);
}

static struct led_classdev tpd_led = {
	.name           = "eeepc::touchpad",
	.brightness_set = tpd_led_set,
	.max_brightness = 1
};

A
Alan Jenkins 已提交
457
static int eeepc_led_init(struct device *dev)
458
{
A
Alan Jenkins 已提交
459
	int rv;
460

A
Alan Jenkins 已提交
461 462
	if (get_acpi(CM_ASL_TPD) == -ENODEV)
		return 0;
463

A
Alan Jenkins 已提交
464 465 466
	led_workqueue = create_singlethread_workqueue("led_workqueue");
	if (!led_workqueue)
		return -ENOMEM;
467

A
Alan Jenkins 已提交
468 469 470 471 472
	rv = led_classdev_register(dev, &tpd_led);
	if (rv) {
		destroy_workqueue(led_workqueue);
		return rv;
	}
473

A
Alan Jenkins 已提交
474
	return 0;
475 476
}

A
Alan Jenkins 已提交
477
static void eeepc_led_exit(void)
478
{
A
Alan Jenkins 已提交
479 480 481 482 483
	if (tpd_led.dev)
		led_classdev_unregister(&tpd_led);
	if (led_workqueue)
		destroy_workqueue(led_workqueue);
}
484 485


A
Alan Jenkins 已提交
486 487 488 489 490 491 492 493
/*
 * PCI hotplug (for wlan rfkill)
 */
static bool eeepc_wlan_rfkill_blocked(void)
{
	if (get_acpi(CM_ASL_WLAN) == 1)
		return false;
	return true;
494 495
}

A
Alan Jenkins 已提交
496
static void eeepc_rfkill_hotplug(void)
497
{
A
Alan Jenkins 已提交
498 499 500
	struct pci_dev *dev;
	struct pci_bus *bus;
	bool blocked = eeepc_wlan_rfkill_blocked();
501

A
Alan Jenkins 已提交
502 503
	if (eeepc->wlan_rfkill)
		rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
504

A
Alan Jenkins 已提交
505
	mutex_lock(&eeepc->hotplug_lock);
A
Alan Jenkins 已提交
506

A
Alan Jenkins 已提交
507
	if (eeepc->hotplug_slot) {
A
Alan Jenkins 已提交
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
		bus = pci_find_bus(0, 1);
		if (!bus) {
			pr_warning("Unable to find PCI bus 1?\n");
			goto out_unlock;
		}

		if (!blocked) {
			dev = pci_get_slot(bus, 0);
			if (dev) {
				/* Device already present */
				pci_dev_put(dev);
				goto out_unlock;
			}
			dev = pci_scan_single_device(bus, 0);
			if (dev) {
				pci_bus_assign_resources(bus);
				if (pci_bus_add_device(dev))
					pr_err("Unable to hotplug wifi\n");
			}
		} else {
			dev = pci_get_slot(bus, 0);
			if (dev) {
				pci_remove_bus_device(dev);
				pci_dev_put(dev);
			}
		}
534 535
	}

A
Alan Jenkins 已提交
536
out_unlock:
A
Alan Jenkins 已提交
537
	mutex_unlock(&eeepc->hotplug_lock);
538 539
}

A
Alan Jenkins 已提交
540
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
541
{
A
Alan Jenkins 已提交
542 543
	if (event != ACPI_NOTIFY_BUS_CHECK)
		return;
544

545
	eeepc_rfkill_hotplug();
546 547
}

548 549 550 551 552 553 554 555 556 557 558 559 560
static int eeepc_register_rfkill_notifier(char *node)
{
	acpi_status status = AE_OK;
	acpi_handle handle;

	status = acpi_get_handle(NULL, node, &handle);

	if (ACPI_SUCCESS(status)) {
		status = acpi_install_notify_handler(handle,
						     ACPI_SYSTEM_NOTIFY,
						     eeepc_rfkill_notify,
						     NULL);
		if (ACPI_FAILURE(status))
561
			pr_warning("Failed to register notify on %s\n", node);
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	} else
		return -ENODEV;

	return 0;
}

static void eeepc_unregister_rfkill_notifier(char *node)
{
	acpi_status status = AE_OK;
	acpi_handle handle;

	status = acpi_get_handle(NULL, node, &handle);

	if (ACPI_SUCCESS(status)) {
		status = acpi_remove_notify_handler(handle,
						     ACPI_SYSTEM_NOTIFY,
						     eeepc_rfkill_notify);
		if (ACPI_FAILURE(status))
580
			pr_err("Error removing rfkill notify handler %s\n",
581 582 583 584
				node);
	}
}

A
Alan Jenkins 已提交
585 586 587 588 589 590 591 592 593 594 595 596 597
static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
				    u8 *value)
{
	int val = get_acpi(CM_ASL_WLAN);

	if (val == 1 || val == 0)
		*value = val;
	else
		return -EINVAL;

	return 0;
}

598 599 600 601 602 603
static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
{
	kfree(hotplug_slot->info);
	kfree(hotplug_slot);
}

A
Alan Jenkins 已提交
604 605 606 607 608 609
static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
	.owner = THIS_MODULE,
	.get_adapter_status = eeepc_get_adapter_status,
	.get_power_status = eeepc_get_adapter_status,
};

610 611 612 613 614 615
static int eeepc_setup_pci_hotplug(void)
{
	int ret = -ENOMEM;
	struct pci_bus *bus = pci_find_bus(0, 1);

	if (!bus) {
616
		pr_err("Unable to find wifi PCI bus\n");
617 618 619
		return -ENODEV;
	}

A
Alan Jenkins 已提交
620 621
	eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
	if (!eeepc->hotplug_slot)
622 623
		goto error_slot;

A
Alan Jenkins 已提交
624
	eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
625
					    GFP_KERNEL);
A
Alan Jenkins 已提交
626
	if (!eeepc->hotplug_slot->info)
627 628
		goto error_info;

A
Alan Jenkins 已提交
629 630 631 632 633
	eeepc->hotplug_slot->private = eeepc;
	eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
	eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
	eeepc_get_adapter_status(eeepc->hotplug_slot,
				 &eeepc->hotplug_slot->info->adapter_status);
634

A
Alan Jenkins 已提交
635
	ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
636
	if (ret) {
637
		pr_err("Unable to register hotplug slot - %d\n", ret);
638 639 640 641 642 643
		goto error_register;
	}

	return 0;

error_register:
A
Alan Jenkins 已提交
644
	kfree(eeepc->hotplug_slot->info);
645
error_info:
A
Alan Jenkins 已提交
646 647
	kfree(eeepc->hotplug_slot);
	eeepc->hotplug_slot = NULL;
648 649 650 651
error_slot:
	return ret;
}

A
Alan Jenkins 已提交
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
/*
 * Rfkill devices
 */
static int eeepc_rfkill_set(void *data, bool blocked)
{
	unsigned long asl = (unsigned long)data;
	return set_acpi(asl, !blocked);
}

static const struct rfkill_ops eeepc_rfkill_ops = {
	.set_block = eeepc_rfkill_set,
};

static int eeepc_new_rfkill(struct rfkill **rfkill,
			    const char *name, struct device *dev,
			    enum rfkill_type type, int cm)
{
	int result;

	result = get_acpi(cm);
	if (result < 0)
		return result;

	*rfkill = rfkill_alloc(name, dev, type,
			       &eeepc_rfkill_ops, (void *)(unsigned long)cm);

	if (!*rfkill)
		return -EINVAL;

	rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
	result = rfkill_register(*rfkill);
	if (result) {
		rfkill_destroy(*rfkill);
		*rfkill = NULL;
		return result;
	}
	return 0;
}

static void eeepc_rfkill_exit(void)
{
	eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
	eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
	eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
A
Alan Jenkins 已提交
696 697 698 699
	if (eeepc->wlan_rfkill) {
		rfkill_unregister(eeepc->wlan_rfkill);
		rfkill_destroy(eeepc->wlan_rfkill);
		eeepc->wlan_rfkill = NULL;
A
Alan Jenkins 已提交
700 701 702 703 704 705
	}
	/*
	 * Refresh pci hotplug in case the rfkill state was changed after
	 * eeepc_unregister_rfkill_notifier()
	 */
	eeepc_rfkill_hotplug();
A
Alan Jenkins 已提交
706 707
	if (eeepc->hotplug_slot)
		pci_hp_deregister(eeepc->hotplug_slot);
A
Alan Jenkins 已提交
708

A
Alan Jenkins 已提交
709 710 711 712
	if (eeepc->bluetooth_rfkill) {
		rfkill_unregister(eeepc->bluetooth_rfkill);
		rfkill_destroy(eeepc->bluetooth_rfkill);
		eeepc->bluetooth_rfkill = NULL;
A
Alan Jenkins 已提交
713
	}
A
Alan Jenkins 已提交
714 715 716 717
	if (eeepc->wwan3g_rfkill) {
		rfkill_unregister(eeepc->wwan3g_rfkill);
		rfkill_destroy(eeepc->wwan3g_rfkill);
		eeepc->wwan3g_rfkill = NULL;
A
Alan Jenkins 已提交
718
	}
A
Alan Jenkins 已提交
719 720 721 722
	if (eeepc->wimax_rfkill) {
		rfkill_unregister(eeepc->wimax_rfkill);
		rfkill_destroy(eeepc->wimax_rfkill);
		eeepc->wimax_rfkill = NULL;
A
Alan Jenkins 已提交
723 724 725 726 727 728 729
	}
}

static int eeepc_rfkill_init(struct device *dev)
{
	int result = 0;

A
Alan Jenkins 已提交
730
	mutex_init(&eeepc->hotplug_lock);
A
Alan Jenkins 已提交
731

A
Alan Jenkins 已提交
732
	result = eeepc_new_rfkill(&eeepc->wlan_rfkill,
A
Alan Jenkins 已提交
733 734 735 736 737 738
				  "eeepc-wlan", dev,
				  RFKILL_TYPE_WLAN, CM_ASL_WLAN);

	if (result && result != -ENODEV)
		goto exit;

A
Alan Jenkins 已提交
739
	result = eeepc_new_rfkill(&eeepc->bluetooth_rfkill,
A
Alan Jenkins 已提交
740 741 742 743 744 745
				  "eeepc-bluetooth", dev,
				  RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);

	if (result && result != -ENODEV)
		goto exit;

A
Alan Jenkins 已提交
746
	result = eeepc_new_rfkill(&eeepc->wwan3g_rfkill,
A
Alan Jenkins 已提交
747 748 749 750 751 752
				  "eeepc-wwan3g", dev,
				  RFKILL_TYPE_WWAN, CM_ASL_3G);

	if (result && result != -ENODEV)
		goto exit;

A
Alan Jenkins 已提交
753
	result = eeepc_new_rfkill(&eeepc->wimax_rfkill,
A
Alan Jenkins 已提交
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
				  "eeepc-wimax", dev,
				  RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);

	if (result && result != -ENODEV)
		goto exit;

	result = eeepc_setup_pci_hotplug();
	/*
	 * If we get -EBUSY then something else is handling the PCI hotplug -
	 * don't fail in this case
	 */
	if (result == -EBUSY)
		result = 0;

	eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
	eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
	eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
	/*
	 * Refresh pci hotplug in case the rfkill state was changed during
	 * setup.
	 */
	eeepc_rfkill_hotplug();

exit:
	if (result && result != -ENODEV)
		eeepc_rfkill_exit();
	return result;
}

/*
 * Platform driver - hibernate/resume callbacks
 */
A
Alan Jenkins 已提交
786
static int eeepc_thaw(struct device *device)
787
{
A
Alan Jenkins 已提交
788
	if (eeepc->wlan_rfkill) {
789 790
		bool wlan;

791 792 793
		/*
		 * Work around bios bug - acpi _PTS turns off the wireless led
		 * during suspend.  Normally it restores it on resume, but
794
		 * we should kick it ourselves in case hibernation is aborted.
795 796 797
		 */
		wlan = get_acpi(CM_ASL_WLAN);
		set_acpi(CM_ASL_WLAN, wlan);
798 799 800 801
	}

	return 0;
}
802

A
Alan Jenkins 已提交
803
static int eeepc_restore(struct device *device)
804 805
{
	/* Refresh both wlan rfkill state and pci hotplug */
A
Alan Jenkins 已提交
806
	if (eeepc->wlan_rfkill)
807
		eeepc_rfkill_hotplug();
808

A
Alan Jenkins 已提交
809 810
	if (eeepc->bluetooth_rfkill)
		rfkill_set_sw_state(eeepc->bluetooth_rfkill,
811
				    get_acpi(CM_ASL_BLUETOOTH) != 1);
A
Alan Jenkins 已提交
812 813
	if (eeepc->wwan3g_rfkill)
		rfkill_set_sw_state(eeepc->wwan3g_rfkill,
814
				    get_acpi(CM_ASL_3G) != 1);
A
Alan Jenkins 已提交
815 816
	if (eeepc->wimax_rfkill)
		rfkill_set_sw_state(eeepc->wimax_rfkill,
817
				    get_acpi(CM_ASL_WIMAX) != 1);
818 819 820 821

	return 0;
}

A
Alan Jenkins 已提交
822
static struct dev_pm_ops eeepc_pm_ops = {
A
Alan Jenkins 已提交
823 824
	.thaw = eeepc_thaw,
	.restore = eeepc_restore,
A
Alan Jenkins 已提交
825 826 827 828
};

static struct platform_driver platform_driver = {
	.driver = {
A
Alan Jenkins 已提交
829
		.name = EEEPC_LAPTOP_FILE,
A
Alan Jenkins 已提交
830 831 832 833 834
		.owner = THIS_MODULE,
		.pm = &eeepc_pm_ops,
	}
};

835
/*
A
Alan Jenkins 已提交
836
 * Hwmon device
837
 */
A
Alan Jenkins 已提交
838 839 840 841 842 843 844 845 846

#define EEEPC_EC_SC00      0x61
#define EEEPC_EC_FAN_PWM   (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
#define EEEPC_EC_FAN_HRPM  (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
#define EEEPC_EC_FAN_LRPM  (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */

#define EEEPC_EC_SFB0      0xD0
#define EEEPC_EC_FAN_CTRL  (EEEPC_EC_SFB0 + 3) /* Byte containing SF25  */

847 848
static int eeepc_get_fan_pwm(void)
{
849
	u8 value = 0;
850

851 852
	ec_read(EEEPC_EC_FAN_PWM, &value);
	return value * 255 / 100;
853 854 855 856
}

static void eeepc_set_fan_pwm(int value)
{
C
Corentin Chary 已提交
857 858
	value = SENSORS_LIMIT(value, 0, 255);
	value = value * 100 / 255;
859
	ec_write(EEEPC_EC_FAN_PWM, value);
860 861 862 863
}

static int eeepc_get_fan_rpm(void)
{
864 865
	u8 high = 0;
	u8 low = 0;
866

867 868 869
	ec_read(EEEPC_EC_FAN_HRPM, &high);
	ec_read(EEEPC_EC_FAN_LRPM, &low);
	return high << 8 | low;
870 871 872 873
}

static int eeepc_get_fan_ctrl(void)
{
874
	u8 value = 0;
875

876
	ec_read(EEEPC_EC_FAN_CTRL, &value);
877 878 879 880
	if (value & 0x02)
		return 1; /* manual */
	else
		return 2; /* automatic */
881 882 883 884
}

static void eeepc_set_fan_ctrl(int manual)
{
885
	u8 value = 0;
886

887
	ec_read(EEEPC_EC_FAN_CTRL, &value);
888
	if (manual == 1)
889 890 891
		value |= 0x02;
	else
		value &= ~0x02;
892
	ec_write(EEEPC_EC_FAN_CTRL, value);
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
}

static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
{
	int rv, value;

	rv = parse_arg(buf, count, &value);
	if (rv > 0)
		set(value);
	return rv;
}

static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
{
	return sprintf(buf, "%d\n", get());
}

#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get)		\
	static ssize_t show_##_name(struct device *dev,			\
				    struct device_attribute *attr,	\
				    char *buf)				\
	{								\
		return show_sys_hwmon(_set, buf);			\
	}								\
	static ssize_t store_##_name(struct device *dev,		\
				     struct device_attribute *attr,	\
				     const char *buf, size_t count)	\
	{								\
		return store_sys_hwmon(_get, buf, count);		\
	}								\
	static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);

EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
C
Corentin Chary 已提交
926
EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
927 928 929 930
			 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
			 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);

C
Corentin Chary 已提交
931 932 933 934 935 936 937
static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "eeepc\n");
}
static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);

938
static struct attribute *hwmon_attributes[] = {
C
Corentin Chary 已提交
939
	&sensor_dev_attr_pwm1.dev_attr.attr,
940 941
	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
C
Corentin Chary 已提交
942
	&sensor_dev_attr_name.dev_attr.attr,
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
	NULL
};

static struct attribute_group hwmon_attribute_group = {
	.attrs = hwmon_attributes
};

static void eeepc_hwmon_exit(void)
{
	struct device *hwmon;

	hwmon = eeepc_hwmon_device;
	if (!hwmon)
		return ;
	sysfs_remove_group(&hwmon->kobj,
			   &hwmon_attribute_group);
959
	hwmon_device_unregister(hwmon);
960 961 962
	eeepc_hwmon_device = NULL;
}

A
Alan Jenkins 已提交
963
static int eeepc_hwmon_init(struct device *dev)
964
{
A
Alan Jenkins 已提交
965
	struct device *hwmon;
966 967
	int result;

A
Alan Jenkins 已提交
968 969 970 971 972
	hwmon = hwmon_device_register(dev);
	if (IS_ERR(hwmon)) {
		pr_err("Could not register eeepc hwmon device\n");
		eeepc_hwmon_device = NULL;
		return PTR_ERR(hwmon);
973
	}
A
Alan Jenkins 已提交
974 975 976 977 978 979
	eeepc_hwmon_device = hwmon;
	result = sysfs_create_group(&hwmon->kobj,
				    &hwmon_attribute_group);
	if (result)
		eeepc_hwmon_exit();
	return result;
980 981
}

A
Alan Jenkins 已提交
982 983 984 985
/*
 * Backlight device
 */
static int read_brightness(struct backlight_device *bd)
986
{
A
Alan Jenkins 已提交
987 988
	return get_acpi(CM_ASL_PANELBRIGHT);
}
989

A
Alan Jenkins 已提交
990 991 992 993
static int set_brightness(struct backlight_device *bd, int value)
{
	return set_acpi(CM_ASL_PANELBRIGHT, value);
}
994

A
Alan Jenkins 已提交
995 996 997 998
static int update_bl_status(struct backlight_device *bd)
{
	return set_brightness(bd, bd->props.brightness);
}
999

A
Alan Jenkins 已提交
1000 1001 1002 1003
static struct backlight_ops eeepcbl_ops = {
	.get_brightness = read_brightness,
	.update_status = update_bl_status,
};
1004

A
Alan Jenkins 已提交
1005 1006 1007 1008
static int eeepc_backlight_notify(void)
{
	struct backlight_device *bd = eeepc_backlight_device;
	int old = bd->props.brightness;
1009

A
Alan Jenkins 已提交
1010
	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1011

A
Alan Jenkins 已提交
1012
	return old;
1013 1014
}

C
Corentin Chary 已提交
1015 1016 1017 1018
static int eeepc_backlight_init(struct device *dev)
{
	struct backlight_device *bd;

A
Alan Jenkins 已提交
1019
	bd = backlight_device_register(EEEPC_LAPTOP_FILE, dev,
C
Corentin Chary 已提交
1020 1021
				       NULL, &eeepcbl_ops);
	if (IS_ERR(bd)) {
1022
		pr_err("Could not register eeepc backlight device\n");
C
Corentin Chary 已提交
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
		eeepc_backlight_device = NULL;
		return PTR_ERR(bd);
	}
	eeepc_backlight_device = bd;
	bd->props.max_brightness = 15;
	bd->props.brightness = read_brightness(NULL);
	bd->props.power = FB_BLANK_UNBLANK;
	backlight_update_status(bd);
	return 0;
}

A
Alan Jenkins 已提交
1034
static void eeepc_backlight_exit(void)
1035
{
A
Alan Jenkins 已提交
1036 1037 1038 1039
	if (eeepc_backlight_device)
		backlight_device_unregister(eeepc_backlight_device);
	eeepc_backlight_device = NULL;
}
1040

A
Alan Jenkins 已提交
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063

/*
 * Input device (i.e. hotkeys)
 */
static struct key_entry *eeepc_get_entry_by_scancode(int code)
{
	struct key_entry *key;

	for (key = eeepc_keymap; key->type != KE_END; key++)
		if (code == key->code)
			return key;

	return NULL;
}

static void eeepc_input_notify(int event)
{
	static struct key_entry *key;

	key = eeepc_get_entry_by_scancode(event);
	if (key) {
		switch (key->type) {
		case KE_KEY:
A
Alan Jenkins 已提交
1064
			input_report_key(eeepc->inputdev, key->keycode,
A
Alan Jenkins 已提交
1065
						1);
A
Alan Jenkins 已提交
1066 1067
			input_sync(eeepc->inputdev);
			input_report_key(eeepc->inputdev, key->keycode,
A
Alan Jenkins 已提交
1068
						0);
A
Alan Jenkins 已提交
1069
			input_sync(eeepc->inputdev);
A
Alan Jenkins 已提交
1070 1071
			break;
		}
1072
	}
A
Alan Jenkins 已提交
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
}

static struct key_entry *eepc_get_entry_by_keycode(int code)
{
	struct key_entry *key;

	for (key = eeepc_keymap; key->type != KE_END; key++)
		if (code == key->keycode && key->type == KE_KEY)
			return key;

	return NULL;
}

static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
{
	struct key_entry *key = eeepc_get_entry_by_scancode(scancode);

	if (key && key->type == KE_KEY) {
		*keycode = key->keycode;
		return 0;
	}

	return -EINVAL;
}

static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
{
	struct key_entry *key;
	int old_keycode;

	if (keycode < 0 || keycode > KEY_MAX)
		return -EINVAL;

	key = eeepc_get_entry_by_scancode(scancode);
	if (key && key->type == KE_KEY) {
		old_keycode = key->keycode;
		key->keycode = keycode;
		set_bit(keycode, dev->keybit);
		if (!eepc_get_entry_by_keycode(old_keycode))
			clear_bit(old_keycode, dev->keybit);
		return 0;
	}

	return -EINVAL;
1117 1118
}

1119 1120 1121 1122 1123
static int eeepc_input_init(struct device *dev)
{
	const struct key_entry *key;
	int result;

A
Alan Jenkins 已提交
1124 1125
	eeepc->inputdev = input_allocate_device();
	if (!eeepc->inputdev) {
1126 1127 1128
		pr_info("Unable to allocate input device\n");
		return -ENOMEM;
	}
A
Alan Jenkins 已提交
1129 1130 1131 1132 1133 1134
	eeepc->inputdev->name = "Asus EeePC extra buttons";
	eeepc->inputdev->dev.parent = dev;
	eeepc->inputdev->phys = EEEPC_LAPTOP_FILE "/input0";
	eeepc->inputdev->id.bustype = BUS_HOST;
	eeepc->inputdev->getkeycode = eeepc_getkeycode;
	eeepc->inputdev->setkeycode = eeepc_setkeycode;
1135 1136 1137 1138

	for (key = eeepc_keymap; key->type != KE_END; key++) {
		switch (key->type) {
		case KE_KEY:
A
Alan Jenkins 已提交
1139 1140
			set_bit(EV_KEY, eeepc->inputdev->evbit);
			set_bit(key->keycode, eeepc->inputdev->keybit);
1141 1142 1143
			break;
		}
	}
A
Alan Jenkins 已提交
1144
	result = input_register_device(eeepc->inputdev);
1145 1146
	if (result) {
		pr_info("Unable to register input device\n");
A
Alan Jenkins 已提交
1147
		input_free_device(eeepc->inputdev);
1148 1149 1150 1151 1152
		return result;
	}
	return 0;
}

A
Alan Jenkins 已提交
1153
static void eeepc_input_exit(void)
C
Corentin Chary 已提交
1154
{
A
Alan Jenkins 已提交
1155 1156
	if (eeepc->inputdev)
		input_unregister_device(eeepc->inputdev);
A
Alan Jenkins 已提交
1157
}
C
Corentin Chary 已提交
1158

A
Alan Jenkins 已提交
1159 1160 1161
/*
 * ACPI driver
 */
A
Alan Jenkins 已提交
1162
static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
A
Alan Jenkins 已提交
1163 1164
{
	u16 count;
C
Corentin Chary 已提交
1165

A
Alan Jenkins 已提交
1166 1167
	if (event > ACPI_MAX_SYS_NOTIFY)
		return;
A
Alan Jenkins 已提交
1168 1169 1170 1171
	count = eeepc->event_count[event % 128]++;
	acpi_bus_generate_proc_event(eeepc->device, event, count);
	acpi_bus_generate_netlink_event(eeepc->device->pnp.device_class,
					dev_name(&eeepc->device->dev), event,
A
Alan Jenkins 已提交
1172
					count);
C
Corentin Chary 已提交
1173

A
Alan Jenkins 已提交
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
	if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
		int old_brightness, new_brightness;

		/* Update backlight device. */
		old_brightness = eeepc_backlight_notify();

		/* Convert brightness event to keypress (obsolescent hack). */
		new_brightness = event - NOTIFY_BRN_MIN;

		if (new_brightness < old_brightness) {
			event = NOTIFY_BRN_MIN; /* brightness down */
		} else if (new_brightness > old_brightness) {
			event = NOTIFY_BRN_MAX; /* brightness up */
		} else {
			/*
			 * no change in brightness - already at min/max,
			 * event will be desired value (or else ignored).
			 */
		}
	}
	eeepc_input_notify(event);
}

static void cmsg_quirk(int cm, const char *name)
{
	int dummy;

	/* Some BIOSes do not report cm although it is avaliable.
	   Check if cm_getv[cm] works and, if yes, assume cm should be set. */
A
Alan Jenkins 已提交
1203 1204
	if (!(eeepc->cm_supported & (1 << cm))
	    && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
A
Alan Jenkins 已提交
1205 1206
		pr_info("%s (%x) not reported by BIOS,"
			" enabling anyway\n", name, 1 << cm);
A
Alan Jenkins 已提交
1207
		eeepc->cm_supported |= 1 << cm;
A
Alan Jenkins 已提交
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
	}
}

static void cmsg_quirks(void)
{
	cmsg_quirk(CM_ASL_LID, "LID");
	cmsg_quirk(CM_ASL_TYPE, "TYPE");
	cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
	cmsg_quirk(CM_ASL_TPD, "TPD");
}

A
Alan Jenkins 已提交
1219
static int eeepc_acpi_init(void)
A
Alan Jenkins 已提交
1220 1221 1222 1223
{
	unsigned int init_flags;
	int result;

A
Alan Jenkins 已提交
1224
	result = acpi_bus_get_status(eeepc->device);
A
Alan Jenkins 已提交
1225 1226
	if (result)
		return result;
A
Alan Jenkins 已提交
1227
	if (!eeepc->device->status.present) {
A
Alan Jenkins 已提交
1228 1229 1230 1231 1232 1233 1234
		pr_err("Hotkey device not present, aborting\n");
		return -ENODEV;
	}

	init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
	pr_notice("Hotkey init flags 0x%x\n", init_flags);

A
Alan Jenkins 已提交
1235
	if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
A
Alan Jenkins 已提交
1236 1237 1238 1239 1240
		pr_err("Hotkey initialization failed\n");
		return -ENODEV;
	}

	/* get control methods supported */
A
Alan Jenkins 已提交
1241
	if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
A
Alan Jenkins 已提交
1242 1243
		pr_err("Get control methods supported failed\n");
		return -ENODEV;
1244
	}
A
Alan Jenkins 已提交
1245
	cmsg_quirks();
A
Alan Jenkins 已提交
1246
	pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
1247

C
Corentin Chary 已提交
1248 1249 1250
	return 0;
}

A
Alan Jenkins 已提交
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
static void __devinit eeepc_enable_camera(void)
{
	/*
	 * If the following call to set_acpi() fails, it's because there's no
	 * camera so we can ignore the error.
	 */
	if (get_acpi(CM_ASL_CAMERA) == 0)
		set_acpi(CM_ASL_CAMERA, 1);
}

A
Alan Jenkins 已提交
1261
static int __devinit eeepc_acpi_add(struct acpi_device *device)
E
Eric Cooper 已提交
1262 1263 1264 1265
{
	struct device *dev;
	int result;

A
Alan Jenkins 已提交
1266 1267 1268
	pr_notice(EEEPC_LAPTOP_NAME "\n");
	eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
	if (!eeepc)
1269
		return -ENOMEM;
A
Alan Jenkins 已提交
1270 1271 1272 1273 1274
	eeepc->handle = device->handle;
	strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
	strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
	device->driver_data = eeepc;
	eeepc->device = device;
1275

A
Alan Jenkins 已提交
1276
	result = eeepc_acpi_init();
1277
	if (result)
1278
		goto fail_platform;
1279 1280
	eeepc_enable_camera();

1281
	result = eeepc_platform_init();
E
Eric Cooper 已提交
1282
	if (result)
1283
		goto fail_platform;
1284 1285 1286 1287 1288 1289 1290
	dev = &platform_device->dev;

	if (!acpi_video_backlight_support()) {
		result = eeepc_backlight_init(dev);
		if (result)
			goto fail_backlight;
	} else
1291
		pr_info("Backlight controlled by ACPI video driver\n");
1292

1293 1294 1295 1296
	result = eeepc_input_init(dev);
	if (result)
		goto fail_input;

1297 1298 1299 1300
	result = eeepc_hwmon_init(dev);
	if (result)
		goto fail_hwmon;

C
Corentin Chary 已提交
1301 1302 1303 1304
	result = eeepc_led_init(dev);
	if (result)
		goto fail_led;

1305 1306 1307 1308
	result = eeepc_rfkill_init(dev);
	if (result)
		goto fail_rfkill;

E
Eric Cooper 已提交
1309
	return 0;
1310

1311
fail_rfkill:
C
Corentin Chary 已提交
1312 1313
	eeepc_led_exit();
fail_led:
1314 1315
	eeepc_hwmon_exit();
fail_hwmon:
1316 1317
	eeepc_input_exit();
fail_input:
1318 1319
	eeepc_backlight_exit();
fail_backlight:
1320 1321
	eeepc_platform_exit();
fail_platform:
A
Alan Jenkins 已提交
1322
	kfree(eeepc);
1323

E
Eric Cooper 已提交
1324 1325 1326
	return result;
}

A
Alan Jenkins 已提交
1327
static int eeepc_acpi_remove(struct acpi_device *device, int type)
1328 1329 1330 1331 1332
{
	eeepc_backlight_exit();
	eeepc_rfkill_exit();
	eeepc_input_exit();
	eeepc_hwmon_exit();
C
Corentin Chary 已提交
1333
	eeepc_led_exit();
1334
	eeepc_platform_exit();
1335

A
Alan Jenkins 已提交
1336
	kfree(eeepc);
1337 1338 1339
	return 0;
}

A
Alan Jenkins 已提交
1340 1341

static const struct acpi_device_id eeepc_device_ids[] = {
A
Alan Jenkins 已提交
1342
	{EEEPC_ACPI_HID, 0},
A
Alan Jenkins 已提交
1343 1344 1345 1346
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);

A
Alan Jenkins 已提交
1347 1348 1349
static struct acpi_driver eeepc_acpi_driver = {
	.name = EEEPC_LAPTOP_NAME,
	.class = EEEPC_ACPI_CLASS,
A
Alan Jenkins 已提交
1350 1351 1352 1353
	.owner = THIS_MODULE,
	.ids = eeepc_device_ids,
	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
	.ops = {
A
Alan Jenkins 已提交
1354 1355 1356
		.add = eeepc_acpi_add,
		.remove = eeepc_acpi_remove,
		.notify = eeepc_acpi_notify,
A
Alan Jenkins 已提交
1357 1358 1359 1360
	},
};


1361 1362 1363 1364
static int __init eeepc_laptop_init(void)
{
	int result;

1365
	result = platform_driver_register(&platform_driver);
1366 1367
	if (result < 0)
		return result;
1368

A
Alan Jenkins 已提交
1369
	result = acpi_bus_register_driver(&eeepc_acpi_driver);
1370 1371
	if (result < 0)
		goto fail_acpi_driver;
A
Alan Jenkins 已提交
1372
	if (!eeepc) {
1373 1374
		result = -ENODEV;
		goto fail_no_device;
1375 1376
	}
	return 0;
1377 1378

fail_no_device:
A
Alan Jenkins 已提交
1379
	acpi_bus_unregister_driver(&eeepc_acpi_driver);
1380 1381 1382
fail_acpi_driver:
	platform_driver_unregister(&platform_driver);
	return result;
1383 1384 1385 1386
}

static void __exit eeepc_laptop_exit(void)
{
A
Alan Jenkins 已提交
1387
	acpi_bus_unregister_driver(&eeepc_acpi_driver);
1388
	platform_driver_unregister(&platform_driver);
1389 1390
}

E
Eric Cooper 已提交
1391 1392
module_init(eeepc_laptop_init);
module_exit(eeepc_laptop_exit);