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

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


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

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

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

1194 1195 1196 1197
	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 已提交
1198
	}
A
Alan Jenkins 已提交
1199

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

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

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

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

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

A
Alan Jenkins 已提交
1233 1234
	if (event > ACPI_MAX_SYS_NOTIFY)
		return;
A
Alan Jenkins 已提交
1235
	count = eeepc->event_count[event % 128]++;
1236 1237 1238
	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 已提交
1239
					count);
C
Corentin Chary 已提交
1240

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

1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
		/* 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)
				*/
			}
1264 1265
			sparse_keymap_report_event(eeepc->inputdev, event,
						   1, true);
A
Alan Jenkins 已提交
1266
		}
1267 1268
	} else {
		/* Everything else is a bona-fide keypress event */
1269
		sparse_keymap_report_event(eeepc->inputdev, event, 1, true);
A
Alan Jenkins 已提交
1270 1271 1272
	}
}

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

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

1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
	/*
	 * 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");
	}
1306 1307 1308 1309 1310 1311 1312 1313

	/*
	 * 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...
	 */
1314 1315
	if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||
	    strcmp(model, "1005PE") == 0) {
1316 1317 1318
		eeepc->hotplug_disabled = true;
		pr_info("wlan hotplug disabled\n");
	}
1319 1320
}

1321
static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
A
Alan Jenkins 已提交
1322 1323 1324
{
	int dummy;

1325
	/* Some BIOSes do not report cm although it is available.
A
Alan Jenkins 已提交
1326
	   Check if cm_getv[cm] works and, if yes, assume cm should be set. */
A
Alan Jenkins 已提交
1327 1328
	if (!(eeepc->cm_supported & (1 << cm))
	    && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
A
Alan Jenkins 已提交
1329 1330
		pr_info("%s (%x) not reported by BIOS,"
			" enabling anyway\n", name, 1 << cm);
A
Alan Jenkins 已提交
1331
		eeepc->cm_supported |= 1 << cm;
A
Alan Jenkins 已提交
1332 1333 1334
	}
}

1335
static void cmsg_quirks(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1336
{
1337 1338 1339 1340
	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 已提交
1341 1342
}

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

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

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

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

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

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

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

1385 1386
static bool eeepc_device_present;

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

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

1402 1403
	eeepc->hotplug_disabled = hotplug_disabled;

1404 1405
	eeepc_dmi_check(eeepc);

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

1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
	/*
	 * 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 已提交
1424
	if (result)
1425
		goto fail_platform;
1426 1427

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

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

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

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

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

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

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

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

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

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

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

A
Alan Jenkins 已提交
1484 1485

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

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


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

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

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

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

1522
	return 0;
1523 1524

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

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

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