aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig10
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/acerhdf.c121
-rw-r--r--drivers/platform/x86/asus-laptop.c227
-rw-r--r--drivers/platform/x86/eeepc-laptop.c340
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c109
-rw-r--r--drivers/platform/x86/hp-wmi.c15
-rw-r--r--drivers/platform/x86/sony-laptop.c7
-rw-r--r--drivers/platform/x86/topstar-laptop.c265
-rw-r--r--drivers/platform/x86/wmi.c1
10 files changed, 792 insertions, 304 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 77c6097ced8..55ca39dea42 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -99,6 +99,7 @@ config FUJITSU_LAPTOP
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
+ depends on LEDS_CLASS || LEDS_CLASS=n
---help---
This is a driver for laptops built by Fujitsu:
@@ -396,6 +397,15 @@ config ACPI_ASUS
NOTE: This driver is deprecated and will probably be removed soon,
use asus-laptop instead.
+config TOPSTAR_LAPTOP
+ tristate "Topstar Laptop Extras"
+ depends on ACPI
+ depends on INPUT
+ ---help---
+ This driver adds support for hotkeys found on Topstar laptops.
+
+ If you have a Topstar laptop, say Y or M here.
+
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 641b8bfa553..d1c16210a51 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -19,4 +19,5 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
+obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index bdfee177eef..0a8f735f6c4 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -52,7 +52,7 @@
*/
#undef START_IN_KERNEL_MODE
-#define DRV_VER "0.5.13"
+#define DRV_VER "0.5.17"
/*
* According to the Atom N270 datasheet,
@@ -90,6 +90,7 @@ static unsigned int fanoff = 58;
static unsigned int verbose;
static unsigned int fanstate = ACERHDF_FAN_AUTO;
static char force_bios[16];
+static char force_product[16];
static unsigned int prev_interval;
struct thermal_zone_device *thz_dev;
struct thermal_cooling_device *cl_dev;
@@ -107,34 +108,62 @@ module_param(verbose, uint, 0600);
MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
module_param_string(force_bios, force_bios, 16, 0);
MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
+module_param_string(force_product, force_product, 16, 0);
+MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
+
+/*
+ * cmd_off: to switch the fan completely off / to check if the fan is off
+ * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
+ * the fan speed depending on the temperature
+ */
+struct fancmd {
+ u8 cmd_off;
+ u8 cmd_auto;
+};
/* BIOS settings */
struct bios_settings_t {
const char *vendor;
+ const char *product;
const char *version;
unsigned char fanreg;
unsigned char tempreg;
- unsigned char fancmd[2]; /* fan off and auto commands */
+ struct fancmd cmd;
};
/* Register addresses and values for different BIOS versions */
static const struct bios_settings_t bios_tbl[] = {
- {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
- {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
- {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
- {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
- {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
- {"", "", 0, 0, {0, 0} }
+ /* AOA110 */
+ {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
+ {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
+ {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
+ {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
+ {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+ /* AOA150 */
+ {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+ /* special BIOS / other */
+ {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
+ {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} },
+ {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} },
+ {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
+ {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} },
+ /* pewpew-terminator */
+ {"", "", "", 0, 0, {0, 0} }
};
static const struct bios_settings_t *bios_cfg __read_mostly;
-
static int acerhdf_get_temp(int *temp)
{
u8 read_temp;
@@ -150,13 +179,14 @@ static int acerhdf_get_temp(int *temp)
static int acerhdf_get_fanstate(int *state)
{
u8 fan;
- bool tmp;
if (ec_read(bios_cfg->fanreg, &fan))
return -EINVAL;
- tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
- *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
+ if (fan != bios_cfg->cmd.cmd_off)
+ *state = ACERHDF_FAN_AUTO;
+ else
+ *state = ACERHDF_FAN_OFF;
return 0;
}
@@ -175,7 +205,8 @@ static void acerhdf_change_fanstate(int state)
state = ACERHDF_FAN_AUTO;
}
- cmd = bios_cfg->fancmd[state];
+ cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off
+ : bios_cfg->cmd.cmd_auto;
fanstate = state;
ec_write(bios_cfg->fanreg, cmd);
@@ -408,7 +439,7 @@ struct thermal_cooling_device_ops acerhdf_cooling_ops = {
};
/* suspend / resume functionality */
-static int acerhdf_suspend(struct platform_device *dev, pm_message_t state)
+static int acerhdf_suspend(struct device *dev)
{
if (kernelmode)
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
@@ -419,14 +450,6 @@ static int acerhdf_suspend(struct platform_device *dev, pm_message_t state)
return 0;
}
-static int acerhdf_resume(struct platform_device *device)
-{
- if (verbose)
- pr_notice("resuming\n");
-
- return 0;
-}
-
static int __devinit acerhdf_probe(struct platform_device *device)
{
return 0;
@@ -437,15 +460,19 @@ static int acerhdf_remove(struct platform_device *device)
return 0;
}
-struct platform_driver acerhdf_drv = {
+static struct dev_pm_ops acerhdf_pm_ops = {
+ .suspend = acerhdf_suspend,
+ .freeze = acerhdf_suspend,
+};
+
+static struct platform_driver acerhdf_driver = {
.driver = {
- .name = "acerhdf",
+ .name = "acerhdf",
.owner = THIS_MODULE,
+ .pm = &acerhdf_pm_ops,
},
.probe = acerhdf_probe,
.remove = acerhdf_remove,
- .suspend = acerhdf_suspend,
- .resume = acerhdf_resume,
};
@@ -454,32 +481,40 @@ static int acerhdf_check_hardware(void)
{
char const *vendor, *version, *product;
int i;
+ unsigned long prod_len = 0;
/* get BIOS data */
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
version = dmi_get_system_info(DMI_BIOS_VERSION);
product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);
- if (!force_bios[0]) {
- if (strncmp(product, "AO", 2)) {
- pr_err("no Aspire One hardware found\n");
- return -EINVAL;
- }
- } else {
- pr_info("forcing BIOS version: %s\n", version);
+ if (force_bios[0]) {
version = force_bios;
+ pr_info("forcing BIOS version: %s\n", version);
+ kernelmode = 0;
+ }
+
+ if (force_product[0]) {
+ product = force_product;
+ pr_info("forcing BIOS product: %s\n", product);
kernelmode = 0;
}
+ prod_len = strlen(product);
+
if (verbose)
pr_info("BIOS info: %s %s, product: %s\n",
vendor, version, product);
/* search BIOS version and vendor in BIOS settings table */
for (i = 0; bios_tbl[i].version[0]; i++) {
- if (!strcmp(bios_tbl[i].vendor, vendor) &&
+ if (strlen(bios_tbl[i].product) >= prod_len &&
+ !strncmp(bios_tbl[i].product, product,
+ strlen(bios_tbl[i].product)) &&
+ !strcmp(bios_tbl[i].vendor, vendor) &&
!strcmp(bios_tbl[i].version, version)) {
bios_cfg = &bios_tbl[i];
break;
@@ -487,8 +522,8 @@ static int acerhdf_check_hardware(void)
}
if (!bios_cfg) {
- pr_err("unknown (unsupported) BIOS version %s/%s, "
- "please report, aborting!\n", vendor, version);
+ pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
+ "please report, aborting!\n", vendor, product, version);
return -EINVAL;
}
@@ -509,7 +544,7 @@ static int acerhdf_register_platform(void)
{
int err = 0;
- err = platform_driver_register(&acerhdf_drv);
+ err = platform_driver_register(&acerhdf_driver);
if (err)
return err;
@@ -525,7 +560,7 @@ static void acerhdf_unregister_platform(void)
return;
platform_device_del(acerhdf_dev);
- platform_driver_unregister(&acerhdf_drv);
+ platform_driver_unregister(&acerhdf_driver);
}
static int acerhdf_register_thermal(void)
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index db657bbeec9..b39d2bb3e75 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -77,15 +77,16 @@
* Flags for hotk status
* WL_ON and BT_ON are also used for wireless_status()
*/
-#define WL_ON 0x01 //internal Wifi
-#define BT_ON 0x02 //internal Bluetooth
-#define MLED_ON 0x04 //mail LED
-#define TLED_ON 0x08 //touchpad LED
-#define RLED_ON 0x10 //Record LED
-#define PLED_ON 0x20 //Phone LED
-#define GLED_ON 0x40 //Gaming LED
-#define LCD_ON 0x80 //LCD backlight
-#define GPS_ON 0x100 //GPS
+#define WL_ON 0x01 /* internal Wifi */
+#define BT_ON 0x02 /* internal Bluetooth */
+#define MLED_ON 0x04 /* mail LED */
+#define TLED_ON 0x08 /* touchpad LED */
+#define RLED_ON 0x10 /* Record LED */
+#define PLED_ON 0x20 /* Phone LED */
+#define GLED_ON 0x40 /* Gaming LED */
+#define LCD_ON 0x80 /* LCD backlight */
+#define GPS_ON 0x100 /* GPS */
+#define KEY_ON 0x200 /* Keyboard backlight */
#define ASUS_LOG ASUS_HOTK_FILE ": "
#define ASUS_ERR KERN_ERR ASUS_LOG
@@ -98,7 +99,8 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
MODULE_DESCRIPTION(ASUS_HOTK_NAME);
MODULE_LICENSE("GPL");
-/* WAPF defines the behavior of the Fn+Fx wlan key
+/*
+ * WAPF defines the behavior of the Fn+Fx wlan key
* The significance of values is yet to be found, but
* most of the time:
* 0x0 will do nothing
@@ -125,7 +127,8 @@ ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
/* LEDD */
ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
-/* Bluetooth and WLAN
+/*
+ * Bluetooth and WLAN
* WLED and BLED are not handled like other XLED, because in some dsdt
* they also control the WLAN/Bluetooth device.
*/
@@ -149,22 +152,32 @@ ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
/* Display */
ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
-ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G
- M6A M6V VX-1 V6J V6V W3Z */
- "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
- S5A M5A z33A W1Jc W2V G1 */
- "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
- "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
- "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */
- "\\_SB.PCI0.VGA.GETD", /* Z96F */
- "\\ACTD", /* A2D */
- "\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
- "\\DNXT", /* P30 */
- "\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
- "\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */
-
-ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
-ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
+ASUS_HANDLE(display_get,
+ /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
+ "\\_SB.PCI0.P0P1.VGA.GETD",
+ /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
+ "\\_SB.PCI0.P0P2.VGA.GETD",
+ /* A6V A6Q */
+ "\\_SB.PCI0.P0P3.VGA.GETD",
+ /* A6T, A6M */
+ "\\_SB.PCI0.P0PA.VGA.GETD",
+ /* L3C */
+ "\\_SB.PCI0.PCI1.VGAC.NMAP",
+ /* Z96F */
+ "\\_SB.PCI0.VGA.GETD",
+ /* A2D */
+ "\\ACTD",
+ /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
+ "\\ADVG",
+ /* P30 */
+ "\\DNXT",
+ /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
+ "\\INFB",
+ /* A3F A6F A3N A3L M6N W3N W6A */
+ "\\SSTE");
+
+ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
+ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
/* GPS */
/* R2H use different handle for GPS on/off */
@@ -172,19 +185,23 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */
ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
+/* Keyboard light */
+ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB");
+ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB");
+
/*
* This is the main structure, we can use it to store anything interesting
* about the hotk device
*/
struct asus_hotk {
- char *name; //laptop name
- struct acpi_device *device; //the device we are in
- acpi_handle handle; //the handle of the hotk device
- char status; //status of the hotk, for LEDs, ...
- u32 ledd_status; //status of the LED display
- u8 light_level; //light sensor level
- u8 light_switch; //light sensor switch value
- u16 event_count[128]; //count for each event TODO make this better
+ char *name; /* laptop name */
+ struct acpi_device *device; /* the device we are in */
+ acpi_handle handle; /* the handle of the hotk device */
+ char status; /* status of the hotk, for LEDs, ... */
+ u32 ledd_status; /* status of the LED display */
+ u8 light_level; /* light sensor level */
+ u8 light_switch; /* light sensor switch value */
+ u16 event_count[128]; /* count for each event TODO make this better */
struct input_dev *inputdev;
u16 *keycode_map;
};
@@ -237,28 +254,35 @@ static struct backlight_ops asusbl_ops = {
.update_status = update_bl_status,
};
-/* These functions actually update the LED's, and are called from a
+/*
+ * 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. */
+ * potentially bad time, such as a timer interrupt.
+ */
static struct workqueue_struct *led_workqueue;
-#define ASUS_LED(object, ledname) \
+#define ASUS_LED(object, ledname, max) \
static void object##_led_set(struct led_classdev *led_cdev, \
enum led_brightness value); \
+ static enum led_brightness object##_led_get( \
+ struct led_classdev *led_cdev); \
static void object##_led_update(struct work_struct *ignored); \
static int object##_led_wk; \
static DECLARE_WORK(object##_led_work, object##_led_update); \
static struct led_classdev object##_led = { \
.name = "asus::" ledname, \
.brightness_set = object##_led_set, \
+ .brightness_get = object##_led_get, \
+ .max_brightness = max \
}
-ASUS_LED(mled, "mail");
-ASUS_LED(tled, "touchpad");
-ASUS_LED(rled, "record");
-ASUS_LED(pled, "phone");
-ASUS_LED(gled, "gaming");
+ASUS_LED(mled, "mail", 1);
+ASUS_LED(tled, "touchpad", 1);
+ASUS_LED(rled, "record", 1);
+ASUS_LED(pled, "phone", 1);
+ASUS_LED(gled, "gaming", 1);
+ASUS_LED(kled, "kbd_backlight", 3);
struct key_entry {
char type;
@@ -278,16 +302,23 @@ static struct key_entry asus_keymap[] = {
{KE_KEY, 0x41, KEY_NEXTSONG},
{KE_KEY, 0x43, KEY_STOPCD},
{KE_KEY, 0x45, KEY_PLAYPAUSE},
+ {KE_KEY, 0x4c, KEY_MEDIA},
{KE_KEY, 0x50, KEY_EMAIL},
{KE_KEY, 0x51, KEY_WWW},
+ {KE_KEY, 0x55, KEY_CALC},
{KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */
{KE_KEY, 0x5D, KEY_WLAN},
+ {KE_KEY, 0x5E, KEY_WLAN},
+ {KE_KEY, 0x5F, KEY_WLAN},
+ {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
{KE_KEY, 0x82, KEY_CAMERA},
{KE_KEY, 0x8A, KEY_PROG1},
{KE_KEY, 0x95, KEY_MEDIA},
{KE_KEY, 0x99, KEY_PHONE},
+ {KE_KEY, 0xc4, KEY_KBDILLUMUP},
+ {KE_KEY, 0xc5, KEY_KBDILLUMDOWN},
{KE_END, 0},
};
@@ -301,8 +332,8 @@ static struct key_entry asus_keymap[] = {
static int write_acpi_int(acpi_handle handle, const char *method, int val,
struct acpi_buffer *output)
{
- struct acpi_object_list params; //list of input parameters (an int here)
- union acpi_object in_obj; //the only param we use
+ struct acpi_object_list params; /* list of input parameters (an int) */
+ union acpi_object in_obj; /* the only param we use */
acpi_status status;
if (!handle)
@@ -399,6 +430,11 @@ static void write_status(acpi_handle handle, int out, int mask)
{ \
int value = object##_led_wk; \
write_status(object##_set_handle, value, (mask)); \
+ } \
+ static enum led_brightness object##_led_get( \
+ struct led_classdev *led_cdev) \
+ { \
+ return led_cdev->brightness; \
}
ASUS_LED_HANDLER(mled, MLED_ON);
@@ -407,6 +443,60 @@ ASUS_LED_HANDLER(rled, RLED_ON);
ASUS_LED_HANDLER(tled, TLED_ON);
ASUS_LED_HANDLER(gled, GLED_ON);
+/*
+ * Keyboard backlight
+ */
+static int get_kled_lvl(void)
+{
+ unsigned long long kblv;
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ acpi_status rv;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = 2;
+
+ rv = acpi_evaluate_integer(kled_get_handle, NULL, &params, &kblv);
+ if (ACPI_FAILURE(rv)) {
+ pr_warning("Error reading kled level\n");
+ return 0;
+ }
+ return kblv;
+}
+
+static int set_kled_lvl(int kblv)
+{
+ if (kblv > 0)
+ kblv = (1 << 7) | (kblv & 0x7F);
+ else
+ kblv = 0;
+
+ if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) {
+ pr_warning("Keyboard LED display write failed\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void kled_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ kled_led_wk = value;
+ queue_work(led_workqueue, &kled_led_work);
+}
+
+static void kled_led_update(struct work_struct *ignored)
+{
+ set_kled_lvl(kled_led_wk);
+}
+
+static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
+{
+ return get_kled_lvl();
+}
+
static int get_lcd_state(void)
{
return read_status(LCD_ON);
@@ -498,7 +588,7 @@ static ssize_t show_infos(struct device *dev,
{
int len = 0;
unsigned long long temp;
- char buf[16]; //enough for all info
+ char buf[16]; /* enough for all info */
acpi_status rv = AE_OK;
/*
@@ -516,7 +606,17 @@ static ssize_t show_infos(struct device *dev,
*/
rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
if (!ACPI_FAILURE(rv))
- len += sprintf(page + len, "SFUN value : 0x%04x\n",
+ len += sprintf(page + len, "SFUN value : %#x\n",
+ (uint) temp);
+ /*
+ * The HWRS method return informations about the hardware.
+ * 0x80 bit is for WLAN, 0x100 for Bluetooth.
+ * The significance of others is yet to be found.
+ * If we don't find the method, we assume the device are present.
+ */
+ rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp);
+ if (!ACPI_FAILURE(rv))
+ len += sprintf(page + len, "HRWS value : %#x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
@@ -527,7 +627,7 @@ static ssize_t show_infos(struct device *dev,
*/
rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
if (!ACPI_FAILURE(rv))
- len += sprintf(page + len, "ASYM value : 0x%04x\n",
+ len += sprintf(page + len, "ASYM value : %#x\n",
(uint) temp);
if (asus_info) {
snprintf(buf, 16, "%d", asus_info->length);
@@ -648,8 +748,10 @@ static int read_display(void)
unsigned long long value = 0;
acpi_status rv = AE_OK;
- /* In most of the case, we know how to set the display, but sometime
- we can't read it */
+ /*
+ * In most of the case, we know how to set the display, but sometime
+ * we can't read it
+ */
if (display_get_handle) {
rv = acpi_evaluate_integer(display_get_handle, NULL,
NULL, &value);
@@ -1037,6 +1139,9 @@ static int asus_hotk_get_info(void)
ASUS_HANDLE_INIT(ledd_set);
+ ASUS_HANDLE_INIT(kled_set);
+ ASUS_HANDLE_INIT(kled_get);
+
/*
* The HWRS method return informations about the hardware.
* 0x80 bit is for WLAN, 0x100 for Bluetooth.
@@ -1063,8 +1168,10 @@ static int asus_hotk_get_info(void)
ASUS_HANDLE_INIT(display_set);
ASUS_HANDLE_INIT(display_get);
- /* There is a lot of models with "ALSL", but a few get
- a real light sens, so we need to check it. */
+ /*
+ * There is a lot of models with "ALSL", but a few get
+ * a real light sens, so we need to check it.
+ */
if (!ASUS_HANDLE_INIT(ls_switch))
ASUS_HANDLE_INIT(ls_level);
@@ -1168,6 +1275,10 @@ static int asus_hotk_add(struct acpi_device *device)
/* LCD Backlight is on by default */
write_status(NULL, 1, LCD_ON);
+ /* Keyboard Backlight is on by default */
+ if (kled_set_handle)
+ set_kled_lvl(1);
+
/* LED display is off by default */
hotk->ledd_status = 0xFFF;
@@ -1222,6 +1333,7 @@ static void asus_led_exit(void)
ASUS_LED_UNREGISTER(pled);
ASUS_LED_UNREGISTER(rled);
ASUS_LED_UNREGISTER(gled);
+ ASUS_LED_UNREGISTER(kled);
}
static void asus_input_exit(void)
@@ -1301,13 +1413,20 @@ static int asus_led_init(struct device *dev)
if (rv)
goto out4;
+ if (kled_set_handle && kled_get_handle)
+ rv = ASUS_LED_REGISTER(kled, dev);
+ if (rv)
+ goto out5;
+
led_workqueue = create_singlethread_workqueue("led_workqueue");
if (!led_workqueue)
- goto out5;
+ goto out6;
return 0;
-out5:
+out6:
rv = -ENOMEM;
+ ASUS_LED_UNREGISTER(kled);
+out5:
ASUS_LED_UNREGISTER(gled);
out4:
ASUS_LED_UNREGISTER(pled);
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 222ffb892f2..da3c08b3dcc 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -142,18 +142,28 @@ struct eeepc_hotk {
struct rfkill *wlan_rfkill;
struct rfkill *bluetooth_rfkill;
struct rfkill *wwan3g_rfkill;
+ struct rfkill *wimax_rfkill;
struct hotplug_slot *hotplug_slot;
- struct work_struct hotplug_work;
+ struct mutex hotplug_lock;
};
/* The actual device the driver binds to */
static struct eeepc_hotk *ehotk;
/* Platform device/driver */
+static int eeepc_hotk_thaw(struct device *device);
+static int eeepc_hotk_restore(struct device *device);
+
+static struct dev_pm_ops eeepc_pm_ops = {
+ .thaw = eeepc_hotk_thaw,
+ .restore = eeepc_hotk_restore,
+};
+
static struct platform_driver platform_driver = {
.driver = {
.name = EEEPC_HOTK_FILE,
.owner = THIS_MODULE,
+ .pm = &eeepc_pm_ops,
}
};
@@ -192,7 +202,6 @@ static struct key_entry eeepc_keymap[] = {
*/
static int eeepc_hotk_add(struct acpi_device *device);
static int eeepc_hotk_remove(struct acpi_device *device, int type);
-static int eeepc_hotk_resume(struct acpi_device *device);
static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id eeepc_device_ids[] = {
@@ -209,7 +218,6 @@ static struct acpi_driver eeepc_hotk_driver = {
.ops = {
.add = eeepc_hotk_add,
.remove = eeepc_hotk_remove,
- .resume = eeepc_hotk_resume,
.notify = eeepc_hotk_notify,
},
};
@@ -579,7 +587,6 @@ static void cmsg_quirks(void)
static int eeepc_hotk_check(void)
{
- const struct key_entry *key;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
int result;
@@ -604,31 +611,6 @@ static int eeepc_hotk_check(void)
pr_info("Get control methods supported: 0x%x\n",
ehotk->cm_supported);
}
- ehotk->inputdev = input_allocate_device();
- if (!ehotk->inputdev) {
- pr_info("Unable to allocate input device\n");
- return 0;
- }
- ehotk->inputdev->name = "Asus EeePC extra buttons";
- ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
- ehotk->inputdev->id.bustype = BUS_HOST;
- ehotk->inputdev->getkeycode = eeepc_getkeycode;
- ehotk->inputdev->setkeycode = eeepc_setkeycode;
-
- for (key = eeepc_keymap; key->type != KE_END; key++) {
- switch (key->type) {
- case KE_KEY:
- set_bit(EV_KEY, ehotk->inputdev->evbit);
- set_bit(key->keycode, ehotk->inputdev->keybit);
- break;
- }
- }
- result = input_register_device(ehotk->inputdev);
- if (result) {
- pr_info("Unable to register input device\n");
- input_free_device(ehotk->inputdev);
- return 0;
- }
} else {
pr_err("Hotkey device not present, aborting\n");
return -EINVAL;
@@ -661,40 +643,48 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
return 0;
}
-static void eeepc_hotplug_work(struct work_struct *work)
+static void eeepc_rfkill_hotplug(void)
{
struct pci_dev *dev;
- struct pci_bus *bus = pci_find_bus(0, 1);
- bool blocked;
+ struct pci_bus *bus;
+ bool blocked = eeepc_wlan_rfkill_blocked();
- if (!bus) {
- pr_warning("Unable to find PCI bus 1?\n");
- return;
- }
+ if (ehotk->wlan_rfkill)
+ rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
- blocked = eeepc_wlan_rfkill_blocked();
- if (!blocked) {
- dev = pci_get_slot(bus, 0);
- if (dev) {
- /* Device already present */
- pci_dev_put(dev);
- return;
- }
- 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");
+ mutex_lock(&ehotk->hotplug_lock);
+
+ if (ehotk->hotplug_slot) {
+ bus = pci_find_bus(0, 1);
+ if (!bus) {
+ pr_warning("Unable to find PCI bus 1?\n");
+ goto out_unlock;
}
- } else {
- dev = pci_get_slot(bus, 0);
- if (dev) {
- pci_remove_bus_device(dev);
- pci_dev_put(dev);
+
+ 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);
+ }
}
}
- rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
+out_unlock:
+ mutex_unlock(&ehotk->hotplug_lock);
}
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
@@ -702,7 +692,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
- schedule_work(&ehotk->hotplug_work);
+ eeepc_rfkill_hotplug();
}
static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
@@ -839,66 +829,38 @@ error_slot:
return ret;
}
-static int eeepc_hotk_add(struct acpi_device *device)
-{
- int result;
-
- if (!device)
- return -EINVAL;
- pr_notice(EEEPC_HOTK_NAME "\n");
- ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
- if (!ehotk)
- return -ENOMEM;
- ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
- ehotk->handle = device->handle;
- strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
- strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
- device->driver_data = ehotk;
- ehotk->device = device;
- result = eeepc_hotk_check();
- if (result)
- goto ehotk_fail;
-
- return 0;
-
- ehotk_fail:
- kfree(ehotk);
- ehotk = NULL;
-
- return result;
-}
-
-static int eeepc_hotk_remove(struct acpi_device *device, int type)
-{
- if (!device || !acpi_driver_data(device))
- return -EINVAL;
-
- kfree(ehotk);
- return 0;
-}
-
-static int eeepc_hotk_resume(struct acpi_device *device)
+static int eeepc_hotk_thaw(struct device *device)
{
if (ehotk->wlan_rfkill) {
bool wlan;
- /* Workaround - it seems that _PTS disables the wireless
- without notification or changing the value read by WLAN.
- Normally this is fine because the correct value is restored
- from the non-volatile storage on resume, but we need to do
- it ourself if case suspend is aborted, or we lose wireless.
+ /*
+ * Work around bios bug - acpi _PTS turns off the wireless led
+ * during suspend. Normally it restores it on resume, but
+ * we should kick it ourselves in case hibernation is aborted.
*/
wlan = get_acpi(CM_ASL_WLAN);
set_acpi(CM_ASL_WLAN, wlan);
+ }
- rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1);
+ return 0;
+}
- schedule_work(&ehotk->hotplug_work);
- }
+static int eeepc_hotk_restore(struct device *device)
+{
+ /* Refresh both wlan rfkill state and pci hotplug */
+ if (ehotk->wlan_rfkill)
+ eeepc_rfkill_hotplug();
if (ehotk->bluetooth_rfkill)
rfkill_set_sw_state(ehotk->bluetooth_rfkill,
get_acpi(CM_ASL_BLUETOOTH) != 1);
+ if (ehotk->wwan3g_rfkill)
+ rfkill_set_sw_state(ehotk->wwan3g_rfkill,
+ get_acpi(CM_ASL_3G) != 1);
+ if (ehotk->wimax_rfkill)
+ rfkill_set_sw_state(ehotk->wimax_rfkill,
+ get_acpi(CM_ASL_WIMAX) != 1);
return 0;
}
@@ -1019,16 +981,37 @@ static void eeepc_backlight_exit(void)
static void eeepc_rfkill_exit(void)
{
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
- if (ehotk->wlan_rfkill)
+ if (ehotk->wlan_rfkill) {
rfkill_unregister(ehotk->wlan_rfkill);
- if (ehotk->bluetooth_rfkill)
- rfkill_unregister(ehotk->bluetooth_rfkill);
- if (ehotk->wwan3g_rfkill)
- rfkill_unregister(ehotk->wwan3g_rfkill);
+ rfkill_destroy(ehotk->wlan_rfkill);
+ ehotk->wlan_rfkill = NULL;
+ }
+ /*
+ * Refresh pci hotplug in case the rfkill state was changed after
+ * eeepc_unregister_rfkill_notifier()
+ */
+ eeepc_rfkill_hotplug();
if (ehotk->hotplug_slot)
pci_hp_deregister(ehotk->hotplug_slot);
+
+ if (ehotk->bluetooth_rfkill) {
+ rfkill_unregister(ehotk->bluetooth_rfkill);
+ rfkill_destroy(ehotk->bluetooth_rfkill);
+ ehotk->bluetooth_rfkill = NULL;
+ }
+ if (ehotk->wwan3g_rfkill) {
+ rfkill_unregister(ehotk->wwan3g_rfkill);
+ rfkill_destroy(ehotk->wwan3g_rfkill);
+ ehotk->wwan3g_rfkill = NULL;
+ }
+ if (ehotk->wimax_rfkill) {
+ rfkill_unregister(ehotk->wimax_rfkill);
+ rfkill_destroy(ehotk->wimax_rfkill);
+ ehotk->wimax_rfkill = NULL;
+ }
}
static void eeepc_input_exit(void)
@@ -1050,19 +1033,6 @@ static void eeepc_hwmon_exit(void)
eeepc_hwmon_device = NULL;
}
-static void __exit eeepc_laptop_exit(void)
-{
- eeepc_backlight_exit();
- eeepc_rfkill_exit();
- eeepc_input_exit();
- eeepc_hwmon_exit();
- acpi_bus_unregister_driver(&eeepc_hotk_driver);
- sysfs_remove_group(&platform_device->dev.kobj,
- &platform_attribute_group);
- platform_device_unregister(platform_device);
- platform_driver_unregister(&platform_driver);
-}
-
static int eeepc_new_rfkill(struct rfkill **rfkill,
const char *name, struct device *dev,
enum rfkill_type type, int cm)
@@ -1094,10 +1064,7 @@ static int eeepc_rfkill_init(struct device *dev)
{
int result = 0;
- INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work);
-
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
+ mutex_init(&ehotk->hotplug_lock);
result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
"eeepc-wlan", dev,<