eeepc-laptop.c 36.0 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
static int eeepc_led_init(struct eeepc_laptop *eeepc)
533
{
A
Alan Jenkins 已提交
534
	int rv;
535

536
	if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
A
Alan Jenkins 已提交
537
		return 0;
538

539 540
	eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
	if (!eeepc->led_workqueue)
A
Alan Jenkins 已提交
541
		return -ENOMEM;
542
	INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
543

544 545 546
	eeepc->tpd_led.name = "eeepc::touchpad";
	eeepc->tpd_led.brightness_set = tpd_led_set;
	eeepc->tpd_led.max_brightness = 1;
E
Eric Cooper 已提交
547

548 549
	rv = led_classdev_register(&eeepc->platform_device->dev,
				   &eeepc->tpd_led);
A
Alan Jenkins 已提交
550
	if (rv) {
551
		destroy_workqueue(eeepc->led_workqueue);
A
Alan Jenkins 已提交
552
		return rv;
E
Eric Cooper 已提交
553
	}
554

E
Eric Cooper 已提交
555 556 557
	return 0;
}

558
static void eeepc_led_exit(struct eeepc_laptop *eeepc)
C
Corentin Chary 已提交
559
{
560 561 562 563
	if (eeepc->tpd_led.dev)
		led_classdev_unregister(&eeepc->tpd_led);
	if (eeepc->led_workqueue)
		destroy_workqueue(eeepc->led_workqueue);
C
Corentin Chary 已提交
564 565
}

566

A
Alan Jenkins 已提交
567 568 569
/*
 * PCI hotplug (for wlan rfkill)
 */
570
static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
571
{
572
	if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
A
Alan Jenkins 已提交
573 574
		return false;
	return true;
575 576
}

577
static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
578 579
{
	struct pci_dev *dev;
580
	struct pci_bus *bus;
581
	bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
582 583
	bool absent;
	u32 l;
584

A
Alan Jenkins 已提交
585 586
	if (eeepc->wlan_rfkill)
		rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
587

A
Alan Jenkins 已提交
588
	mutex_lock(&eeepc->hotplug_lock);
589

A
Alan Jenkins 已提交
590
	if (eeepc->hotplug_slot) {
591 592 593
		bus = pci_find_bus(0, 1);
		if (!bus) {
			pr_warning("Unable to find PCI bus 1?\n");
594
			goto out_unlock;
595
		}
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
		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;
		}

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
		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);
			}
632 633
		}
	}
634 635

out_unlock:
A
Alan Jenkins 已提交
636
	mutex_unlock(&eeepc->hotplug_lock);
637 638
}

639 640
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
641 642
	struct eeepc_laptop *eeepc = data;

643 644 645
	if (event != ACPI_NOTIFY_BUS_CHECK)
		return;

646
	eeepc_rfkill_hotplug(eeepc);
E
Eric Cooper 已提交
647 648
}

649 650
static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
					  char *node)
651
{
652
	acpi_status status;
653 654 655 656 657 658 659 660
	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,
661
						     eeepc);
662
		if (ACPI_FAILURE(status))
663
			pr_warning("Failed to register notify on %s\n", node);
664 665 666 667 668 669
	} else
		return -ENODEV;

	return 0;
}

670 671
static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
					     char *node)
672 673 674 675 676 677 678 679 680 681 682
{
	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))
683
			pr_err("Error removing rfkill notify handler %s\n",
684 685 686 687
				node);
	}
}

A
Alan Jenkins 已提交
688 689 690
static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
				    u8 *value)
{
691 692
	struct eeepc_laptop *eeepc = hotplug_slot->private;
	int val = get_acpi(eeepc, CM_ASL_WLAN);
A
Alan Jenkins 已提交
693 694 695 696 697 698 699 700 701

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

	return 0;
}

702 703 704 705 706 707
static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
{
	kfree(hotplug_slot->info);
	kfree(hotplug_slot);
}

A
Alan Jenkins 已提交
708 709 710 711 712 713
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,
};

714
static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
715 716 717 718 719
{
	int ret = -ENOMEM;
	struct pci_bus *bus = pci_find_bus(0, 1);

	if (!bus) {
720
		pr_err("Unable to find wifi PCI bus\n");
721 722 723
		return -ENODEV;
	}

A
Alan Jenkins 已提交
724 725
	eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
	if (!eeepc->hotplug_slot)
726 727
		goto error_slot;

A
Alan Jenkins 已提交
728
	eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
729
					    GFP_KERNEL);
A
Alan Jenkins 已提交
730
	if (!eeepc->hotplug_slot->info)
731 732
		goto error_info;

A
Alan Jenkins 已提交
733 734 735 736 737
	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);
738

A
Alan Jenkins 已提交
739
	ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
740
	if (ret) {
741
		pr_err("Unable to register hotplug slot - %d\n", ret);
742 743 744 745 746 747
		goto error_register;
	}

	return 0;

error_register:
A
Alan Jenkins 已提交
748
	kfree(eeepc->hotplug_slot->info);
749
error_info:
A
Alan Jenkins 已提交
750 751
	kfree(eeepc->hotplug_slot);
	eeepc->hotplug_slot = NULL;
752 753 754 755
error_slot:
	return ret;
}

A
Alan Jenkins 已提交
756 757 758 759
/*
 * Rfkill devices
 */
static int eeepc_rfkill_set(void *data, bool blocked)
760
{
761
	acpi_handle handle = data;
762

763
	return write_acpi_int(handle, NULL, !blocked);
764
}
765

A
Alan Jenkins 已提交
766 767 768 769
static const struct rfkill_ops eeepc_rfkill_ops = {
	.set_block = eeepc_rfkill_set,
};

770 771 772
static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
			    struct rfkill **rfkill,
			    const char *name,
A
Alan Jenkins 已提交
773 774
			    enum rfkill_type type, int cm)
{
775
	acpi_handle handle;
A
Alan Jenkins 已提交
776 777
	int result;

778
	result = acpi_setter_handle(eeepc, cm, &handle);
A
Alan Jenkins 已提交
779 780 781
	if (result < 0)
		return result;

782 783
	*rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
			       &eeepc_rfkill_ops, handle);
784

A
Alan Jenkins 已提交
785 786 787
	if (!*rfkill)
		return -EINVAL;

788
	rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
A
Alan Jenkins 已提交
789 790 791 792 793 794
	result = rfkill_register(*rfkill);
	if (result) {
		rfkill_destroy(*rfkill);
		*rfkill = NULL;
		return result;
	}
795 796 797
	return 0;
}

798
static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
799
{
800 801 802
	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 已提交
803 804 805 806
	if (eeepc->wlan_rfkill) {
		rfkill_unregister(eeepc->wlan_rfkill);
		rfkill_destroy(eeepc->wlan_rfkill);
		eeepc->wlan_rfkill = NULL;
A
Alan Jenkins 已提交
807 808 809 810 811
	}
	/*
	 * Refresh pci hotplug in case the rfkill state was changed after
	 * eeepc_unregister_rfkill_notifier()
	 */
812
	eeepc_rfkill_hotplug(eeepc);
A
Alan Jenkins 已提交
813 814
	if (eeepc->hotplug_slot)
		pci_hp_deregister(eeepc->hotplug_slot);
A
Alan Jenkins 已提交
815

A
Alan Jenkins 已提交
816 817 818 819
	if (eeepc->bluetooth_rfkill) {
		rfkill_unregister(eeepc->bluetooth_rfkill);
		rfkill_destroy(eeepc->bluetooth_rfkill);
		eeepc->bluetooth_rfkill = NULL;
A
Alan Jenkins 已提交
820
	}
A
Alan Jenkins 已提交
821 822 823 824
	if (eeepc->wwan3g_rfkill) {
		rfkill_unregister(eeepc->wwan3g_rfkill);
		rfkill_destroy(eeepc->wwan3g_rfkill);
		eeepc->wwan3g_rfkill = NULL;
A
Alan Jenkins 已提交
825
	}
A
Alan Jenkins 已提交
826 827 828 829
	if (eeepc->wimax_rfkill) {
		rfkill_unregister(eeepc->wimax_rfkill);
		rfkill_destroy(eeepc->wimax_rfkill);
		eeepc->wimax_rfkill = NULL;
A
Alan Jenkins 已提交
830 831 832
	}
}

833
static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
834 835 836
{
	int result = 0;

A
Alan Jenkins 已提交
837
	mutex_init(&eeepc->hotplug_lock);
A
Alan Jenkins 已提交
838

839 840 841
	result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
				  "eeepc-wlan", RFKILL_TYPE_WLAN,
				  CM_ASL_WLAN);
A
Alan Jenkins 已提交
842 843 844 845

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

846 847 848
	result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
				  "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
				  CM_ASL_BLUETOOTH);
A
Alan Jenkins 已提交
849 850 851 852

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

853 854 855
	result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
				  "eeepc-wwan3g", RFKILL_TYPE_WWAN,
				  CM_ASL_3G);
A
Alan Jenkins 已提交
856 857 858 859

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

860 861 862
	result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
				  "eeepc-wimax", RFKILL_TYPE_WIMAX,
				  CM_ASL_WIMAX);
A
Alan Jenkins 已提交
863 864 865 866

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

867 868 869
	if (eeepc->hotplug_disabled)
		return 0;

870
	result = eeepc_setup_pci_hotplug(eeepc);
A
Alan Jenkins 已提交
871 872 873 874 875 876 877
	/*
	 * If we get -EBUSY then something else is handling the PCI hotplug -
	 * don't fail in this case
	 */
	if (result == -EBUSY)
		result = 0;

878 879 880
	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 已提交
881 882 883 884
	/*
	 * Refresh pci hotplug in case the rfkill state was changed during
	 * setup.
	 */
885
	eeepc_rfkill_hotplug(eeepc);
A
Alan Jenkins 已提交
886 887 888

exit:
	if (result && result != -ENODEV)
889
		eeepc_rfkill_exit(eeepc);
A
Alan Jenkins 已提交
890 891 892
	return result;
}

893
/*
A
Alan Jenkins 已提交
894
 * Platform driver - hibernate/resume callbacks
895
 */
896
static int eeepc_hotk_thaw(struct device *device)
897
{
898 899
	struct eeepc_laptop *eeepc = dev_get_drvdata(device);

A
Alan Jenkins 已提交
900
	if (eeepc->wlan_rfkill) {
901 902
		bool wlan;

903 904 905
		/*
		 * Work around bios bug - acpi _PTS turns off the wireless led
		 * during suspend.  Normally it restores it on resume, but
906
		 * we should kick it ourselves in case hibernation is aborted.
907
		 */
908 909
		wlan = get_acpi(eeepc, CM_ASL_WLAN);
		set_acpi(eeepc, CM_ASL_WLAN, wlan);
910 911 912 913
	}

	return 0;
}
914

915
static int eeepc_hotk_restore(struct device *device)
916
{
917 918
	struct eeepc_laptop *eeepc = dev_get_drvdata(device);

919
	/* Refresh both wlan rfkill state and pci hotplug */
A
Alan Jenkins 已提交
920
	if (eeepc->wlan_rfkill)
921
		eeepc_rfkill_hotplug(eeepc);
922

A
Alan Jenkins 已提交
923 924
	if (eeepc->bluetooth_rfkill)
		rfkill_set_sw_state(eeepc->bluetooth_rfkill,
925
				    get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);
A
Alan Jenkins 已提交
926 927
	if (eeepc->wwan3g_rfkill)
		rfkill_set_sw_state(eeepc->wwan3g_rfkill,
928
				    get_acpi(eeepc, CM_ASL_3G) != 1);
A
Alan Jenkins 已提交
929 930
	if (eeepc->wimax_rfkill)
		rfkill_set_sw_state(eeepc->wimax_rfkill,
931
				    get_acpi(eeepc, CM_ASL_WIMAX) != 1);
932 933 934 935

	return 0;
}

L
Len Brown 已提交
936
static const struct dev_pm_ops eeepc_pm_ops = {
937 938
	.thaw = eeepc_hotk_thaw,
	.restore = eeepc_hotk_restore,
A
Alan Jenkins 已提交
939 940 941 942
};

static struct platform_driver platform_driver = {
	.driver = {
A
Alan Jenkins 已提交
943
		.name = EEEPC_LAPTOP_FILE,
A
Alan Jenkins 已提交
944 945 946 947 948
		.owner = THIS_MODULE,
		.pm = &eeepc_pm_ops,
	}
};

949
/*
A
Alan Jenkins 已提交
950
 * Hwmon device
951
 */
A
Alan Jenkins 已提交
952 953 954 955 956 957 958 959 960

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

961 962
static int eeepc_get_fan_pwm(void)
{
963
	u8 value = 0;
964

965 966
	ec_read(EEEPC_EC_FAN_PWM, &value);
	return value * 255 / 100;
967 968 969 970
}

static void eeepc_set_fan_pwm(int value)
{
C
Corentin Chary 已提交
971 972
	value = SENSORS_LIMIT(value, 0, 255);
	value = value * 100 / 255;
973
	ec_write(EEEPC_EC_FAN_PWM, value);
974 975 976 977
}

static int eeepc_get_fan_rpm(void)
{
978 979
	u8 high = 0;
	u8 low = 0;
980

981 982 983
	ec_read(EEEPC_EC_FAN_HRPM, &high);
	ec_read(EEEPC_EC_FAN_LRPM, &low);
	return high << 8 | low;
984 985 986 987
}

static int eeepc_get_fan_ctrl(void)
{
988
	u8 value = 0;
989

990
	ec_read(EEEPC_EC_FAN_CTRL, &value);
991 992 993 994
	if (value & 0x02)
		return 1; /* manual */
	else
		return 2; /* automatic */
995 996 997 998
}

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

1001
	ec_read(EEEPC_EC_FAN_CTRL, &value);
1002
	if (manual == 1)
1003 1004 1005
		value |= 0x02;
	else
		value &= ~0x02;
1006
	ec_write(EEEPC_EC_FAN_CTRL, value);
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
}

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 已提交
1040
EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
1041 1042 1043 1044
			 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 已提交
1045 1046 1047 1048 1049 1050 1051
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);

1052
static struct attribute *hwmon_attributes[] = {
C
Corentin Chary 已提交
1053
	&sensor_dev_attr_pwm1.dev_attr.attr,
1054 1055
	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
C
Corentin Chary 已提交
1056
	&sensor_dev_attr_name.dev_attr.attr,
1057 1058 1059 1060 1061 1062 1063
	NULL
};

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

1064
static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc)
C
Corentin Chary 已提交
1065
{
1066 1067
	struct device *hwmon;

1068
	hwmon = eeepc->hwmon_device;
1069
	if (!hwmon)
1070
		return;
1071 1072
	sysfs_remove_group(&hwmon->kobj,
			   &hwmon_attribute_group);
1073
	hwmon_device_unregister(hwmon);
1074
	eeepc->hwmon_device = NULL;
1075 1076
}

1077
static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
1078
{
A
Alan Jenkins 已提交
1079
	struct device *hwmon;
1080 1081
	int result;

1082
	hwmon = hwmon_device_register(&eeepc->platform_device->dev);
A
Alan Jenkins 已提交
1083 1084
	if (IS_ERR(hwmon)) {
		pr_err("Could not register eeepc hwmon device\n");
1085
		eeepc->hwmon_device = NULL;
A
Alan Jenkins 已提交
1086
		return PTR_ERR(hwmon);
1087
	}
1088
	eeepc->hwmon_device = hwmon;
A
Alan Jenkins 已提交
1089 1090 1091
	result = sysfs_create_group(&hwmon->kobj,
				    &hwmon_attribute_group);
	if (result)
1092
		eeepc_hwmon_exit(eeepc);
A
Alan Jenkins 已提交
1093
	return result;
1094 1095
}

A
Alan Jenkins 已提交
1096 1097 1098 1099
/*
 * Backlight device
 */
static int read_brightness(struct backlight_device *bd)
1100
{
1101 1102 1103
	struct eeepc_laptop *eeepc = bl_get_data(bd);

	return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
C
Corentin Chary 已提交
1104 1105
}

A
Alan Jenkins 已提交
1106
static int set_brightness(struct backlight_device *bd, int value)
1107
{
1108
	struct eeepc_laptop *eeepc = bl_get_data(bd);
1109

1110
	return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
1111 1112
}

A
Alan Jenkins 已提交
1113
static int update_bl_status(struct backlight_device *bd)
1114
{
A
Alan Jenkins 已提交
1115 1116
	return set_brightness(bd, bd->props.brightness);
}
1117

A
Alan Jenkins 已提交
1118 1119 1120 1121
static struct backlight_ops eeepcbl_ops = {
	.get_brightness = read_brightness,
	.update_status = update_bl_status,
};
1122

1123
static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1124
{
1125
	struct backlight_device *bd = eeepc->backlight_device;
A
Alan Jenkins 已提交
1126
	int old = bd->props.brightness;
1127

A
Alan Jenkins 已提交
1128
	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1129

A
Alan Jenkins 已提交
1130
	return old;
1131 1132
}

1133
static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
1134
{
1135
	struct backlight_properties props;
C
Corentin Chary 已提交
1136
	struct backlight_device *bd;
1137

1138 1139
	memset(&props, 0, sizeof(struct backlight_properties));
	props.max_brightness = 15;
1140
	bd = backlight_device_register(EEEPC_LAPTOP_FILE,
1141 1142
				       &eeepc->platform_device->dev, eeepc,
				       &eeepcbl_ops, &props);
C
Corentin Chary 已提交
1143
	if (IS_ERR(bd)) {
1144
		pr_err("Could not register eeepc backlight device\n");
1145
		eeepc->backlight_device = NULL;
C
Corentin Chary 已提交
1146 1147
		return PTR_ERR(bd);
	}
1148 1149
	eeepc->backlight_device = bd;
	bd->props.brightness = read_brightness(bd);
C
Corentin Chary 已提交
1150 1151 1152 1153
	bd->props.power = FB_BLANK_UNBLANK;
	backlight_update_status(bd);
	return 0;
}
1154

1155
static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
1156
{
1157 1158 1159
	if (eeepc->backlight_device)
		backlight_device_unregister(eeepc->backlight_device);
	eeepc->backlight_device = NULL;
A
Alan Jenkins 已提交
1160
}
1161 1162


A
Alan Jenkins 已提交
1163 1164 1165
/*
 * Input device (i.e. hotkeys)
 */
1166
static int eeepc_input_init(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1167
{
1168 1169
	struct input_dev *input;
	int error;
1170

1171 1172 1173 1174
	input = input_allocate_device();
	if (!input) {
		pr_info("Unable to allocate input device\n");
		return -ENOMEM;
1175
	}
1176

1177 1178 1179 1180
	input->name = "Asus EeePC extra buttons";
	input->phys = EEEPC_LAPTOP_FILE "/input0";
	input->id.bustype = BUS_HOST;
	input->dev.parent = &eeepc->platform_device->dev;
1181

1182 1183 1184 1185
	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 已提交
1186
	}
A
Alan Jenkins 已提交
1187

1188 1189 1190 1191
	error = input_register_device(input);
	if (error) {
		pr_err("Unable to register input device\n");
		goto err_free_keymap;
1192
	}
A
Alan Jenkins 已提交
1193

1194
	eeepc->inputdev = input;
1195
	return 0;
1196

1197
err_free_keymap:
1198
	sparse_keymap_free(input);
1199
err_free_dev:
1200 1201
	input_free_device(input);
	return error;
1202 1203
}

1204
static void eeepc_input_exit(struct eeepc_laptop *eeepc)
E
Eric Cooper 已提交
1205
{
1206
	if (eeepc->inputdev) {
1207
		sparse_keymap_free(eeepc->inputdev);
A
Alan Jenkins 已提交
1208
		input_unregister_device(eeepc->inputdev);
1209
	}
1210
	eeepc->inputdev = NULL;
A
Alan Jenkins 已提交
1211
}
C
Corentin Chary 已提交
1212

A
Alan Jenkins 已提交
1213 1214 1215
/*
 * ACPI driver
 */
A
Alan Jenkins 已提交
1216
static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
A
Alan Jenkins 已提交
1217
{
1218
	struct eeepc_laptop *eeepc = acpi_driver_data(device);
A
Alan Jenkins 已提交
1219
	u16 count;
C
Corentin Chary 已提交
1220

A
Alan Jenkins 已提交
1221 1222
	if (event > ACPI_MAX_SYS_NOTIFY)
		return;
A
Alan Jenkins 已提交
1223
	count = eeepc->event_count[event % 128]++;
1224 1225 1226
	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 已提交
1227
					count);
C
Corentin Chary 已提交
1228

1229
	/* Brightness events are special */
A
Alan Jenkins 已提交
1230 1231
	if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {

1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
		/* 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)
				*/
			}
1252 1253
			sparse_keymap_report_event(eeepc->inputdev, event,
						   1, true);
A
Alan Jenkins 已提交
1254
		}
1255 1256
	} else {
		/* Everything else is a bona-fide keypress event */
1257
		sparse_keymap_report_event(eeepc->inputdev, event, 1, true);
A
Alan Jenkins 已提交
1258 1259 1260
	}
}

1261 1262 1263 1264
static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
{
	const char *model;

1265 1266 1267 1268
	model = dmi_get_system_info(DMI_PRODUCT_NAME);
	if (!model)
		return;

1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
	/*
	 * 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");
	}
1294 1295 1296 1297 1298 1299 1300 1301

	/*
	 * 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...
	 */
1302 1303
	if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||
	    strcmp(model, "1005PE") == 0) {
1304 1305 1306
		eeepc->hotplug_disabled = true;
		pr_info("wlan hotplug disabled\n");
	}
1307 1308
}

1309
static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
A
Alan Jenkins 已提交
1310 1311 1312 1313 1314
{
	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 已提交
1315 1316
	if (!(eeepc->cm_supported & (1 << cm))
	    && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
A
Alan Jenkins 已提交
1317 1318
		pr_info("%s (%x) not reported by BIOS,"
			" enabling anyway\n", name, 1 << cm);
A
Alan Jenkins 已提交
1319
		eeepc->cm_supported |= 1 << cm;
A
Alan Jenkins 已提交
1320 1321 1322
	}
}

1323
static void cmsg_quirks(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1324
{
1325 1326 1327 1328
	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 已提交
1329 1330
}

1331
static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1332 1333
{
	unsigned int init_flags;
E
Eric Cooper 已提交
1334 1335
	int result;

1336
	result = acpi_bus_get_status(eeepc->device);
1337
	if (result)
A
Alan Jenkins 已提交
1338
		return result;
1339
	if (!eeepc->device->status.present) {
A
Alan Jenkins 已提交
1340 1341 1342
		pr_err("Hotkey device not present, aborting\n");
		return -ENODEV;
	}
1343

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

A
Alan Jenkins 已提交
1347
	if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
A
Alan Jenkins 已提交
1348 1349
		pr_err("Hotkey initialization failed\n");
		return -ENODEV;
E
Eric Cooper 已提交
1350
	}
A
Alan Jenkins 已提交
1351 1352

	/* get control methods supported */
A
Alan Jenkins 已提交
1353
	if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
A
Alan Jenkins 已提交
1354 1355
		pr_err("Get control methods supported failed\n");
		return -ENODEV;
1356
	}
1357
	cmsg_quirks(eeepc);
A
Alan Jenkins 已提交
1358
	pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
1359

C
Corentin Chary 已提交
1360 1361 1362
	return 0;
}

1363
static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
A
Alan Jenkins 已提交
1364 1365 1366 1367 1368
{
	/*
	 * If the following call to set_acpi() fails, it's because there's no
	 * camera so we can ignore the error.
	 */
1369 1370
	if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
		set_acpi(eeepc, CM_ASL_CAMERA, 1);
A
Alan Jenkins 已提交
1371 1372
}

1373 1374
static bool eeepc_device_present;

A
Alan Jenkins 已提交
1375
static int __devinit eeepc_acpi_add(struct acpi_device *device)
E
Eric Cooper 已提交
1376
{
1377
	struct eeepc_laptop *eeepc;
E
Eric Cooper 已提交
1378 1379
	int result;

A
Alan Jenkins 已提交
1380 1381 1382
	pr_notice(EEEPC_LAPTOP_NAME "\n");
	eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
	if (!eeepc)
1383
		return -ENOMEM;
A
Alan Jenkins 已提交
1384 1385 1386 1387
	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;
1388
	eeepc->device = device;
1389

1390 1391
	eeepc->hotplug_disabled = hotplug_disabled;

1392 1393
	eeepc_dmi_check(eeepc);

1394
	result = eeepc_acpi_init(eeepc);
E
Eric Cooper 已提交
1395
	if (result)
1396
		goto fail_platform;
1397
	eeepc_enable_camera(eeepc);
1398

1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
	/*
	 * 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 已提交
1412
	if (result)
1413
		goto fail_platform;
1414 1415

	if (!acpi_video_backlight_support()) {
1416
		result = eeepc_backlight_init(eeepc);
1417 1418 1419
		if (result)
			goto fail_backlight;
	} else
1420
		pr_info("Backlight controlled by ACPI video driver\n");
1421

1422
	result = eeepc_input_init(eeepc);
1423 1424 1425
	if (result)
		goto fail_input;

1426
	result = eeepc_hwmon_init(eeepc);
1427 1428 1429
	if (result)
		goto fail_hwmon;

1430
	result = eeepc_led_init(eeepc);
C
Corentin Chary 已提交
1431 1432 1433
	if (result)
		goto fail_led;

1434
	result = eeepc_rfkill_init(eeepc);
1435 1436 1437
	if (result)
		goto fail_rfkill;

1438
	eeepc_device_present = true;
E
Eric Cooper 已提交
1439
	return 0;
1440

1441
fail_rfkill:
1442
	eeepc_led_exit(eeepc);
C
Corentin Chary 已提交
1443
fail_led:
1444
	eeepc_hwmon_exit(eeepc);
1445
fail_hwmon:
1446
	eeepc_input_exit(eeepc);
1447
fail_input:
1448
	eeepc_backlight_exit(eeepc);
1449
fail_backlight:
1450
	eeepc_platform_exit(eeepc);
1451
fail_platform:
A
Alan Jenkins 已提交
1452
	kfree(eeepc);
1453

E
Eric Cooper 已提交
1454 1455 1456
	return result;
}

A
Alan Jenkins 已提交
1457
static int eeepc_acpi_remove(struct acpi_device *device, int type)
1458
{
1459
	struct eeepc_laptop *eeepc = acpi_driver_data(device);
1460

1461 1462 1463 1464 1465 1466
	eeepc_backlight_exit(eeepc);
	eeepc_rfkill_exit(eeepc);
	eeepc_input_exit(eeepc);
	eeepc_hwmon_exit(eeepc);
	eeepc_led_exit(eeepc);
	eeepc_platform_exit(eeepc);
1467

A
Alan Jenkins 已提交
1468
	kfree(eeepc);
1469 1470 1471
	return 0;
}

A
Alan Jenkins 已提交
1472 1473

static const struct acpi_device_id eeepc_device_ids[] = {
A
Alan Jenkins 已提交
1474
	{EEEPC_ACPI_HID, 0},
A
Alan Jenkins 已提交
1475 1476 1477 1478
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);

A
Alan Jenkins 已提交
1479 1480 1481
static struct acpi_driver eeepc_acpi_driver = {
	.name = EEEPC_LAPTOP_NAME,
	.class = EEEPC_ACPI_CLASS,
A
Alan Jenkins 已提交
1482 1483 1484 1485
	.owner = THIS_MODULE,
	.ids = eeepc_device_ids,
	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
	.ops = {
A
Alan Jenkins 已提交
1486 1487 1488
		.add = eeepc_acpi_add,
		.remove = eeepc_acpi_remove,
		.notify = eeepc_acpi_notify,
A
Alan Jenkins 已提交
1489 1490 1491 1492
	},
};


1493 1494 1495 1496
static int __init eeepc_laptop_init(void)
{
	int result;

1497
	result = platform_driver_register(&platform_driver);
1498 1499
	if (result < 0)
		return result;
1500

A
Alan Jenkins 已提交
1501
	result = acpi_bus_register_driver(&eeepc_acpi_driver);
1502 1503
	if (result < 0)
		goto fail_acpi_driver;
1504

1505
	if (!eeepc_device_present) {
1506 1507
		result = -ENODEV;
		goto fail_no_device;
1508
	}
1509

1510
	return 0;
1511 1512

fail_no_device:
A
Alan Jenkins 已提交
1513
	acpi_bus_unregister_driver(&eeepc_acpi_driver);
1514 1515 1516
fail_acpi_driver:
	platform_driver_unregister(&platform_driver);
	return result;
1517 1518 1519 1520
}

static void __exit eeepc_laptop_exit(void)
{
A
Alan Jenkins 已提交
1521
	acpi_bus_unregister_driver(&eeepc_acpi_driver);
1522
	platform_driver_unregister(&platform_driver);
1523 1524
}

E
Eric Cooper 已提交
1525 1526
module_init(eeepc_laptop_init);
module_exit(eeepc_laptop_exit);