eeepc-laptop.c 36.4 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>
30
#include <linux/slab.h>
E
Eric Cooper 已提交
31 32 33
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/uaccess.h>
34
#include <linux/input.h>
35
#include <linux/input/sparse-keymap.h>
36
#include <linux/rfkill.h>
37
#include <linux/pci.h>
38
#include <linux/pci_hotplug.h>
C
Corentin Chary 已提交
39
#include <linux/leds.h>
40
#include <linux/dmi.h>
E
Eric Cooper 已提交
41 42

#define EEEPC_LAPTOP_VERSION	"0.1"
A
Alan Jenkins 已提交
43 44
#define EEEPC_LAPTOP_NAME	"Eee PC Hotkey Driver"
#define EEEPC_LAPTOP_FILE	"eeepc"
E
Eric Cooper 已提交
45

A
Alan Jenkins 已提交
46 47 48
#define EEEPC_ACPI_CLASS	"hotkey"
#define EEEPC_ACPI_DEVICE_NAME	"Hotkey"
#define EEEPC_ACPI_HID		"ASUS010"
E
Eric Cooper 已提交
49

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

54 55
static bool hotplug_disabled;

56
module_param(hotplug_disabled, bool, 0444);
57 58 59 60 61
MODULE_PARM_DESC(hotplug_disabled,
		 "Disable hotplug for wireless device. "
		 "If your laptop need that, please report to "
		 "acpi4asus-user@lists.sourceforge.net.");

E
Eric Cooper 已提交
62 63 64
/*
 * Definitions for Asus EeePC
 */
C
Corentin Chary 已提交
65 66
#define NOTIFY_BRN_MIN	0x20
#define NOTIFY_BRN_MAX	0x2f
E
Eric Cooper 已提交
67 68 69 70 71 72 73 74 75 76

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,
77 78 79 80
	DISABLE_ASL_CARDREADER = 0x0100,
	DISABLE_ASL_3G = 0x0200,
	DISABLE_ASL_WIMAX = 0x0400,
	DISABLE_ASL_HWCF = 0x0800
E
Eric Cooper 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
};

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,
105 106 107 108 109 110 111
	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 已提交
112 113
};

A
Adrian Bunk 已提交
114
static const char *cm_getv[] = {
115
	"WLDG", "BTHG", NULL, NULL,
E
Eric Cooper 已提交
116 117 118 119
	"CAMG", NULL, NULL, NULL,
	NULL, "PBLG", NULL, NULL,
	"CFVG", NULL, NULL, NULL,
	"USBG", NULL, NULL, "MODG",
120 121
	"CRDG", "M3GG", "WIMG", "HWCF",
	"LIDG",	"TYPE", "PBPG",	"TPDG"
E
Eric Cooper 已提交
122 123
};

A
Adrian Bunk 已提交
124
static const char *cm_setv[] = {
125
	"WLDS", "BTHS", NULL, NULL,
E
Eric Cooper 已提交
126 127 128 129
	"CAMS", NULL, NULL, NULL,
	"SDSP", "PBLS", "HDPS", NULL,
	"CFVS", NULL, NULL, NULL,
	"USBG", NULL, NULL, "MODS",
130 131
	"CRDS", "M3GS", "WIMS", NULL,
	NULL, NULL, "PBPS", "TPDS"
E
Eric Cooper 已提交
132 133
};

134
static const struct key_entry eeepc_keymap[] = {
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	{ KE_KEY, 0x10, { KEY_WLAN } },
	{ KE_KEY, 0x11, { KEY_WLAN } },
	{ KE_KEY, 0x12, { KEY_PROG1 } },
	{ KE_KEY, 0x13, { KEY_MUTE } },
	{ KE_KEY, 0x14, { KEY_VOLUMEDOWN } },
	{ KE_KEY, 0x15, { KEY_VOLUMEUP } },
	{ KE_KEY, 0x16, { KEY_DISPLAY_OFF } },
	{ KE_KEY, 0x1a, { KEY_COFFEE } },
	{ KE_KEY, 0x1b, { KEY_ZOOM } },
	{ KE_KEY, 0x1c, { KEY_PROG2 } },
	{ KE_KEY, 0x1d, { KEY_PROG3 } },
	{ KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } },
	{ KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } },
	{ KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } },
	{ KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } },
	{ KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } },
	{ KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */
	{ KE_KEY, 0x38, { KEY_F14 } },
	{ KE_END, 0 },
154 155
};

E
Eric Cooper 已提交
156
/*
A
Alan Jenkins 已提交
157
 * This is the main structure, we can use it to store useful information
E
Eric Cooper 已提交
158
 */
A
Alan Jenkins 已提交
159
struct eeepc_laptop {
160
	acpi_handle handle;		/* the handle of the acpi device */
A
Alan Jenkins 已提交
161 162
	u32 cm_supported;		/* the control methods supported
					   by this BIOS */
163
	bool cpufv_disabled;
164
	bool hotplug_disabled;
A
Alan Jenkins 已提交
165
	u16 event_count[128];		/* count for each event */
E
Eric Cooper 已提交
166

167
	struct platform_device *platform_device;
168
	struct acpi_device *device;		/* the device we are in */
169 170
	struct device *hwmon_device;
	struct backlight_device *backlight_device;
171

A
Alan Jenkins 已提交
172
	struct input_dev *inputdev;
173

A
Alan Jenkins 已提交
174 175 176 177
	struct rfkill *wlan_rfkill;
	struct rfkill *bluetooth_rfkill;
	struct rfkill *wwan3g_rfkill;
	struct rfkill *wimax_rfkill;
C
Corentin Chary 已提交
178

A
Alan Jenkins 已提交
179 180
	struct hotplug_slot *hotplug_slot;
	struct mutex hotplug_lock;
181

182 183 184 185
	struct led_classdev tpd_led;
	int tpd_led_wk;
	struct workqueue_struct *led_workqueue;
	struct work_struct tpd_led_work;
C
Corentin Chary 已提交
186 187
};

E
Eric Cooper 已提交
188 189 190
/*
 * ACPI Helpers
 */
191
static int write_acpi_int(acpi_handle handle, const char *method, int val)
E
Eric Cooper 已提交
192 193 194 195 196 197 198 199 200 201
{
	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;

202
	status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
E
Eric Cooper 已提交
203 204 205 206 207 208
	return (status == AE_OK ? 0 : -1);
}

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

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

221
static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value)
E
Eric Cooper 已提交
222
{
223
	const char *method = cm_setv[cm];
E
Eric Cooper 已提交
224

225 226
	if (method == NULL)
		return -ENODEV;
A
Alan Jenkins 已提交
227
	if ((eeepc->cm_supported & (0x1 << cm)) == 0)
228
		return -ENODEV;
C
Corentin Chary 已提交
229

A
Alan Jenkins 已提交
230
	if (write_acpi_int(eeepc->handle, method, value))
231
		pr_warning("Error writing %s\n", method);
E
Eric Cooper 已提交
232
	return 0;
C
Corentin Chary 已提交
233 234
}

235
static int get_acpi(struct eeepc_laptop *eeepc, int cm)
C
Corentin Chary 已提交
236
{
237 238
	const char *method = cm_getv[cm];
	int value;
C
Corentin Chary 已提交
239

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

A
Alan Jenkins 已提交
245
	if (read_acpi_int(eeepc->handle, method, &value))
246
		pr_warning("Error reading %s\n", method);
E
Eric Cooper 已提交
247
	return value;
248 249
}

C
Corentin Chary 已提交
250 251
static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,
			      acpi_handle *handle)
252
{
253 254
	const char *method = cm_setv[cm];
	acpi_status status;
255

256 257 258 259
	if (method == NULL)
		return -ENODEV;
	if ((eeepc->cm_supported & (0x1 << cm)) == 0)
		return -ENODEV;
260

261
	status = acpi_get_handle(eeepc->handle, (char *)method,
C
Corentin Chary 已提交
262
				 handle);
263 264 265 266 267
	if (status != AE_OK) {
		pr_warning("Error finding %s\n", method);
		return -ENODEV;
	}
	return 0;
268 269
}

270

E
Eric Cooper 已提交
271 272 273 274 275 276 277 278 279 280 281 282
/*
 * 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;
}

283 284
static ssize_t store_sys_acpi(struct device *dev, int cm,
			      const char *buf, size_t count)
E
Eric Cooper 已提交
285
{
286
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
E
Eric Cooper 已提交
287 288 289 290
	int rv, value;

	rv = parse_arg(buf, count, &value);
	if (rv > 0)
291
		value = set_acpi(eeepc, cm, value);
292
	if (value < 0)
293
		return -EIO;
E
Eric Cooper 已提交
294 295 296
	return rv;
}

297
static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
E
Eric Cooper 已提交
298
{
299 300
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
	int value = get_acpi(eeepc, cm);
301 302

	if (value < 0)
303
		return -EIO;
304
	return sprintf(buf, "%d\n", value);
E
Eric Cooper 已提交
305 306
}

307
#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm)			\
E
Eric Cooper 已提交
308 309 310 311
	static ssize_t show_##_name(struct device *dev,			\
				    struct device_attribute *attr,	\
				    char *buf)				\
	{								\
312
		return show_sys_acpi(dev, _cm, buf);			\
E
Eric Cooper 已提交
313 314 315 316 317
	}								\
	static ssize_t store_##_name(struct device *dev,		\
				     struct device_attribute *attr,	\
				     const char *buf, size_t count)	\
	{								\
318
		return store_sys_acpi(dev, _cm, buf, count);		\
E
Eric Cooper 已提交
319 320 321 322
	}								\
	static struct device_attribute dev_attr_##_name = {		\
		.attr = {						\
			.name = __stringify(_name),			\
323
			.mode = _mode },				\
E
Eric Cooper 已提交
324 325 326 327
		.show   = show_##_name,					\
		.store  = store_##_name,				\
	}

328 329 330
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 已提交
331 332 333 334 335 336

struct eeepc_cpufv {
	int num;
	int cur;
};

337
static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)
C
Corentin Chary 已提交
338
{
339
	c->cur = get_acpi(eeepc, CM_ASL_CPUFV);
C
Corentin Chary 已提交
340 341 342 343 344 345 346 347 348 349 350
	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)
{
351
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
C
Corentin Chary 已提交
352 353 354 355
	struct eeepc_cpufv c;
	int i;
	ssize_t len = 0;

356
	if (get_cpufv(eeepc, &c))
C
Corentin Chary 已提交
357 358 359 360 361 362 363 364 365 366 367
		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)
{
368
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
C
Corentin Chary 已提交
369 370
	struct eeepc_cpufv c;

371
	if (get_cpufv(eeepc, &c))
C
Corentin Chary 已提交
372 373 374 375 376 377 378 379
		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)
{
380
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
C
Corentin Chary 已提交
381 382 383
	struct eeepc_cpufv c;
	int rv, value;

384 385
	if (eeepc->cpufv_disabled)
		return -EPERM;
386
	if (get_cpufv(eeepc, &c))
C
Corentin Chary 已提交
387 388 389 390 391 392
		return -ENODEV;
	rv = parse_arg(buf, count, &value);
	if (rv < 0)
		return rv;
	if (!rv || value < 0 || value >= c.num)
		return -EINVAL;
393
	set_acpi(eeepc, CM_ASL_CPUFV, value);
C
Corentin Chary 已提交
394 395 396
	return rv;
}

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 424 425 426 427 428 429 430 431
static ssize_t show_cpufv_disabled(struct device *dev,
			  struct device_attribute *attr,
			  char *buf)
{
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);

	return sprintf(buf, "%d\n", eeepc->cpufv_disabled);
}

static ssize_t store_cpufv_disabled(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t count)
{
	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
	int rv, value;

	rv = parse_arg(buf, count, &value);
	if (rv < 0)
		return rv;

	switch (value) {
	case 0:
		if (eeepc->cpufv_disabled)
			pr_warning("cpufv enabled (not officially supported "
				"on this model)\n");
		eeepc->cpufv_disabled = false;
		return rv;
	case 1:
		return -EPERM;
	default:
		return -EINVAL;
	}
}


C
Corentin Chary 已提交
432 433 434 435 436 437 438 439 440 441 442 443 444 445
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 已提交
446

447 448 449 450 451 452 453 454 455
static struct device_attribute dev_attr_cpufv_disabled = {
	.attr = {
		.name = "cpufv_disabled",
		.mode = 0644 },
	.show   = show_cpufv_disabled,
	.store  = store_cpufv_disabled
};


E
Eric Cooper 已提交
456 457 458 459
static struct attribute *platform_attributes[] = {
	&dev_attr_camera.attr,
	&dev_attr_cardr.attr,
	&dev_attr_disp.attr,
460
	&dev_attr_cpufv.attr,
C
Corentin Chary 已提交
461
	&dev_attr_available_cpufv.attr,
462
	&dev_attr_cpufv_disabled.attr,
E
Eric Cooper 已提交
463 464 465 466 467 468 469
	NULL
};

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

470
static int eeepc_platform_init(struct eeepc_laptop *eeepc)
471
{
472
	int result;
473

474 475
	eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
	if (!eeepc->platform_device)
476
		return -ENOMEM;
477
	platform_set_drvdata(eeepc->platform_device, eeepc);
478

479
	result = platform_device_add(eeepc->platform_device);
480 481
	if (result)
		goto fail_platform_device;
482

483
	result = sysfs_create_group(&eeepc->platform_device->dev.kobj,
484 485 486 487
				    &platform_attribute_group);
	if (result)
		goto fail_sysfs;
	return 0;
488

489
fail_sysfs:
490
	platform_device_del(eeepc->platform_device);
491
fail_platform_device:
492
	platform_device_put(eeepc->platform_device);
493
	return result;
494 495
}

496
static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
497
{
498
	sysfs_remove_group(&eeepc->platform_device->dev.kobj,
499
			   &platform_attribute_group);
500
	platform_device_unregister(eeepc->platform_device);
501
}
502

C
Corentin Chary 已提交
503 504 505 506 507 508 509 510 511
/*
 * 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.
 */
512 513 514
static void tpd_led_update(struct work_struct *work)
 {
	struct eeepc_laptop *eeepc;
515

516
	eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
C
Corentin Chary 已提交
517

518
	set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);
519 520
}

C
Corentin Chary 已提交
521 522
static void tpd_led_set(struct led_classdev *led_cdev,
			enum led_brightness value)
523
{
524
	struct eeepc_laptop *eeepc;
525

526
	eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
527

528 529
	eeepc->tpd_led_wk = (value > 0) ? 1 : 0;
	queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
530 531
}

532 533 534 535 536 537 538 539 540
static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
{
	struct eeepc_laptop *eeepc;

	eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);

	return get_acpi(eeepc, CM_ASL_TPD);
}

541
static int eeepc_led_init(struct eeepc_laptop *eeepc)
542
{
A
Alan Jenkins 已提交
543
	int rv;
544

545
	if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
A
Alan Jenkins 已提交
546
		return 0;
547

548 549
	eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
	if (!eeepc->led_workqueue)
A
Alan Jenkins 已提交
550
		return -ENOMEM;
551
	INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
552

553 554
	eeepc->tpd_led.name = "eeepc::touchpad";
	eeepc->tpd_led.brightness_set = tpd_led_set;
555 556
	if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */
	  eeepc->tpd_led.brightness_get = tpd_led_get;
557
	eeepc->tpd_led.max_brightness = 1;
E
Eric Cooper 已提交
558

559 560
	rv = led_classdev_register(&eeepc->platform_device->dev,
				   &eeepc->tpd_led);
A
Alan Jenkins 已提交
561
	if (rv) {
562
		destroy_workqueue(eeepc->led_workqueue);
A
Alan Jenkins 已提交
563
		return rv;
E
Eric Cooper 已提交
564
	}
565

E
Eric Cooper 已提交
566 567 568
	return 0;
}

569
static void eeepc_led_exit(struct eeepc_laptop *eeepc)
C
Corentin Chary 已提交
570
{
571 572 573 574
	if (eeepc->tpd_led.dev)
		led_classdev_unregister(&eeepc->tpd_led);
	if (eeepc->led_workqueue)
		destroy_workqueue(eeepc->led_workqueue);
C
Corentin Chary 已提交
575 576
}

577

A
Alan Jenkins 已提交
578 579 580
/*
 * PCI hotplug (for wlan rfkill)
 */
581
static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
582
{
583
	if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
A
Alan Jenkins 已提交
584 585
		return false;
	return true;
586 587
}

588
static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
589 590
{
	struct pci_dev *dev;
591
	struct pci_bus *bus;
592
	bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
593 594
	bool absent;
	u32 l;
595

A
Alan Jenkins 已提交
596 597
	if (eeepc->wlan_rfkill)
		rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
598

A
Alan Jenkins 已提交
599
	mutex_lock(&eeepc->hotplug_lock);
600

A
Alan Jenkins 已提交
601
	if (eeepc->hotplug_slot) {
602 603 604
		bus = pci_find_bus(0, 1);
		if (!bus) {
			pr_warning("Unable to find PCI bus 1?\n");
605
			goto out_unlock;
606
		}
607

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
		if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
			pr_err("Unable to read PCI config space?\n");
			goto out_unlock;
		}
		absent = (l == 0xffffffff);

		if (blocked != absent) {
			pr_warning("BIOS says wireless lan is %s, "
					"but the pci device is %s\n",
				blocked ? "blocked" : "unblocked",
				absent ? "absent" : "present");
			pr_warning("skipped wireless hotplug as probably "
					"inappropriate for this model\n");
			goto out_unlock;
		}

624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
		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);
			}
643 644
		}
	}
645 646

out_unlock:
A
Alan Jenkins 已提交
647
	mutex_unlock(&eeepc->hotplug_lock);
648 649
}

650 651
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
652 653
	struct eeepc_laptop *eeepc = data;

654 655 656
	if (event != ACPI_NOTIFY_BUS_CHECK)
		return;

657
	eeepc_rfkill_hotplug(eeepc);
E
Eric Cooper 已提交
658 659
}

660 661
static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
					  char *node)
662
{
663
	acpi_status status;
664 665 666 667 668 669 670 671
	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,
672
						     eeepc);
673
		if (ACPI_FAILURE(status))
674
			pr_warning("Failed to register notify on %s\n", node);
675 676 677 678 679 680
	} else
		return -ENODEV;

	return 0;
}

681 682
static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
					     char *node)
683 684 685 686 687 688 689 690 691 692 693
{
	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))
694
			pr_err("Error removing rfkill notify handler %s\n",
695 696 697 698
				node);
	}
}

A
Alan Jenkins 已提交
699 700 701
static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
				    u8 *value)
{
702 703
	struct eeepc_laptop *eeepc = hotplug_slot->private;
	int val = get_acpi(eeepc, CM_ASL_WLAN);
A
Alan Jenkins 已提交
704 705 706 707 708 709 710 711 712

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

	return 0;
}

713 714 715 716 717 718
static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
{
	kfree(hotplug_slot->info);
	kfree(hotplug_slot);
}

A
Alan Jenkins 已提交
719 720 721 722 723 724
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,
};

725
static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
726 727 728 729 730
{
	int ret = -ENOMEM;
	struct pci_bus *bus = pci_find_bus(0, 1);

	if (!bus) {
731
		pr_err("Unable to find wifi PCI bus\n");
732 733 734
		return -ENODEV;
	}

A
Alan Jenkins 已提交
735 736
	eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
	if (!eeepc->hotplug_slot)
737 738
		goto error_slot;

A
Alan Jenkins 已提交
739
	eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
740
					    GFP_KERNEL);
A
Alan Jenkins 已提交
741
	if (!eeepc->hotplug_slot->info)
742 743
		goto error_info;

A
Alan Jenkins 已提交
744 745 746 747 748
	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);
749

A
Alan Jenkins 已提交
750
	ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
751
	if (ret) {
752
		pr_err("Unable to register hotplug slot - %d\n", ret);
753 754 755 756 757 758
		goto error_register;
	}

	return 0;

error_register:
A
Alan Jenkins 已提交
759
	kfree(eeepc->hotplug_slot->info);
760
error_info:
A
Alan Jenkins 已提交
761 762
	kfree(eeepc->hotplug_slot);
	eeepc->hotplug_slot = NULL;
763 764 765 766
error_slot:
	return ret;
}

A
Alan Jenkins 已提交
767 768 769 770
/*
 * Rfkill devices
 */
static int eeepc_rfkill_set(void *data, bool blocked)
771
{
772
	acpi_handle handle = data;
773

774
	return write_acpi_int(handle, NULL, !blocked);
775
}
776

A
Alan Jenkins 已提交
777 778 779 780
static const struct rfkill_ops eeepc_rfkill_ops = {
	.set_block = eeepc_rfkill_set,
};

781 782 783
static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
			    struct rfkill **rfkill,
			    const char *name,
A
Alan Jenkins 已提交
784 785
			    enum rfkill_type type, int cm)
{
786
	acpi_handle handle;
A
Alan Jenkins 已提交
787 788
	int result;

789
	result = acpi_setter_handle(eeepc, cm, &handle);
A
Alan Jenkins 已提交
790 791 792
	if (result < 0)
		return result;

793 794
	*rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
			       &eeepc_rfkill_ops, handle);
795

A
Alan Jenkins 已提交
796 797 798
	if (!*rfkill)
		return -EINVAL;

799
	rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
A
Alan Jenkins 已提交
800 801 802 803 804 805
	result = rfkill_register(*rfkill);
	if (result) {
		rfkill_destroy(*rfkill);
		*rfkill = NULL;
		return result;
	}
806 807 808
	return 0;
}

809
static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
810
{
811 812 813
	eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
	eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
	eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
A
Alan Jenkins 已提交
814 815 816 817
	if (eeepc->wlan_rfkill) {
		rfkill_unregister(eeepc->wlan_rfkill);
		rfkill_destroy(eeepc->wlan_rfkill);
		eeepc->wlan_rfkill = NULL;
A
Alan Jenkins 已提交
818 819 820 821 822
	}
	/*
	 * Refresh pci hotplug in case the rfkill state was changed after
	 * eeepc_unregister_rfkill_notifier()
	 */
823
	eeepc_rfkill_hotplug(eeepc);
A
Alan Jenkins 已提交
824 825
	if (eeepc->hotplug_slot)
		pci_hp_deregister(eeepc->hotplug_slot);
A
Alan Jenkins 已提交
826

A
Alan Jenkins 已提交
827 828 829 830
	if (eeepc->bluetooth_rfkill) {
		rfkill_unregister(eeepc->bluetooth_rfkill);
		rfkill_destroy(eeepc->bluetooth_rfkill);
		eeepc->bluetooth_rfkill = NULL;
A
Alan Jenkins 已提交
831
	}
A
Alan Jenkins 已提交
832 833 834 835
	if (eeepc->wwan3g_rfkill) {
		rfkill_unregister(eeepc->wwan3g_rfkill);
		rfkill_destroy(eeepc->wwan3g_rfkill);
		eeepc->wwan3g_rfkill = NULL;
A
Alan Jenkins 已提交
836
	}
A
Alan Jenkins 已提交
837 838 839 840
	if (eeepc->wimax_rfkill) {
		rfkill_unregister(eeepc->wimax_rfkill);
		rfkill_destroy(eeepc->wimax_rfkill);
		eeepc->wimax_rfkill = NULL;
A
Alan Jenkins 已提交
841 842 843
	}
}

844
static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
845 846 847
{
	int result = 0;

A
Alan Jenkins 已提交
848
	mutex_init(&eeepc->hotplug_lock);
A
Alan Jenkins 已提交
849

850 851 852
	result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
				  "eeepc-wlan", RFKILL_TYPE_WLAN,
				  CM_ASL_WLAN);
A
Alan Jenkins 已提交
853 854 855 856

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

857 858 859
	result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
				  "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
				  CM_ASL_BLUETOOTH);
A
Alan Jenkins 已提交
860 861 862 863

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

864 865 866
	result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
				  "eeepc-wwan3g", RFKILL_TYPE_WWAN,
				  CM_ASL_3G);
A
Alan Jenkins 已提交
867 868 869 870

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

871 872 873
	result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
				  "eeepc-wimax", RFKILL_TYPE_WIMAX,
				  CM_ASL_WIMAX);
A
Alan Jenkins 已提交
874 875 876 877

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

878 879 880
	if (eeepc->hotplug_disabled)
		return 0;

881
	result = eeepc_setup_pci_hotplug(eeepc);
A
Alan Jenkins 已提交
882 883 884 885 886 887 888
	/*
	 * If we get -EBUSY then something else is handling the PCI hotplug -
	 * don't fail in this case
	 */
	if (result == -EBUSY)
		result = 0;

889 890 891
	eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
	eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
	eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
A
Alan Jenkins 已提交
892 893 894 895
	/*
	 * Refresh pci hotplug in case the rfkill state was changed during
	 * setup.
	 */
896
	eeepc_rfkill_hotplug(eeepc);
A
Alan Jenkins 已提交
897 898 899

exit:
	if (result && result != -ENODEV)
900
		eeepc_rfkill_exit(eeepc);
A
Alan Jenkins 已提交
901 902 903
	return result;
}

904
/*
A
Alan Jenkins 已提交
905
 * Platform driver - hibernate/resume callbacks
906
 */
907
static int eeepc_hotk_thaw(struct device *device)
908
{
909 910
	struct eeepc_laptop *eeepc = dev_get_drvdata(device);

A
Alan Jenkins 已提交
911
	if (eeepc->wlan_rfkill) {
912 913
		bool wlan;

914 915 916
		/*
		 * Work around bios bug - acpi _PTS turns off the wireless led
		 * during suspend.  Normally it restores it on resume, but
917
		 * we should kick it ourselves in case hibernation is aborted.
918
		 */
919 920
		wlan = get_acpi(eeepc, CM_ASL_WLAN);
		set_acpi(eeepc, CM_ASL_WLAN, wlan);
921 922 923 924
	}

	return 0;
}
925

926
static int eeepc_hotk_restore(struct device *device)
927
{
928 929
	struct eeepc_laptop *eeepc = dev_get_drvdata(device);

930
	/* Refresh both wlan rfkill state and pci hotplug */
A
Alan Jenkins 已提交
931
	if (eeepc->wlan_rfkill)
932
		eeepc_rfkill_hotplug(eeepc);
933

A
Alan Jenkins 已提交
934 935
	if (eeepc->bluetooth_rfkill)
		rfkill_set_sw_state(eeepc->bluetooth_rfkill,
936
				    get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);
A
Alan Jenkins 已提交
937 938
	if (eeepc->wwan3g_rfkill)
		rfkill_set_sw_state(eeepc->wwan3g_rfkill,
939
				    get_acpi(eeepc, CM_ASL_3G) != 1);
A
Alan Jenkins 已提交
940 941
	if (eeepc->wimax_rfkill)
		rfkill_set_sw_state(eeepc->wimax_rfkill,
942
				    get_acpi(eeepc, CM_ASL_WIMAX) != 1);
943 944 945 946

	return 0;
}

L
Len Brown 已提交
947
static const struct dev_pm_ops eeepc_pm_ops = {
948 949
	.thaw = eeepc_hotk_thaw,
	.restore = eeepc_hotk_restore,
A
Alan Jenkins 已提交
950 951 952 953
};

static struct platform_driver platform_driver = {
	.driver = {
A
Alan Jenkins 已提交
954
		.name = EEEPC_LAPTOP_FILE,
A
Alan Jenkins 已提交
955 956 957 958 959
		.owner = THIS_MODULE,
		.pm = &eeepc_pm_ops,
	}
};

960
/*
A
Alan Jenkins 已提交
961
 * Hwmon device
962
 */
A
Alan Jenkins 已提交
963 964 965 966 967 968 969 970 971

#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  */

972 973
static int eeepc_get_fan_pwm(void)
{
974
	u8 value = 0;
975

976 977
	ec_read(EEEPC_EC_FAN_PWM, &value);
	return value * 255 / 100;
978 979 980 981
}

static void eeepc_set_fan_pwm(int value)
{
C
Corentin Chary 已提交
982 983
	value = SENSORS_LIMIT(value, 0, 255);
	value = value * 100 / 255;
984
	ec_write(EEEPC_EC_FAN_PWM, value);
985 986 987 988
}

static int eeepc_get_fan_rpm(void)
{
989 990
	u8 high = 0;
	u8 low = 0;
991

992 993 994
	ec_read(EEEPC_EC_FAN_HRPM, &high);
	ec_read(EEEPC_EC_FAN_LRPM, &low);
	return high << 8 | low;
995 996 997 998
}

static int eeepc_get_fan_ctrl(void)
{
999
	u8 value = 0;
1000

1001
	ec_read(EEEPC_EC_FAN_CTRL, &value);
1002 1003 1004 1005
	if (value & 0x02)
		return 1; /* manual */
	else
		return 2; /* automatic */
1006 1007 1008 1009
}

static void eeepc_set_fan_ctrl(int manual)
{
1010
	u8 value = 0;
1011

1012
	ec_read(EEEPC_EC_FAN_CTRL, &value);
1013
	if (manual == 1)
1014 1015 1016
		value |= 0x02;
	else
		value &= ~0x02;
1017
	ec_write(EEEPC_EC_FAN_CTRL, value);
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
}

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 已提交
1051
EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
1052 1053 1054 1055
			 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 已提交
1056 1057 1058 1059 1060 1061 1062
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);

1063
static struct attribute *hwmon_attributes[] = {
C
Corentin Chary 已提交
1064
	&sensor_dev_attr_pwm1.dev_attr.attr,
1065 1066
	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
C
Corentin Chary 已提交
1067
	&sensor_dev_attr_name.dev_attr.attr,
1068 1069 1070 1071 1072 1073 1074
	NULL
};

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

1075
static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc)
C
Corentin Chary 已提交
1076
{
1077 1078
	struct device *hwmon;

1079
	hwmon = eeepc->hwmon_device;
1080
	if (!hwmon)
1081
		return;
1082 1083
	sysfs_remove_group(&hwmon->kobj,
			   &hwmon_attribute_group);
1084
	hwmon_device_unregister(hwmon);
1085
	eeepc->hwmon_device = NULL;
1086 1087
}

1088
static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
1089
{
A
Alan Jenkins 已提交
1090
	struct device *hwmon;
1091 1092
	int result;

1093
	hwmon = hwmon_device_register(&eeepc->platform_device->dev);
A
Alan Jenkins 已提交
1094 1095
	if (IS_ERR(hwmon)) {
		pr_err("Could not register eeepc hwmon device\n");
1096
		eeepc->hwmon_device = NULL;
A
Alan Jenkins 已提交
1097
		return PTR_ERR(hwmon);
1098
	}
1099
	eeepc->hwmon_device = hwmon;
A
Alan Jenkins 已提交
1100 1101 1102
	result = sysfs_create_group(&hwmon->kobj,
				    &hwmon_attribute_group);
	if (result)
1103
		eeepc_hwmon_exit(eeepc);
A
Alan Jenkins 已提交
1104
	return result;
1105 1106
}

A
Alan Jenkins 已提交
1107 1108 1109 1110
/*
 * Backlight device
 */
static int read_brightness(struct backlight_device *bd)
1111
{
1112 1113 1114
	struct eeepc_laptop *eeepc = bl_get_data(bd);

	return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
C
Corentin Chary 已提交
1115 1116
}

A
Alan Jenkins 已提交
1117
static int set_brightness(struct backlight_device *bd, int value)
1118
{
1119
	struct eeepc_laptop *eeepc = bl_get_data(bd);
1120

1121
	return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
1122 1123
}

A
Alan Jenkins 已提交
1124
static int update_bl_status(struct backlight_device *bd)
1125
{
A
Alan Jenkins 已提交
1126 1127
	return set_brightness(bd, bd->props.brightness);
}
1128

L
Lionel Debroux 已提交
1129
static const struct backlight_ops eeepcbl_ops = {
A
Alan Jenkins 已提交
1130 1131 1132
	.get_brightness = read_brightness,
	.update_status = update_bl_status,
};
1133

1134
static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1135
{
1136
	struct backlight_device *bd = eeepc->backlight_device;
A
Alan Jenkins 已提交
1137
	int old = bd->props.brightness;
1138

A
Alan Jenkins 已提交
1139
	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1140

A
Alan Jenkins 已提交
1141
	return old;
1142 1143
}

1144
static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
1145
{
1146
	struct backlight_properties props;
C
Corentin Chary 已提交
1147
	struct backlight_device *bd;
1148

1149 1150
	memset(&props, 0, sizeof(struct backlight_properties));
	props.max_brightness = 15;
1151
	bd = backlight_device_register(EEEPC_LAPTOP_FILE,
1152 1153
				       &eeepc->platform_device->dev, eeepc,
				       &eeepcbl_ops, &props);
C
Corentin Chary 已提交
1154
	if (IS_ERR(bd)) {
1155
		pr_err("Could not register eeepc backlight device\n");
1156
		eeepc->backlight_device = NULL;
C
Corentin Chary 已提交
1157 1158
		return PTR_ERR(bd);
	}
1159 1160
	eeepc->backlight_device = bd;
	bd->props.brightness = read_brightness(bd);
C
Corentin Chary 已提交
1161 1162 1163 1164
	bd->props.power = FB_BLANK_UNBLANK;
	backlight_update_status(bd);
	return 0;
}
1165

1166
static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
1167
{
1168 1169 1170
	if (eeepc->backlight_device)
		backlight_device_unregister(eeepc->backlight_device);
	eeepc->backlight_device = NULL;
A
Alan Jenkins 已提交
1171
}
1172 1173


A
Alan Jenkins 已提交
1174 1175 1176
/*
 * Input device (i.e. hotkeys)
 */
1177
static int eeepc_input_init(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1178
{
1179 1180
	struct input_dev *input;
	int error;
1181

1182 1183 1184 1185
	input = input_allocate_device();
	if (!input) {
		pr_info("Unable to allocate input device\n");
		return -ENOMEM;
1186
	}
1187

1188 1189 1190 1191
	input->name = "Asus EeePC extra buttons";
	input->phys = EEEPC_LAPTOP_FILE "/input0";
	input->id.bustype = BUS_HOST;
	input->dev.parent = &eeepc->platform_device->dev;
1192

1193 1194 1195 1196
	error = sparse_keymap_setup(input, eeepc_keymap, NULL);
	if (error) {
		pr_err("Unable to setup input device keymap\n");
		goto err_free_dev;
C
Corentin Chary 已提交
1197
	}
A
Alan Jenkins 已提交
1198

1199 1200 1201 1202
	error = input_register_device(input);
	if (error) {
		pr_err("Unable to register input device\n");
		goto err_free_keymap;
1203
	}
A
Alan Jenkins 已提交
1204

1205
	eeepc->inputdev = input;
1206
	return 0;
1207

1208
err_free_keymap:
1209
	sparse_keymap_free(input);
1210
err_free_dev:
1211 1212
	input_free_device(input);
	return error;
1213 1214
}

1215
static void eeepc_input_exit(struct eeepc_laptop *eeepc)
E
Eric Cooper 已提交
1216
{
1217
	if (eeepc->inputdev) {
1218
		sparse_keymap_free(eeepc->inputdev);
A
Alan Jenkins 已提交
1219
		input_unregister_device(eeepc->inputdev);
1220
	}
1221
	eeepc->inputdev = NULL;
A
Alan Jenkins 已提交
1222
}
C
Corentin Chary 已提交
1223

A
Alan Jenkins 已提交
1224 1225 1226
/*
 * ACPI driver
 */
A
Alan Jenkins 已提交
1227
static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
A
Alan Jenkins 已提交
1228
{
1229
	struct eeepc_laptop *eeepc = acpi_driver_data(device);
A
Alan Jenkins 已提交
1230
	u16 count;
C
Corentin Chary 已提交
1231

A
Alan Jenkins 已提交
1232 1233
	if (event > ACPI_MAX_SYS_NOTIFY)
		return;
A
Alan Jenkins 已提交
1234
	count = eeepc->event_count[event % 128]++;
1235 1236 1237
	acpi_bus_generate_proc_event(device, event, count);
	acpi_bus_generate_netlink_event(device->pnp.device_class,
					dev_name(&device->dev), event,
A
Alan Jenkins 已提交
1238
					count);
C
Corentin Chary 已提交
1239

1240
	/* Brightness events are special */
A
Alan Jenkins 已提交
1241 1242
	if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
		/* Ignore them completely if the acpi video driver is used */
		if (eeepc->backlight_device != NULL) {
			int old_brightness, new_brightness;

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

			/* Convert 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)
				*/
			}
1263 1264
			sparse_keymap_report_event(eeepc->inputdev, event,
						   1, true);
A
Alan Jenkins 已提交
1265
		}
1266 1267
	} else {
		/* Everything else is a bona-fide keypress event */
1268
		sparse_keymap_report_event(eeepc->inputdev, event, 1, true);
A
Alan Jenkins 已提交
1269 1270 1271
	}
}

1272 1273 1274 1275
static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
{
	const char *model;

1276 1277 1278 1279
	model = dmi_get_system_info(DMI_PRODUCT_NAME);
	if (!model)
		return;

1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
	/*
	 * Blacklist for setting cpufv (cpu speed).
	 *
	 * EeePC 4G ("701") implements CFVS, but it is not supported
	 * by the pre-installed OS, and the original option to change it
	 * in the BIOS setup screen was removed in later versions.
	 *
	 * Judging by the lack of "Super Hybrid Engine" on Asus product pages,
	 * this applies to all "701" models (4G/4G Surf/2G Surf).
	 *
	 * So Asus made a deliberate decision not to support it on this model.
	 * We have several reports that using it can cause the system to hang
	 *
	 * The hang has also been reported on a "702" (Model name "8G"?).
	 *
	 * We avoid dmi_check_system() / dmi_match(), because they use
	 * substring matching.  We don't want to affect the "701SD"
	 * and "701SDX" models, because they do support S.H.E.
	 */
	if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
		eeepc->cpufv_disabled = true;
		pr_info("model %s does not officially support setting cpu "
			"speed\n", model);
		pr_info("cpufv disabled to avoid instability\n");
	}
1305 1306 1307 1308 1309 1310 1311 1312

	/*
	 * Blacklist for wlan hotplug
	 *
	 * Eeepc 1005HA doesn't work like others models and don't need the
	 * hotplug code. In fact, current hotplug code seems to unplug another
	 * device...
	 */
1313 1314
	if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||
	    strcmp(model, "1005PE") == 0) {
1315 1316 1317
		eeepc->hotplug_disabled = true;
		pr_info("wlan hotplug disabled\n");
	}
1318 1319
}

1320
static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
A
Alan Jenkins 已提交
1321 1322 1323 1324 1325
{
	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 已提交
1326 1327
	if (!(eeepc->cm_supported & (1 << cm))
	    && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
A
Alan Jenkins 已提交
1328 1329
		pr_info("%s (%x) not reported by BIOS,"
			" enabling anyway\n", name, 1 << cm);
A
Alan Jenkins 已提交
1330
		eeepc->cm_supported |= 1 << cm;
A
Alan Jenkins 已提交
1331 1332 1333
	}
}

1334
static void cmsg_quirks(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1335
{
1336 1337 1338 1339
	cmsg_quirk(eeepc, CM_ASL_LID, "LID");
	cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");
	cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");
	cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
A
Alan Jenkins 已提交
1340 1341
}

1342
static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1343 1344
{
	unsigned int init_flags;
E
Eric Cooper 已提交
1345 1346
	int result;

1347
	result = acpi_bus_get_status(eeepc->device);
1348
	if (result)
A
Alan Jenkins 已提交
1349
		return result;
1350
	if (!eeepc->device->status.present) {
A
Alan Jenkins 已提交
1351 1352 1353
		pr_err("Hotkey device not present, aborting\n");
		return -ENODEV;
	}
1354

A
Alan Jenkins 已提交
1355 1356 1357
	init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
	pr_notice("Hotkey init flags 0x%x\n", init_flags);

A
Alan Jenkins 已提交
1358
	if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
A
Alan Jenkins 已提交
1359 1360
		pr_err("Hotkey initialization failed\n");
		return -ENODEV;
E
Eric Cooper 已提交
1361
	}
A
Alan Jenkins 已提交
1362 1363

	/* get control methods supported */
A
Alan Jenkins 已提交
1364
	if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
A
Alan Jenkins 已提交
1365 1366
		pr_err("Get control methods supported failed\n");
		return -ENODEV;
1367
	}
1368
	cmsg_quirks(eeepc);
A
Alan Jenkins 已提交
1369
	pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
1370

C
Corentin Chary 已提交
1371 1372 1373
	return 0;
}

1374
static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1375 1376 1377 1378 1379
{
	/*
	 * If the following call to set_acpi() fails, it's because there's no
	 * camera so we can ignore the error.
	 */
1380 1381
	if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
		set_acpi(eeepc, CM_ASL_CAMERA, 1);
A
Alan Jenkins 已提交
1382 1383
}

1384 1385
static bool eeepc_device_present;

A
Alan Jenkins 已提交
1386
static int __devinit eeepc_acpi_add(struct acpi_device *device)
E
Eric Cooper 已提交
1387
{
1388
	struct eeepc_laptop *eeepc;
E
Eric Cooper 已提交
1389 1390
	int result;

A
Alan Jenkins 已提交
1391 1392 1393
	pr_notice(EEEPC_LAPTOP_NAME "\n");
	eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
	if (!eeepc)
1394
		return -ENOMEM;
A
Alan Jenkins 已提交
1395 1396 1397 1398
	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;
1399
	eeepc->device = device;
1400

1401 1402
	eeepc->hotplug_disabled = hotplug_disabled;

1403 1404
	eeepc_dmi_check(eeepc);

1405
	result = eeepc_acpi_init(eeepc);
E
Eric Cooper 已提交
1406
	if (result)
1407
		goto fail_platform;
1408
	eeepc_enable_camera(eeepc);
1409

1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
	/*
	 * Register the platform device first.  It is used as a parent for the
	 * sub-devices below.
	 *
	 * Note that if there are multiple instances of this ACPI device it
	 * will bail out, because the platform device is registered with a
	 * fixed name.  Of course it doesn't make sense to have more than one,
	 * and machine-specific scripts find the fixed name convenient.  But
	 * It's also good for us to exclude multiple instances because both
	 * our hwmon and our wlan rfkill subdevice use global ACPI objects
	 * (the EC and the wlan PCI slot respectively).
	 */
	result = eeepc_platform_init(eeepc);
E
Eric Cooper 已提交
1423
	if (result)
1424
		goto fail_platform;
1425 1426

	if (!acpi_video_backlight_support()) {
1427
		result = eeepc_backlight_init(eeepc);
1428 1429 1430
		if (result)
			goto fail_backlight;
	} else
1431
		pr_info("Backlight controlled by ACPI video driver\n");
1432

1433
	result = eeepc_input_init(eeepc);
1434 1435 1436
	if (result)
		goto fail_input;

1437
	result = eeepc_hwmon_init(eeepc);
1438 1439 1440
	if (result)
		goto fail_hwmon;

1441
	result = eeepc_led_init(eeepc);
C
Corentin Chary 已提交
1442 1443 1444
	if (result)
		goto fail_led;

1445
	result = eeepc_rfkill_init(eeepc);
1446 1447 1448
	if (result)
		goto fail_rfkill;

1449
	eeepc_device_present = true;
E
Eric Cooper 已提交
1450
	return 0;
1451

1452
fail_rfkill:
1453
	eeepc_led_exit(eeepc);
C
Corentin Chary 已提交
1454
fail_led:
1455
	eeepc_hwmon_exit(eeepc);
1456
fail_hwmon:
1457
	eeepc_input_exit(eeepc);
1458
fail_input:
1459
	eeepc_backlight_exit(eeepc);
1460
fail_backlight:
1461
	eeepc_platform_exit(eeepc);
1462
fail_platform:
A
Alan Jenkins 已提交
1463
	kfree(eeepc);
1464

E
Eric Cooper 已提交
1465 1466 1467
	return result;
}

A
Alan Jenkins 已提交
1468
static int eeepc_acpi_remove(struct acpi_device *device, int type)
1469
{
1470
	struct eeepc_laptop *eeepc = acpi_driver_data(device);
1471

1472 1473 1474 1475 1476 1477
	eeepc_backlight_exit(eeepc);
	eeepc_rfkill_exit(eeepc);
	eeepc_input_exit(eeepc);
	eeepc_hwmon_exit(eeepc);
	eeepc_led_exit(eeepc);
	eeepc_platform_exit(eeepc);
1478

A
Alan Jenkins 已提交
1479
	kfree(eeepc);
1480 1481 1482
	return 0;
}

A
Alan Jenkins 已提交
1483 1484

static const struct acpi_device_id eeepc_device_ids[] = {
A
Alan Jenkins 已提交
1485
	{EEEPC_ACPI_HID, 0},
A
Alan Jenkins 已提交
1486 1487 1488 1489
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);

A
Alan Jenkins 已提交
1490 1491 1492
static struct acpi_driver eeepc_acpi_driver = {
	.name = EEEPC_LAPTOP_NAME,
	.class = EEEPC_ACPI_CLASS,
A
Alan Jenkins 已提交
1493 1494 1495 1496
	.owner = THIS_MODULE,
	.ids = eeepc_device_ids,
	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
	.ops = {
A
Alan Jenkins 已提交
1497 1498 1499
		.add = eeepc_acpi_add,
		.remove = eeepc_acpi_remove,
		.notify = eeepc_acpi_notify,
A
Alan Jenkins 已提交
1500 1501 1502 1503
	},
};


1504 1505 1506 1507
static int __init eeepc_laptop_init(void)
{
	int result;

1508
	result = platform_driver_register(&platform_driver);
1509 1510
	if (result < 0)
		return result;
1511

A
Alan Jenkins 已提交
1512
	result = acpi_bus_register_driver(&eeepc_acpi_driver);
1513 1514
	if (result < 0)
		goto fail_acpi_driver;
1515

1516
	if (!eeepc_device_present) {
1517 1518
		result = -ENODEV;
		goto fail_no_device;
1519
	}
1520

1521
	return 0;
1522 1523

fail_no_device:
A
Alan Jenkins 已提交
1524
	acpi_bus_unregister_driver(&eeepc_acpi_driver);
1525 1526 1527
fail_acpi_driver:
	platform_driver_unregister(&platform_driver);
	return result;
1528 1529 1530 1531
}

static void __exit eeepc_laptop_exit(void)
{
A
Alan Jenkins 已提交
1532
	acpi_bus_unregister_driver(&eeepc_acpi_driver);
1533
	platform_driver_unregister(&platform_driver);
1534 1535
}

E
Eric Cooper 已提交
1536 1537
module_init(eeepc_laptop_init);
module_exit(eeepc_laptop_exit);