]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/platform/x86/eeepc-laptop.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg...
[net-next-2.6.git] / drivers / platform / x86 / eeepc-laptop.c
CommitLineData
e59f8796
EC
1/*
2 * eepc-laptop.c - Asus Eee PC extras
3 *
4 * Based on asus_acpi.c as patched for the Eee PC by Asus:
5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
6 * Based on eee.c from eeepc-linux
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/types.h>
23#include <linux/platform_device.h>
a5fa429b
CC
24#include <linux/backlight.h>
25#include <linux/fb.h>
e1faa9da
CC
26#include <linux/hwmon.h>
27#include <linux/hwmon-sysfs.h>
e59f8796
EC
28#include <acpi/acpi_drivers.h>
29#include <acpi/acpi_bus.h>
30#include <linux/uaccess.h>
a195dcdc
MG
31#include <linux/input.h>
32#include <linux/rfkill.h>
e59f8796
EC
33
34#define EEEPC_LAPTOP_VERSION "0.1"
35
36#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
37#define EEEPC_HOTK_FILE "eeepc"
38#define EEEPC_HOTK_CLASS "hotkey"
39#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
40#define EEEPC_HOTK_HID "ASUS010"
41
42#define EEEPC_LOG EEEPC_HOTK_FILE ": "
43#define EEEPC_ERR KERN_ERR EEEPC_LOG
44#define EEEPC_WARNING KERN_WARNING EEEPC_LOG
45#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
46#define EEEPC_INFO KERN_INFO EEEPC_LOG
47
48/*
49 * Definitions for Asus EeePC
50 */
51#define NOTIFY_WLAN_ON 0x10
a5fa429b
CC
52#define NOTIFY_BRN_MIN 0x20
53#define NOTIFY_BRN_MAX 0x2f
e59f8796
EC
54
55enum {
56 DISABLE_ASL_WLAN = 0x0001,
57 DISABLE_ASL_BLUETOOTH = 0x0002,
58 DISABLE_ASL_IRDA = 0x0004,
59 DISABLE_ASL_CAMERA = 0x0008,
60 DISABLE_ASL_TV = 0x0010,
61 DISABLE_ASL_GPS = 0x0020,
62 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
63 DISABLE_ASL_MODEM = 0x0080,
64 DISABLE_ASL_CARDREADER = 0x0100
65};
66
67enum {
68 CM_ASL_WLAN = 0,
69 CM_ASL_BLUETOOTH,
70 CM_ASL_IRDA,
71 CM_ASL_1394,
72 CM_ASL_CAMERA,
73 CM_ASL_TV,
74 CM_ASL_GPS,
75 CM_ASL_DVDROM,
76 CM_ASL_DISPLAYSWITCH,
77 CM_ASL_PANELBRIGHT,
78 CM_ASL_BIOSFLASH,
79 CM_ASL_ACPIFLASH,
80 CM_ASL_CPUFV,
81 CM_ASL_CPUTEMPERATURE,
82 CM_ASL_FANCPU,
83 CM_ASL_FANCHASSIS,
84 CM_ASL_USBPORT1,
85 CM_ASL_USBPORT2,
86 CM_ASL_USBPORT3,
87 CM_ASL_MODEM,
88 CM_ASL_CARDREADER,
89 CM_ASL_LID
90};
91
14109461 92static const char *cm_getv[] = {
3af9bfcb 93 "WLDG", "BTHG", NULL, NULL,
e59f8796
EC
94 "CAMG", NULL, NULL, NULL,
95 NULL, "PBLG", NULL, NULL,
96 "CFVG", NULL, NULL, NULL,
97 "USBG", NULL, NULL, "MODG",
98 "CRDG", "LIDG"
99};
100
14109461 101static const char *cm_setv[] = {
3af9bfcb 102 "WLDS", "BTHS", NULL, NULL,
e59f8796
EC
103 "CAMS", NULL, NULL, NULL,
104 "SDSP", "PBLS", "HDPS", NULL,
105 "CFVS", NULL, NULL, NULL,
106 "USBG", NULL, NULL, "MODS",
107 "CRDS", NULL
108};
109
e1faa9da
CC
110#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
111
112#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
113#define EEEPC_EC_SC02 0x63
114#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
115#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
116#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
117#define EEEPC_EC_SFB3 0xD3
118
e59f8796
EC
119/*
120 * This is the main structure, we can use it to store useful information
121 * about the hotk device
122 */
123struct eeepc_hotk {
124 struct acpi_device *device; /* the device we are in */
125 acpi_handle handle; /* the handle of the hotk device */
126 u32 cm_supported; /* the control methods supported
127 by this BIOS */
128 uint init_flag; /* Init flags */
129 u16 event_count[128]; /* count for each event */
a195dcdc
MG
130 struct input_dev *inputdev;
131 u16 *keycode_map;
132 struct rfkill *eeepc_wlan_rfkill;
133 struct rfkill *eeepc_bluetooth_rfkill;
e59f8796
EC
134};
135
136/* The actual device the driver binds to */
137static struct eeepc_hotk *ehotk;
138
139/* Platform device/driver */
140static struct platform_driver platform_driver = {
141 .driver = {
142 .name = EEEPC_HOTK_FILE,
143 .owner = THIS_MODULE,
144 }
145};
146
147static struct platform_device *platform_device;
148
a195dcdc
MG
149struct key_entry {
150 char type;
151 u8 code;
152 u16 keycode;
153};
154
155enum { KE_KEY, KE_END };
156
157static struct key_entry eeepc_keymap[] = {
158 /* Sleep already handled via generic ACPI code */
159 {KE_KEY, 0x10, KEY_WLAN },
160 {KE_KEY, 0x12, KEY_PROG1 },
161 {KE_KEY, 0x13, KEY_MUTE },
162 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
163 {KE_KEY, 0x15, KEY_VOLUMEUP },
164 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
165 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
166 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
167 {KE_END, 0},
168};
169
e59f8796
EC
170/*
171 * The hotkey driver declaration
172 */
173static int eeepc_hotk_add(struct acpi_device *device);
174static int eeepc_hotk_remove(struct acpi_device *device, int type);
175
176static const struct acpi_device_id eeepc_device_ids[] = {
177 {EEEPC_HOTK_HID, 0},
178 {"", 0},
179};
180MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
181
182static struct acpi_driver eeepc_hotk_driver = {
183 .name = EEEPC_HOTK_NAME,
184 .class = EEEPC_HOTK_CLASS,
185 .ids = eeepc_device_ids,
186 .ops = {
187 .add = eeepc_hotk_add,
188 .remove = eeepc_hotk_remove,
189 },
190};
191
a5fa429b
CC
192/* The backlight device /sys/class/backlight */
193static struct backlight_device *eeepc_backlight_device;
194
e1faa9da
CC
195/* The hwmon device */
196static struct device *eeepc_hwmon_device;
197
a5fa429b
CC
198/*
199 * The backlight class declaration
200 */
201static int read_brightness(struct backlight_device *bd);
202static int update_bl_status(struct backlight_device *bd);
203static struct backlight_ops eeepcbl_ops = {
204 .get_brightness = read_brightness,
205 .update_status = update_bl_status,
206};
207
e59f8796
EC
208MODULE_AUTHOR("Corentin Chary, Eric Cooper");
209MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
210MODULE_LICENSE("GPL");
211
212/*
213 * ACPI Helpers
214 */
215static int write_acpi_int(acpi_handle handle, const char *method, int val,
216 struct acpi_buffer *output)
217{
218 struct acpi_object_list params;
219 union acpi_object in_obj;
220 acpi_status status;
221
222 params.count = 1;
223 params.pointer = &in_obj;
224 in_obj.type = ACPI_TYPE_INTEGER;
225 in_obj.integer.value = val;
226
227 status = acpi_evaluate_object(handle, (char *)method, &params, output);
228 return (status == AE_OK ? 0 : -1);
229}
230
231static int read_acpi_int(acpi_handle handle, const char *method, int *val)
232{
233 acpi_status status;
27663c58 234 unsigned long long result;
e59f8796
EC
235
236 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
237 if (ACPI_FAILURE(status)) {
238 *val = -1;
239 return -1;
240 } else {
241 *val = result;
242 return 0;
243 }
244}
245
246static int set_acpi(int cm, int value)
247{
248 if (ehotk->cm_supported & (0x1 << cm)) {
249 const char *method = cm_setv[cm];
250 if (method == NULL)
251 return -ENODEV;
252 if (write_acpi_int(ehotk->handle, method, value, NULL))
253 printk(EEEPC_WARNING "Error writing %s\n", method);
254 }
255 return 0;
256}
257
258static int get_acpi(int cm)
259{
260 int value = -1;
261 if ((ehotk->cm_supported & (0x1 << cm))) {
262 const char *method = cm_getv[cm];
263 if (method == NULL)
264 return -ENODEV;
265 if (read_acpi_int(ehotk->handle, method, &value))
266 printk(EEEPC_WARNING "Error reading %s\n", method);
267 }
268 return value;
269}
270
a5fa429b
CC
271/*
272 * Backlight
273 */
274static int read_brightness(struct backlight_device *bd)
275{
276 return get_acpi(CM_ASL_PANELBRIGHT);
277}
278
279static int set_brightness(struct backlight_device *bd, int value)
280{
281 value = max(0, min(15, value));
282 return set_acpi(CM_ASL_PANELBRIGHT, value);
283}
284
285static int update_bl_status(struct backlight_device *bd)
286{
287 return set_brightness(bd, bd->props.brightness);
288}
289
a195dcdc
MG
290/*
291 * Rfkill helpers
292 */
293
294static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
295{
296 if (state == RFKILL_STATE_SOFT_BLOCKED)
297 return set_acpi(CM_ASL_WLAN, 0);
298 else
299 return set_acpi(CM_ASL_WLAN, 1);
300}
301
302static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
303{
304 if (get_acpi(CM_ASL_WLAN) == 1)
305 *state = RFKILL_STATE_UNBLOCKED;
306 else
307 *state = RFKILL_STATE_SOFT_BLOCKED;
308 return 0;
309}
310
311static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
312{
313 if (state == RFKILL_STATE_SOFT_BLOCKED)
314 return set_acpi(CM_ASL_BLUETOOTH, 0);
315 else
316 return set_acpi(CM_ASL_BLUETOOTH, 1);
317}
318
319static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
320{
321 if (get_acpi(CM_ASL_BLUETOOTH) == 1)
322 *state = RFKILL_STATE_UNBLOCKED;
323 else
324 *state = RFKILL_STATE_SOFT_BLOCKED;
325 return 0;
326}
327
e59f8796
EC
328/*
329 * Sys helpers
330 */
331static int parse_arg(const char *buf, unsigned long count, int *val)
332{
333 if (!count)
334 return 0;
335 if (sscanf(buf, "%i", val) != 1)
336 return -EINVAL;
337 return count;
338}
339
340static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
341{
342 int rv, value;
343
344 rv = parse_arg(buf, count, &value);
345 if (rv > 0)
346 set_acpi(cm, value);
347 return rv;
348}
349
350static ssize_t show_sys_acpi(int cm, char *buf)
351{
352 return sprintf(buf, "%d\n", get_acpi(cm));
353}
354
355#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
356 static ssize_t show_##_name(struct device *dev, \
357 struct device_attribute *attr, \
358 char *buf) \
359 { \
360 return show_sys_acpi(_cm, buf); \
361 } \
362 static ssize_t store_##_name(struct device *dev, \
363 struct device_attribute *attr, \
364 const char *buf, size_t count) \
365 { \
366 return store_sys_acpi(_cm, buf, count); \
367 } \
368 static struct device_attribute dev_attr_##_name = { \
369 .attr = { \
370 .name = __stringify(_name), \
371 .mode = 0644 }, \
372 .show = show_##_name, \
373 .store = store_##_name, \
374 }
375
376EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
377EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
378EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
e59f8796
EC
379
380static struct attribute *platform_attributes[] = {
381 &dev_attr_camera.attr,
382 &dev_attr_cardr.attr,
383 &dev_attr_disp.attr,
e59f8796
EC
384 NULL
385};
386
387static struct attribute_group platform_attribute_group = {
388 .attrs = platform_attributes
389};
390
391/*
392 * Hotkey functions
393 */
a195dcdc
MG
394static struct key_entry *eepc_get_entry_by_scancode(int code)
395{
396 struct key_entry *key;
397
398 for (key = eeepc_keymap; key->type != KE_END; key++)
399 if (code == key->code)
400 return key;
401
402 return NULL;
403}
404
405static struct key_entry *eepc_get_entry_by_keycode(int code)
406{
407 struct key_entry *key;
408
409 for (key = eeepc_keymap; key->type != KE_END; key++)
410 if (code == key->keycode && key->type == KE_KEY)
411 return key;
412
413 return NULL;
414}
415
416static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
417{
418 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
419
420 if (key && key->type == KE_KEY) {
421 *keycode = key->keycode;
422 return 0;
423 }
424
425 return -EINVAL;
426}
427
428static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
429{
430 struct key_entry *key;
431 int old_keycode;
432
433 if (keycode < 0 || keycode > KEY_MAX)
434 return -EINVAL;
435
436 key = eepc_get_entry_by_scancode(scancode);
437 if (key && key->type == KE_KEY) {
438 old_keycode = key->keycode;
439 key->keycode = keycode;
440 set_bit(keycode, dev->keybit);
441 if (!eepc_get_entry_by_keycode(old_keycode))
442 clear_bit(old_keycode, dev->keybit);
443 return 0;
444 }
445
446 return -EINVAL;
447}
448
e59f8796
EC
449static int eeepc_hotk_check(void)
450{
a195dcdc 451 const struct key_entry *key;
e59f8796
EC
452 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
453 int result;
454
455 result = acpi_bus_get_status(ehotk->device);
456 if (result)
457 return result;
458 if (ehotk->device->status.present) {
459 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
460 &buffer)) {
461 printk(EEEPC_ERR "Hotkey initialization failed\n");
462 return -ENODEV;
463 } else {
464 printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
465 ehotk->init_flag);
466 }
467 /* get control methods supported */
468 if (read_acpi_int(ehotk->handle, "CMSG"
469 , &ehotk->cm_supported)) {
470 printk(EEEPC_ERR
471 "Get control methods supported failed\n");
472 return -ENODEV;
473 } else {
474 printk(EEEPC_INFO
475 "Get control methods supported: 0x%x\n",
476 ehotk->cm_supported);
477 }
a195dcdc
MG
478 ehotk->inputdev = input_allocate_device();
479 if (!ehotk->inputdev) {
480 printk(EEEPC_INFO "Unable to allocate input device\n");
481 return 0;
482 }
483 ehotk->inputdev->name = "Asus EeePC extra buttons";
484 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
485 ehotk->inputdev->id.bustype = BUS_HOST;
486 ehotk->inputdev->getkeycode = eeepc_getkeycode;
487 ehotk->inputdev->setkeycode = eeepc_setkeycode;
488
489 for (key = eeepc_keymap; key->type != KE_END; key++) {
490 switch (key->type) {
491 case KE_KEY:
492 set_bit(EV_KEY, ehotk->inputdev->evbit);
493 set_bit(key->keycode, ehotk->inputdev->keybit);
494 break;
495 }
496 }
497 result = input_register_device(ehotk->inputdev);
498 if (result) {
499 printk(EEEPC_INFO "Unable to register input device\n");
500 input_free_device(ehotk->inputdev);
501 return 0;
502 }
e59f8796
EC
503 } else {
504 printk(EEEPC_ERR "Hotkey device not present, aborting\n");
505 return -EINVAL;
506 }
507 return 0;
508}
509
a5fa429b
CC
510static void notify_brn(void)
511{
512 struct backlight_device *bd = eeepc_backlight_device;
513 bd->props.brightness = read_brightness(bd);
514}
515
e59f8796
EC
516static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
517{
a195dcdc 518 static struct key_entry *key;
e59f8796
EC
519 if (!ehotk)
520 return;
a5fa429b
CC
521 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
522 notify_brn();
e59f8796
EC
523 acpi_bus_generate_proc_event(ehotk->device, event,
524 ehotk->event_count[event % 128]++);
a195dcdc
MG
525 if (ehotk->inputdev) {
526 key = eepc_get_entry_by_scancode(event);
527 if (key) {
528 switch (key->type) {
529 case KE_KEY:
530 input_report_key(ehotk->inputdev, key->keycode,
531 1);
532 input_sync(ehotk->inputdev);
533 input_report_key(ehotk->inputdev, key->keycode,
534 0);
535 input_sync(ehotk->inputdev);
536 break;
537 }
538 }
539 }
e59f8796
EC
540}
541
542static int eeepc_hotk_add(struct acpi_device *device)
543{
544 acpi_status status = AE_OK;
545 int result;
546
547 if (!device)
548 return -EINVAL;
549 printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
550 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
551 if (!ehotk)
552 return -ENOMEM;
553 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
554 ehotk->handle = device->handle;
555 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
556 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
db89b4f0 557 device->driver_data = ehotk;
e59f8796
EC
558 ehotk->device = device;
559 result = eeepc_hotk_check();
560 if (result)
561 goto end;
562 status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
563 eeepc_hotk_notify, ehotk);
564 if (ACPI_FAILURE(status))
565 printk(EEEPC_ERR "Error installing notify handler\n");
a195dcdc
MG
566
567 if (get_acpi(CM_ASL_WLAN) != -1) {
568 ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
569 RFKILL_TYPE_WLAN);
570
571 if (!ehotk->eeepc_wlan_rfkill)
572 goto end;
573
574 ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
575 ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
576 ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
577 if (get_acpi(CM_ASL_WLAN) == 1)
578 ehotk->eeepc_wlan_rfkill->state =
579 RFKILL_STATE_UNBLOCKED;
580 else
581 ehotk->eeepc_wlan_rfkill->state =
582 RFKILL_STATE_SOFT_BLOCKED;
583 rfkill_register(ehotk->eeepc_wlan_rfkill);
584 }
585
586 if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
587 ehotk->eeepc_bluetooth_rfkill =
588 rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
589
590 if (!ehotk->eeepc_bluetooth_rfkill)
591 goto end;
592
593 ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
594 ehotk->eeepc_bluetooth_rfkill->toggle_radio =
595 eeepc_bluetooth_rfkill_set;
596 ehotk->eeepc_bluetooth_rfkill->get_state =
597 eeepc_bluetooth_rfkill_state;
598 if (get_acpi(CM_ASL_BLUETOOTH) == 1)
599 ehotk->eeepc_bluetooth_rfkill->state =
600 RFKILL_STATE_UNBLOCKED;
601 else
602 ehotk->eeepc_bluetooth_rfkill->state =
603 RFKILL_STATE_SOFT_BLOCKED;
604 rfkill_register(ehotk->eeepc_bluetooth_rfkill);
605 }
606
e59f8796
EC
607 end:
608 if (result) {
609 kfree(ehotk);
610 ehotk = NULL;
611 }
612 return result;
613}
614
615static int eeepc_hotk_remove(struct acpi_device *device, int type)
616{
617 acpi_status status = 0;
618
619 if (!device || !acpi_driver_data(device))
620 return -EINVAL;
621 status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
622 eeepc_hotk_notify);
623 if (ACPI_FAILURE(status))
624 printk(EEEPC_ERR "Error removing notify handler\n");
625 kfree(ehotk);
626 return 0;
627}
628
e1faa9da
CC
629/*
630 * Hwmon
631 */
632static int eeepc_get_fan_pwm(void)
633{
634 int value = 0;
635
636 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
04dcd84b 637 value = value * 255 / 100;
e1faa9da
CC
638 return (value);
639}
640
641static void eeepc_set_fan_pwm(int value)
642{
04dcd84b
CC
643 value = SENSORS_LIMIT(value, 0, 255);
644 value = value * 100 / 255;
e1faa9da
CC
645 ec_write(EEEPC_EC_SC02, value);
646}
647
648static int eeepc_get_fan_rpm(void)
649{
650 int high = 0;
651 int low = 0;
652
653 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
654 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
655 return (high << 8 | low);
656}
657
658static int eeepc_get_fan_ctrl(void)
659{
660 int value = 0;
661
662 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
663 return ((value & 0x02 ? 1 : 0));
664}
665
666static void eeepc_set_fan_ctrl(int manual)
667{
668 int value = 0;
669
670 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
671 if (manual)
672 value |= 0x02;
673 else
674 value &= ~0x02;
675 ec_write(EEEPC_EC_SFB3, value);
676}
677
678static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
679{
680 int rv, value;
681
682 rv = parse_arg(buf, count, &value);
683 if (rv > 0)
684 set(value);
685 return rv;
686}
687
688static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
689{
690 return sprintf(buf, "%d\n", get());
691}
692
693#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
694 static ssize_t show_##_name(struct device *dev, \
695 struct device_attribute *attr, \
696 char *buf) \
697 { \
698 return show_sys_hwmon(_set, buf); \
699 } \
700 static ssize_t store_##_name(struct device *dev, \
701 struct device_attribute *attr, \
702 const char *buf, size_t count) \
703 { \
704 return store_sys_hwmon(_get, buf, count); \
705 } \
706 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
707
708EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
04dcd84b 709EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
e1faa9da
CC
710 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
711EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
712 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
713
04dcd84b
CC
714static ssize_t
715show_name(struct device *dev, struct device_attribute *attr, char *buf)
716{
717 return sprintf(buf, "eeepc\n");
718}
719static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
720
e1faa9da 721static struct attribute *hwmon_attributes[] = {
04dcd84b 722 &sensor_dev_attr_pwm1.dev_attr.attr,
e1faa9da
CC
723 &sensor_dev_attr_fan1_input.dev_attr.attr,
724 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
04dcd84b 725 &sensor_dev_attr_name.dev_attr.attr,
e1faa9da
CC
726 NULL
727};
728
729static struct attribute_group hwmon_attribute_group = {
730 .attrs = hwmon_attributes
731};
732
e59f8796
EC
733/*
734 * exit/init
735 */
a5fa429b
CC
736static void eeepc_backlight_exit(void)
737{
738 if (eeepc_backlight_device)
739 backlight_device_unregister(eeepc_backlight_device);
a195dcdc
MG
740 if (ehotk->inputdev)
741 input_unregister_device(ehotk->inputdev);
742 if (ehotk->eeepc_wlan_rfkill)
743 rfkill_unregister(ehotk->eeepc_wlan_rfkill);
744 if (ehotk->eeepc_bluetooth_rfkill)
745 rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
a5fa429b
CC
746 eeepc_backlight_device = NULL;
747}
748
e1faa9da
CC
749static void eeepc_hwmon_exit(void)
750{
751 struct device *hwmon;
752
753 hwmon = eeepc_hwmon_device;
754 if (!hwmon)
755 return ;
e1faa9da
CC
756 sysfs_remove_group(&hwmon->kobj,
757 &hwmon_attribute_group);
f1441318 758 hwmon_device_unregister(hwmon);
e1faa9da
CC
759 eeepc_hwmon_device = NULL;
760}
761
e59f8796
EC
762static void __exit eeepc_laptop_exit(void)
763{
a5fa429b 764 eeepc_backlight_exit();
e1faa9da 765 eeepc_hwmon_exit();
e59f8796
EC
766 acpi_bus_unregister_driver(&eeepc_hotk_driver);
767 sysfs_remove_group(&platform_device->dev.kobj,
768 &platform_attribute_group);
769 platform_device_unregister(platform_device);
770 platform_driver_unregister(&platform_driver);
771}
772
a5fa429b
CC
773static int eeepc_backlight_init(struct device *dev)
774{
775 struct backlight_device *bd;
776
777 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
778 NULL, &eeepcbl_ops);
779 if (IS_ERR(bd)) {
780 printk(EEEPC_ERR
781 "Could not register eeepc backlight device\n");
782 eeepc_backlight_device = NULL;
783 return PTR_ERR(bd);
784 }
785 eeepc_backlight_device = bd;
786 bd->props.max_brightness = 15;
787 bd->props.brightness = read_brightness(NULL);
788 bd->props.power = FB_BLANK_UNBLANK;
789 backlight_update_status(bd);
790 return 0;
791}
792
e1faa9da
CC
793static int eeepc_hwmon_init(struct device *dev)
794{
795 struct device *hwmon;
796 int result;
797
798 hwmon = hwmon_device_register(dev);
799 if (IS_ERR(hwmon)) {
800 printk(EEEPC_ERR
801 "Could not register eeepc hwmon device\n");
802 eeepc_hwmon_device = NULL;
803 return PTR_ERR(hwmon);
804 }
805 eeepc_hwmon_device = hwmon;
806 result = sysfs_create_group(&hwmon->kobj,
807 &hwmon_attribute_group);
808 if (result)
809 eeepc_hwmon_exit();
810 return result;
811}
812
e59f8796
EC
813static int __init eeepc_laptop_init(void)
814{
815 struct device *dev;
816 int result;
817
818 if (acpi_disabled)
819 return -ENODEV;
820 result = acpi_bus_register_driver(&eeepc_hotk_driver);
821 if (result < 0)
822 return result;
823 if (!ehotk) {
824 acpi_bus_unregister_driver(&eeepc_hotk_driver);
825 return -ENODEV;
826 }
827 dev = acpi_get_physical_device(ehotk->device->handle);
a2bf8c01
TR
828
829 if (!acpi_video_backlight_support()) {
830 result = eeepc_backlight_init(dev);
831 if (result)
832 goto fail_backlight;
833 } else
834 printk(EEEPC_INFO "Backlight controlled by ACPI video "
835 "driver\n");
836
e1faa9da
CC
837 result = eeepc_hwmon_init(dev);
838 if (result)
839 goto fail_hwmon;
e59f8796
EC
840 /* Register platform stuff */
841 result = platform_driver_register(&platform_driver);
842 if (result)
843 goto fail_platform_driver;
844 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
845 if (!platform_device) {
846 result = -ENOMEM;
847 goto fail_platform_device1;
848 }
849 result = platform_device_add(platform_device);
850 if (result)
851 goto fail_platform_device2;
852 result = sysfs_create_group(&platform_device->dev.kobj,
853 &platform_attribute_group);
854 if (result)
855 goto fail_sysfs;
856 return 0;
857fail_sysfs:
858 platform_device_del(platform_device);
859fail_platform_device2:
860 platform_device_put(platform_device);
861fail_platform_device1:
862 platform_driver_unregister(&platform_driver);
863fail_platform_driver:
e1faa9da
CC
864 eeepc_hwmon_exit();
865fail_hwmon:
a5fa429b
CC
866 eeepc_backlight_exit();
867fail_backlight:
e59f8796
EC
868 return result;
869}
870
871module_init(eeepc_laptop_init);
872module_exit(eeepc_laptop_exit);