Commit fbaab1dc authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of...

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (44 commits)
  eeepc-wmi: Add cpufv sysfs interface
  eeepc-wmi: add additional hotkeys
  panasonic-laptop: Simplify calls to acpi_pcc_retrieve_biosdata
  panasonic-laptop: Handle errors properly if they happen
  intel_pmic_gpio: fix off-by-one value range checking
  IBM Real-Time "SMI Free" mode driver -v7
  Add OLPC XO-1 rfkill driver
  Move hdaps driver to platform/x86
  ideapad-laptop: Fix Makefile
  intel_pmic_gpio: swap the bits and mask args for intel_scu_ipc_update_register
  ideapad: Add param: no_bt_rfkill
  ideapad: Change the driver name to ideapad-laptop
  ideapad: rewrite the sw rfkill set
  ideapad: rewrite the hw rfkill notify
  ideapad: use EC command to control camera
  ideapad: use return value of _CFG to tell if device exist or not
  ideapad: make sure we bind on the correct device
  ideapad: check VPC bit before sync rfkill hw status
  ideapad: add ACPI helpers
  dell-laptop: Add debugfs support
  ...
parents 51f00a47 7f80d734
What: state
Date: Sep 2010
KernelVersion: 2.6.37
Contact: Vernon Mauery <vernux@us.ibm.com>
Description: The state file allows a means by which to change in and
out of Premium Real-Time Mode (PRTM), as well as the
ability to query the current state.
0 => PRTM off
1 => PRTM enabled
Users: The ibm-prtm userspace daemon uses this interface.
What: version
Date: Sep 2010
KernelVersion: 2.6.37
Contact: Vernon Mauery <vernux@us.ibm.com>
Description: The version file provides a means by which to query
the RTL table version that lives in the Extended
BIOS Data Area (EBDA).
Users: The ibm-prtm userspace daemon uses this interface.
...@@ -2646,10 +2646,10 @@ F: drivers/net/greth* ...@@ -2646,10 +2646,10 @@ F: drivers/net/greth*
HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
M: Frank Seidel <frank@f-seidel.de> M: Frank Seidel <frank@f-seidel.de>
L: lm-sensors@lm-sensors.org L: platform-driver-x86@vger.kernel.org
W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/ W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/
S: Maintained S: Maintained
F: drivers/hwmon/hdaps.c F: drivers/platform/x86/hdaps.c
HWPOISON MEMORY FAILURE HANDLING HWPOISON MEMORY FAILURE HANDLING
M: Andi Kleen <andi@firstfloor.org> M: Andi Kleen <andi@firstfloor.org>
......
...@@ -89,6 +89,8 @@ extern int olpc_ec_mask_unset(uint8_t bits); ...@@ -89,6 +89,8 @@ extern int olpc_ec_mask_unset(uint8_t bits);
/* EC commands */ /* EC commands */
#define EC_FIRMWARE_REV 0x08 #define EC_FIRMWARE_REV 0x08
#define EC_WLAN_ENTER_RESET 0x35
#define EC_WLAN_LEAVE_RESET 0x25
/* SCI source values */ /* SCI source values */
......
...@@ -1088,26 +1088,6 @@ config SENSORS_ULTRA45 ...@@ -1088,26 +1088,6 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental This driver provides support for the Ultra45 workstation environmental
sensors. sensors.
config SENSORS_HDAPS
tristate "IBM Hard Drive Active Protection System (hdaps)"
depends on INPUT && X86
select INPUT_POLLDEV
default n
help
This driver provides support for the IBM Hard Drive Active Protection
System (hdaps), which provides an accelerometer and other misc. data.
ThinkPads starting with the R50, T41, and X40 are supported. The
accelerometer data is readable via sysfs.
This driver also provides an absolute input class device, allowing
the laptop to act as a pinball machine-esque joystick.
If your ThinkPad is not recognized by the driver, please update to latest
BIOS. This is especially the case for some R52 ThinkPads.
Say Y here if you have an applicable laptop and want to experience
the awesome power of hdaps.
config SENSORS_LIS3_SPI config SENSORS_LIS3_SPI
tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)"
depends on !ACPI && SPI_MASTER && INPUT depends on !ACPI && SPI_MASTER && INPUT
......
...@@ -52,7 +52,6 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o ...@@ -52,7 +52,6 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
......
...@@ -92,6 +92,7 @@ config DELL_WMI ...@@ -92,6 +92,7 @@ config DELL_WMI
tristate "Dell WMI extras" tristate "Dell WMI extras"
depends on ACPI_WMI depends on ACPI_WMI
depends on INPUT depends on INPUT
select INPUT_SPARSEKMAP
---help--- ---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops. Say Y here if you want to support WMI-based hotkeys on Dell laptops.
...@@ -140,6 +141,7 @@ config HP_WMI ...@@ -140,6 +141,7 @@ config HP_WMI
depends on ACPI_WMI depends on ACPI_WMI
depends on INPUT depends on INPUT
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
select INPUT_SPARSEKMAP
help help
Say Y here if you want to support WMI-based hotkeys on HP laptops and Say Y here if you want to support WMI-based hotkeys on HP laptops and
to read data from WMI such as docking or ambient light sensor state. to read data from WMI such as docking or ambient light sensor state.
...@@ -171,6 +173,7 @@ config PANASONIC_LAPTOP ...@@ -171,6 +173,7 @@ config PANASONIC_LAPTOP
tristate "Panasonic Laptop Extras" tristate "Panasonic Laptop Extras"
depends on INPUT && ACPI depends on INPUT && ACPI
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
select INPUT_SPARSEKMAP
---help--- ---help---
This driver adds support for access to backlight control and hotkeys This driver adds support for access to backlight control and hotkeys
on Panasonic Let's Note laptops. on Panasonic Let's Note laptops.
...@@ -219,8 +222,8 @@ config SONYPI_COMPAT ...@@ -219,8 +222,8 @@ config SONYPI_COMPAT
---help--- ---help---
Build the sonypi driver compatibility code into the sony-laptop driver. Build the sonypi driver compatibility code into the sony-laptop driver.
config IDEAPAD_ACPI config IDEAPAD_LAPTOP
tristate "Lenovo IdeaPad ACPI Laptop Extras" tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI depends on ACPI
depends on RFKILL depends on RFKILL
help help
...@@ -365,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL ...@@ -365,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL
If you are not sure, say Y here. The driver enables polling only if If you are not sure, say Y here. The driver enables polling only if
it is strictly necessary to do so. it is strictly necessary to do so.
config SENSORS_HDAPS
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
depends on INPUT && X86
select INPUT_POLLDEV
default n
help
This driver provides support for the IBM Hard Drive Active Protection
System (hdaps), which provides an accelerometer and other misc. data.
ThinkPads starting with the R50, T41, and X40 are supported. The
accelerometer data is readable via sysfs.
This driver also provides an absolute input class device, allowing
the laptop to act as a pinball machine-esque joystick.
If your ThinkPad is not recognized by the driver, please update to latest
BIOS. This is especially the case for some R52 ThinkPads.
Say Y here if you have an applicable laptop and want to experience
the awesome power of hdaps.
config INTEL_MENLOW config INTEL_MENLOW
tristate "Thermal Management driver for Intel menlow platform" tristate "Thermal Management driver for Intel menlow platform"
depends on ACPI_THERMAL depends on ACPI_THERMAL
...@@ -478,6 +501,7 @@ config TOPSTAR_LAPTOP ...@@ -478,6 +501,7 @@ config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras" tristate "Topstar Laptop Extras"
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
select INPUT_SPARSEKMAP
---help--- ---help---
This driver adds support for hotkeys found on Topstar laptops. This driver adds support for hotkeys found on Topstar laptops.
...@@ -492,6 +516,7 @@ config ACPI_TOSHIBA ...@@ -492,6 +516,7 @@ config ACPI_TOSHIBA
depends on INPUT depends on INPUT
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
select INPUT_POLLDEV select INPUT_POLLDEV
select INPUT_SPARSEKMAP
---help--- ---help---
This driver adds support for access to certain system settings This driver adds support for access to certain system settings
on "legacy free" Toshiba laptops. These laptops can be recognized by on "legacy free" Toshiba laptops. These laptops can be recognized by
...@@ -590,4 +615,28 @@ config INTEL_IPS ...@@ -590,4 +615,28 @@ config INTEL_IPS
functionality. If in doubt, say Y here; it will only load on functionality. If in doubt, say Y here; it will only load on
supported platforms. supported platforms.
config IBM_RTL
tristate "Device driver to enable PRTL support"
depends on X86 && PCI
---help---
Enable support for IBM Premium Real Time Mode (PRTM).
This module will allow you the enter and exit PRTM in the BIOS via
sysfs on platforms that support this feature. System in PRTM will
not receive CPU-generated SMIs for recoverable errors. Use of this
feature without proper support may void your hardware warranty.
If the proper BIOS support is found the driver will load and create
/sys/devices/system/ibm_rtl/. The "state" variable will indicate
whether or not the BIOS is in PRTM.
state = 0 (BIOS SMIs on)
state = 1 (BIOS SMIs off)
config XO1_RFKILL
tristate "OLPC XO-1 software RF kill switch"
depends on OLPC
depends on RFKILL
---help---
Support for enabling/disabling the WLAN interface on the OLPC XO-1
laptop.
endif # X86_PLATFORM_DEVICES endif # X86_PLATFORM_DEVICES
...@@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o ...@@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
...@@ -30,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o ...@@ -30,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
...@@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void) ...@@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void)
AMW0_find_mailled(); AMW0_find_mailled();
if (!interface) { if (!interface) {
printk(ACER_ERR "No or unsupported WMI interface, unable to " printk(ACER_INFO "No or unsupported WMI interface, unable to "
"load\n"); "load\n");
return -ENODEV; return -ENODEV;
} }
......
...@@ -236,7 +236,6 @@ struct asus_laptop { ...@@ -236,7 +236,6 @@ struct asus_laptop {
u8 light_level; /* light sensor level */ u8 light_level; /* light sensor level */
u8 light_switch; /* light sensor switch value */ u8 light_switch; /* light sensor switch value */
u16 event_count[128]; /* count for each event TODO make this better */ u16 event_count[128]; /* count for each event TODO make this better */
u16 *keycode_map;
}; };
static const struct key_entry asus_keymap[] = { static const struct key_entry asus_keymap[] = {
...@@ -278,6 +277,7 @@ static const struct key_entry asus_keymap[] = { ...@@ -278,6 +277,7 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x99, { KEY_PHONE } }, {KE_KEY, 0x99, { KEY_PHONE } },
{KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, {KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
{KE_KEY, 0xb5, { KEY_CALC } },
{KE_END, 0}, {KE_END, 0},
}; };
...@@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus) ...@@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus)
static int asus_backlight_init(struct asus_laptop *asus) static int asus_backlight_init(struct asus_laptop *asus)
{ {
struct backlight_device *bd; struct backlight_device *bd;
struct device *dev = &asus->platform_device->dev;
struct backlight_properties props; struct backlight_properties props;
if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
lcd_switch_handle) { !lcd_switch_handle)
memset(&props, 0, sizeof(struct backlight_properties)); return 0;
props.max_brightness = 15;
bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
asus, &asusbl_ops, &props);
if (IS_ERR(bd)) {
pr_err("Could not register asus backlight device\n");
asus->backlight_device = NULL;
return PTR_ERR(bd);
}
asus->backlight_device = bd; memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 15;
bd->props.power = FB_BLANK_UNBLANK; bd = backlight_device_register(ASUS_LAPTOP_FILE,
bd->props.brightness = asus_read_brightness(bd); &asus->platform_device->dev, asus,
backlight_update_status(bd); &asusbl_ops, &props);
if (IS_ERR(bd)) {
pr_err("Could not register asus backlight device\n");
asus->backlight_device = NULL;
return PTR_ERR(bd);
} }
asus->backlight_device = bd;
bd->props.brightness = asus_read_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK;
backlight_update_status(bd);
return 0; return 0;
} }
...@@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, ...@@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
*/ */
static int asus_gps_rfkill_set(void *data, bool blocked) static int asus_gps_rfkill_set(void *data, bool blocked)
{ {
acpi_handle handle = data; struct asus_laptop *asus = data;
return asus_gps_switch(handle, !blocked); return asus_gps_switch(asus, !blocked);
} }
static const struct rfkill_ops asus_gps_rfkill_ops = { static const struct rfkill_ops asus_gps_rfkill_ops = {
...@@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus) ...@@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus)
asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
RFKILL_TYPE_GPS, RFKILL_TYPE_GPS,
&asus_gps_rfkill_ops, NULL); &asus_gps_rfkill_ops, asus);
if (!asus->gps_rfkill) if (!asus->gps_rfkill)
return -EINVAL; return -EINVAL;
...@@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus) ...@@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus)
input->phys = ASUS_LAPTOP_FILE "/input0"; input->phys = ASUS_LAPTOP_FILE "/input0";
input->id.bustype = BUS_HOST; input->id.bustype = BUS_HOST;
input->dev.parent = &asus->platform_device->dev; input->dev.parent = &asus->platform_device->dev;
input_set_drvdata(input, asus);
error = sparse_keymap_setup(input, asus_keymap, NULL); error = sparse_keymap_setup(input, asus_keymap, NULL);
if (error) { if (error) {
...@@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus) ...@@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus)
sparse_keymap_free(asus->inputdev); sparse_keymap_free(asus->inputdev);
input_unregister_device(asus->inputdev); input_unregister_device(asus->inputdev);
} }
asus->inputdev = NULL;
} }
/* /*
...@@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) ...@@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);
static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);
static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
store_bluetooth); show_bluetooth, store_bluetooth);
static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
static void asus_sysfs_exit(struct asus_laptop *asus) static struct attribute *asus_attributes[] = {
{ &dev_attr_infos.attr,
struct platform_device *device = asus->platform_device; &dev_attr_wlan.attr,
&dev_attr_bluetooth.attr,
device_remove_file(&device->dev, &dev_attr_infos); &dev_attr_display.attr,
device_remove_file(&device->dev, &dev_attr_wlan); &dev_attr_ledd.attr,
device_remove_file(&device->dev, &dev_attr_bluetooth); &dev_attr_ls_level.attr,
device_remove_file(&device->dev, &dev_attr_display); &dev_attr_ls_switch.attr,
device_remove_file(&device->dev, &dev_attr_ledd); &dev_attr_gps.attr,
device_remove_file(&device->dev, &dev_attr_ls_switch); NULL
device_remove_file(&device->dev, &dev_attr_ls_level); };
device_remove_file(&device->dev, &dev_attr_gps);
}
static int asus_sysfs_init(struct asus_laptop *asus) static mode_t asus_sysfs_is_visible(struct kobject *kobj,
struct attribute *attr,
int idx)
{ {
struct platform_device *device = asus->platform_device; struct device *dev = container_of(kobj, struct device, kobj);
int err; struct platform_device *pdev = to_platform_device(dev);
struct asus_laptop *asus = platform_get_drvdata(pdev);
acpi_handle handle = asus->handle;
bool supported;
err = device_create_file(&device->dev, &dev_attr_infos); if (attr == &dev_attr_wlan.attr) {
if (err) supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
return err;
if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { } else if (attr == &dev_attr_bluetooth.attr) {
err = device_create_file(&device->dev, &dev_attr_wlan); supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { } else if (attr == &dev_attr_display.attr) {
err = device_create_file(&device->dev, &dev_attr_bluetooth); supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { } else if (attr == &dev_attr_ledd.attr) {
err = device_create_file(&device->dev, &dev_attr_display); supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { } else if (attr == &dev_attr_ls_switch.attr ||
err = device_create_file(&device->dev, &dev_attr_ledd); attr == &dev_attr_ls_level.attr) {
if (err) supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
return err; !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
}
if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
!acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
err = device_create_file(&device->dev, &dev_attr_ls_switch);
if (err)
return err;
err = device_create_file(&device->dev, &dev_attr_ls_level);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && } else if (attr == &dev_attr_gps.attr) {
!acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
!acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) { !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
err = device_create_file(&device->dev, &dev_attr_gps); !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
if (err) } else {
return err; supported = true;
} }
return err; return supported ? attr->mode : 0;
} }
static const struct attribute_group asus_attr_group = {
.is_visible = asus_sysfs_is_visible,
.attrs = asus_attributes,
};
static int asus_platform_init(struct asus_laptop *asus) static int asus_platform_init(struct asus_laptop *asus)
{ {
int err; int result;
asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
if (!asus->platform_device) if (!asus->platform_device)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(asus->platform_device, asus); platform_set_drvdata(asus->platform_device, asus);
err = platform_device_add(asus->platform_device); result = platform_device_add(asus->platform_device);
if (err) if (result)
goto fail_platform_device; goto fail_platform_device;
err = asus_sysfs_init(asus); result = sysfs_create_group(&asus->platform_device->dev.kobj,
if (err) &asus_attr_group);
if (result)
goto fail_sysfs; goto fail_sysfs;
return 0; return 0;
fail_sysfs: fail_sysfs:
asus_sysfs_exit(asus);
platform_device_del(asus->platform_device); platform_device_del(asus->platform_device);
fail_platform_device: fail_platform_device:
platform_device_put(asus->platform_device); platform_device_put(asus->platform_device);
return err; return result;
} }
static void asus_platform_exit(struct asus_laptop *asus) static void asus_platform_exit(struct asus_laptop *asus)
{ {
asus_sysfs_exit(asus); sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
platform_device_unregister(asus->platform_device); platform_device_unregister(asus->platform_device);
} }
...@@ -1428,8 +1417,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus) ...@@ -1428,8 +1417,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
return AE_OK; return AE_OK;
} }
static bool asus_device_present;
static int __devinit asus_acpi_init(struct asus_laptop *asus) static int __devinit asus_acpi_init(struct asus_laptop *asus)
{ {
int result = 0; int result = 0;
...@@ -1474,6 +1461,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) ...@@ -1474,6 +1461,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
return result; return result;
} }
static bool asus_device_present;
static int __devinit asus_acpi_add(struct acpi_device *device) static int __devinit asus_acpi_add(struct acpi_device *device)
{ {
struct asus_laptop *asus; struct asus_laptop *asus;
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/i8042.h> #include <linux/i8042.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "../../firmware/dcdbas.h" #include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d #define BRIGHTNESS_TOKEN 0x7d
...@@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = { ...@@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = {
.query = dell_rfkill_query, .query = dell_rfkill_query,
}; };
static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data)
{
int status;
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
release_buffer();
seq_printf(s, "status:\t0x%X\n", status);
seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
status & BIT(0));
seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
(status & BIT(1)) >> 1);
seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
(status & BIT(2)) >> 2);
seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
(status & BIT(3)) >> 3);
seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
(status & BIT(4)) >> 4);
seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
(status & BIT(5)) >> 5);
seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
(status & BIT(8)) >> 8);
seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
(status & BIT(9)) >> 9);
seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
(status & BIT(10)) >> 10);
seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
(status & BIT(16)) >> 16);
seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
(status & BIT(17)) >> 17);
seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
(status & BIT(18)) >> 18);
seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
(status & BIT(19)) >> 19);
seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
hwswitch_state & BIT(0));
seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
(hwswitch_state & BIT(1)) >> 1);
seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
(hwswitch_state & BIT(2)) >> 2);
seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
(hwswitch_state & BIT(7)) >> 7);
seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
(hwswitch_state & BIT(8)) >> 8);
seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
(hwswitch_state & BIT(15)) >> 15);
return 0;
}
static int dell_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, dell_debugfs_show, inode->i_private);
}
static const struct file_operations dell_debugfs_fops = {
.owner = THIS_MODULE,
.open = dell_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void dell_update_rfkill(struct work_struct *ignored) static void dell_update_rfkill(struct work_struct *ignored)
{ {
if (wifi_rfkill) if (wifi_rfkill)
...@@ -556,6 +627,11 @@ static int __init dell_init(void) ...@@ -556,6 +627,11 @@ static int __init dell_init(void)
goto fail_filter; goto fail_filter;
} }
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
&dell_debugfs_fops);
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't /* In the event of an ACPI backlight being available, don't
* register the platform controller. * register the platform controller.
...@@ -615,6 +691,7 @@ static int __init dell_init(void) ...@@ -615,6 +691,7 @@ static int __init dell_init(void)
static void __exit dell_exit(void) static void __exit dell_exit(void)
{ {
debugfs_remove_recursive(dell_laptop_dir);
i8042_remove_filter(dell_laptop_i8042_filter); i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work); cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device); backlight_device_unregister(dell_backlight_device);
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -44,78 +45,70 @@ static int acpi_video; ...@@ -44,78 +45,70 @@ static int acpi_video;
MODULE_ALIAS("wmi:"DELL_EVENT_GUID); MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
struct key_entry {
char type; /* See KE_* below */
u16 code;
u16 keycode;
};
enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
/* /*
* Certain keys are flagged as KE_IGNORE. All of these are either * Certain keys are flagged as KE_IGNORE. All of these are either
* notifications (rather than requests for change) or are also sent * notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again. * via the keyboard controller so should not be sent again.
*/ */
static struct key_entry dell_legacy_wmi_keymap[] = { static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
{KE_KEY, 0xe045, KEY_PROG1}, { KE_KEY, 0xe045, { KEY_PROG1 } },
{KE_KEY, 0xe009, KEY_EJECTCD}, { KE_KEY, 0xe009, { KEY_EJECTCD } },
/* These also contain the brightness level at offset 6 */ /* These also contain the brightness level at offset 6 */
{KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
{KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
/* Battery health status button */ /* Battery health status button */
{KE_KEY, 0xe007, KEY_BATTERY}, { KE_KEY, 0xe007, { KEY_BATTERY } },
/* This is actually for all radios. Although physically a /* This is actually for all radios. Although physically a
* switch, the notification does not provide an indication of * switch, the notification does not provide an indication of
* state and so it should be reported as a key */ * state and so it should be reported as a key */
{KE_KEY, 0xe008, KEY_WLAN}, { KE_KEY, 0xe008, { KEY_WLAN } },
/* The next device is at offset 6, the active devices are at /* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */ offset 8 and the attached devices at offset 10 */
{KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
/* BIOS error detected */ /* BIOS error detected */
{KE_IGNORE, 0xe00d, KEY_RESERVED}, { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
/* Wifi Catcher */ /* Wifi Catcher */
{KE_KEY, 0xe011, KEY_PROG2}, { KE_KEY, 0xe011, {KEY_PROG2 } },
/* Ambient light sensor toggle */ /* Ambient light sensor toggle */
{KE_IGNORE, 0xe013, KEY_RESERVED}, { KE_IGNORE, 0xe013, { KEY_RESERVED } },
{KE_IGNORE, 0xe020, KEY_MUTE}, { KE_IGNORE, 0xe020, { KEY_MUTE } },
{KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
{KE_IGNORE, 0xe030, KEY_VOLUMEUP}, { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
{KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
{KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
{KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
{KE_IGNORE, 0xe045, KEY_NUMLOCK}, { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
{KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
{KE_END, 0} { KE_END, 0 }
}; };
static bool dell_new_hk_type; static bool dell_new_hk_type;
struct dell_new_keymap_entry { struct dell_bios_keymap_entry {
u16 scancode; u16 scancode;
u16 keycode; u16 keycode;
}; };
struct dell_hotkey_table { struct dell_bios_hotkey_table {
struct dmi_header header; struct dmi_header header;
struct dell_new_keymap_entry keymap[]; struct dell_bios_keymap_entry keymap[];
}; };
static struct key_entry *dell_new_wmi_keymap; static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
static u16 bios_to_linux_keycode[256] = { static const u16 bios_to_linux_keycode[256] __initconst = {
KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
...@@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = { ...@@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = {
KEY_PROG3 KEY_PROG3
}; };
static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
static struct input_dev *dell_wmi_input_dev; static struct input_dev *dell_wmi_input_dev;
static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code)
{
struct key_entry *key;
for (key = dell_wmi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode)
{
struct key_entry *key;
for (key = dell_wmi_keymap; key->type != KE_END; key++)
if (key->type == KE_KEY && keycode == key->keycode)
return key;
return NULL;
}
static int dell_wmi_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int dell_wmi_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct key_entry *key;
unsigned int old_keycode;
key = dell_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!dell_wmi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void dell_wmi_notify(u32 value, void *context) static void dell_wmi_notify(u32 value, void *context)
{ {
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj; union acpi_object *obj;
acpi_status status; acpi_status status;
...@@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context)
obj = (union acpi_object *)response.pointer; obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER) { if (obj && obj->type == ACPI_TYPE_BUFFER) {
const struct key_entry *key;
int reported_key; int reported_key;
u16 *buffer_entry = (u16 *)obj->buffer.pointer; u16 *buffer_entry = (u16 *)obj->buffer.pointer;
if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
printk(KERN_INFO "dell-wmi: Received unknown WMI event" printk(KERN_INFO "dell-wmi: Received unknown WMI event"
" (0x%x)\n", buffer_entry[1]); " (0x%x)\n", buffer_entry[1]);
...@@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context)
else else
reported_key = (int)buffer_entry[1] & 0xffff; reported_key = (int)buffer_entry[1] & 0xffff;
key = dell_wmi_get_entry_by_scancode(reported_key); key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
reported_key);
if (!key) { if (!key) {
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
reported_key); reported_key);
...@@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context)
* come via ACPI */ * come via ACPI */
; ;
} else { } else {
input_report_key(dell_wmi_input_dev, key->keycode, 1); sparse_keymap_report_entry(dell_wmi_input_dev, key,
input_sync(dell_wmi_input_dev); 1, true);
input_report_key(dell_wmi_input_dev, key->keycode, 0);
input_sync(dell_wmi_input_dev);
} }
} }
kfree(obj); kfree(obj);
} }
static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
static void setup_new_hk_map(const struct dmi_header *dm)
{ {
int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
sizeof(struct dell_bios_keymap_entry);
struct key_entry *keymap;
int i; int i;
int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
struct dell_hotkey_table *table =
container_of(dm, struct dell_hotkey_table, header);
dell_new_wmi_keymap = kzalloc((hotkey_num+1) * keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
sizeof(struct key_entry), GFP_KERNEL); if (!keymap)
return NULL;
for (i = 0; i < hotkey_num; i++) { for (i = 0; i < hotkey_num; i++) {
dell_new_wmi_keymap[i].type = KE_KEY; const struct dell_bios_keymap_entry *bios_entry =
dell_new_wmi_keymap[i].code = table->keymap[i].scancode; &dell_bios_hotkey_table->keymap[i];
dell_new_wmi_keymap[i].keycode = keymap[i].type = KE_KEY;
(table->keymap[i].keycode > 255) ? 0 : keymap[i].code = bios_entry->scancode;
bios_to_linux_keycode[table->keymap[i].keycode]; keymap[i].keycode = bios_entry->keycode < 256 ?
bios_to_linux_keycode[bios_entry->keycode] :
KEY_RESERVED;
} }
dell_new_wmi_keymap[i].type = KE_END; keymap[hotkey_num].type = KE_END;
dell_new_wmi_keymap[i].code = 0;
dell_new_wmi_keymap[i].keycode = 0;
dell_wmi_keymap = dell_new_wmi_keymap;
return keymap;
} }
static void find_hk_type(const struct dmi_header *dm, void *dummy)
{
if ((dm->type == 0xb2) && (dm->length > 6)) {
dell_new_hk_type = true;
setup_new_hk_map(dm);
}
}
static int __init dell_wmi_input_setup(void) static int __init dell_wmi_input_setup(void)
{ {
struct key_entry *key;
int err; int err;
dell_wmi_input_dev = input_allocate_device(); dell_wmi_input_dev = input_allocate_device();
if (!dell_wmi_input_dev) if (!dell_wmi_input_dev)
return -ENOMEM; return -ENOMEM;
dell_wmi_input_dev->name = "Dell WMI hotkeys"; dell_wmi_input_dev->name = "Dell WMI hotkeys";
dell_wmi_input_dev->phys = "wmi/input0"; dell_wmi_input_dev->phys = "wmi/input0";
dell_wmi_input_dev->id.bustype = BUS_HOST; dell_wmi_input_dev->id.bustype = BUS_HOST;
dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode;
dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; if (dell_new_hk_type) {
const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
for (key = dell_wmi_keymap; key->type != KE_END; key++) { if (!keymap) {
switch (key->type) { err = -ENOMEM;
case KE_KEY: goto err_free_dev;
set_bit(EV_KEY, dell_wmi_input_dev->evbit);
set_bit(key->keycode, dell_wmi_input_dev->keybit);
break;
case KE_SW:
set_bit(EV_SW, dell_wmi_input_dev->evbit);
set_bit(key->keycode, dell_wmi_input_dev->swbit);
break;
} }
}
err = input_register_device(dell_wmi_input_dev); err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
if (err) { /*
input_free_device(dell_wmi_input_dev); * Sparse keymap library makes a copy of keymap so we
return err; * don't need the original one that was allocated.
*/
kfree(keymap);
} else {
err = sparse_keymap_setup(dell_wmi_input_dev,
dell_wmi_legacy_keymap, NULL);
} }
if (err)
goto err_free_dev;
err = input_register_device(dell_wmi_input_dev);
if (err)
goto err_free_keymap;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(dell_wmi_input_dev);
err_free_dev:
input_free_device(dell_wmi_input_dev);
return err;
}
static void dell_wmi_input_destroy(void)
{
sparse_keymap_free(dell_wmi_input_dev);
input_unregister_device(dell_wmi_input_dev);
}
static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
{
if (dm->type == 0xb2 && dm->length > 6) {
dell_new_hk_type = true;
dell_bios_hotkey_table =
container_of(dm, struct dell_bios_hotkey_table, header);
}
} }
static int __init dell_wmi_init(void) static int __init dell_wmi_init(void)
...@@ -339,18 +283,13 @@ static int __init dell_wmi_init(void) ...@@ -339,18 +283,13 @@ static int __init dell_wmi_init(void)
acpi_video = acpi_video_backlight_support(); acpi_video = acpi_video_backlight_support();
err = dell_wmi_input_setup(); err = dell_wmi_input_setup();
if (err) { if (err)
if (dell_new_hk_type)
kfree(dell_wmi_keymap);
return err; return err;
}
status = wmi_install_notify_handler(DELL_EVENT_GUID, status = wmi_install_notify_handler(DELL_EVENT_GUID,
dell_wmi_notify, NULL); dell_wmi_notify, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
input_unregister_device(dell_wmi_input_dev); dell_wmi_input_destroy();
if (dell_new_hk_type)
kfree(dell_wmi_keymap);
printk(KERN_ERR printk(KERN_ERR
"dell-wmi: Unable to register notify handler - %d\n", "dell-wmi: Unable to register notify handler - %d\n",
status); status);
...@@ -359,14 +298,11 @@ static int __init dell_wmi_init(void) ...@@ -359,14 +298,11 @@ static int __init dell_wmi_init(void)
return 0; return 0;
} }
module_init(dell_wmi_init);
static void __exit dell_wmi_exit(void) static void __exit dell_wmi_exit(void)
{ {
wmi_remove_notify_handler(DELL_EVENT_GUID); wmi_remove_notify_handler(DELL_EVENT_GUID);
input_unregister_device(dell_wmi_input_dev); dell_wmi_input_destroy();
if (dell_new_hk_type)
kfree(dell_wmi_keymap);
} }
module_init(dell_wmi_init);
module_exit(dell_wmi_exit); module_exit(dell_wmi_exit);
...@@ -165,6 +165,7 @@ struct eeepc_laptop { ...@@ -165,6 +165,7 @@ struct eeepc_laptop {
u16 event_count[128]; /* count for each event */ u16 event_count[128]; /* count for each event */
struct platform_device *platform_device; struct platform_device *platform_device;
struct acpi_device *device; /* the device we are in */
struct device *hwmon_device; struct device *hwmon_device;
struct backlight_device *backlight_device; struct backlight_device *backlight_device;
...@@ -1193,9 +1194,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) ...@@ -1193,9 +1194,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
eeepc->inputdev = input; eeepc->inputdev = input;
return 0; return 0;
err_free_keymap: err_free_keymap:
sparse_keymap_free(input); sparse_keymap_free(input);
err_free_dev: err_free_dev:
input_free_device(input); input_free_device(input);
return error; return error;
} }
...@@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) ...@@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
sparse_keymap_free(eeepc->inputdev); sparse_keymap_free(eeepc->inputdev);
input_unregister_device(eeepc->inputdev); input_unregister_device(eeepc->inputdev);
} }
eeepc->inputdev = NULL;
} }
/* /*
...@@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc) ...@@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
} }
static int eeepc_acpi_init(struct eeepc_laptop *eeepc, static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
struct acpi_device *device)
{ {
unsigned int init_flags; unsigned int init_flags;
int result; int result;
result = acpi_bus_get_status(device); result = acpi_bus_get_status(eeepc->device);
if (result) if (result)
return result; return result;
if (!device->status.present) { if (!eeepc->device->status.present) {
pr_err("Hotkey device not present, aborting\n"); pr_err("Hotkey device not present, aborting\n");
return -ENODEV; return -ENODEV;
} }
...@@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) ...@@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
device->driver_data = eeepc; device->driver_data = eeepc;
eeepc->device = device;
eeepc->hotplug_disabled = hotplug_disabled; eeepc->hotplug_disabled = hotplug_disabled;
eeepc_dmi_check(eeepc); eeepc_dmi_check(eeepc);
result = eeepc_acpi_init(eeepc, device); result = eeepc_acpi_init(eeepc);
if (result) if (result)
goto fail_platform; goto fail_platform;
eeepc_enable_camera(eeepc); eeepc_enable_camera(eeepc);
......
...@@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); ...@@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
#define EEEPC_WMI_METHODID_DEVS 0x53564544 #define EEEPC_WMI_METHODID_DEVS 0x53564544
#define EEEPC_WMI_METHODID_DSTS 0x53544344 #define EEEPC_WMI_METHODID_DSTS 0x53544344
#define EEEPC_WMI_METHODID_CFVS 0x53564643
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
...@@ -69,6 +70,11 @@ static const struct key_entry eeepc_wmi_keymap[] = { ...@@ -69,6 +70,11 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
{ KE_KEY, 0xe1, { KEY_F14 } },
{ KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
{ KE_KEY, 0xe0, { KEY_PROG1 } },
{ KE_KEY, 0x5c, { KEY_F15 } },
{ KE_END, 0}, { KE_END, 0},
}; };
...@@ -292,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context) ...@@ -292,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context)
kfree(obj); kfree(obj);
} }
static int store_cpufv(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int value;
struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
acpi_status status;
if (!count || sscanf(buf, "%i", &value) != 1)
return -EINVAL;
if (value < 0 || value > 2)
return -EINVAL;
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
if (ACPI_FAILURE(status))
return -EIO;
else
return count;
}
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
static void eeepc_wmi_sysfs_exit(struct platform_device *device)
{
device_remove_file(&device->dev, &dev_attr_cpufv);
}
static int eeepc_wmi_sysfs_init(struct platform_device *device)
{
int retval = -ENOMEM;
retval = device_create_file(&device->dev, &dev_attr_cpufv);
if (retval)
goto error_sysfs;
return 0;
error_sysfs:
eeepc_wmi_sysfs_exit(platform_device);
return retval;
}
static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
{ {
struct eeepc_wmi *eeepc; struct eeepc_wmi *eeepc;
...@@ -387,8 +436,14 @@ static int __init eeepc_wmi_init(void) ...@@ -387,8 +436,14 @@ static int __init eeepc_wmi_init(void)
goto del_dev; goto del_dev;
} }
err = eeepc_wmi_sysfs_init(platform_device);
if (err)
goto del_sysfs;
return 0; return 0;
del_sysfs:
eeepc_wmi_sysfs_exit(platform_device);
del_dev: del_dev:
platform_device_del(platform_device); platform_device_del(platform_device);
put_dev: put_dev:
...@@ -403,6 +458,7 @@ static void __exit eeepc_wmi_exit(void) ...@@ -403,6 +458,7 @@ static void __exit eeepc_wmi_exit(void)
{ {
struct eeepc_wmi *eeepc; struct eeepc_wmi *eeepc;
eeepc_wmi_sysfs_exit(platform_device);
eeepc = platform_get_drvdata(platform_device); eeepc = platform_get_drvdata(platform_device);
platform_driver_unregister(&platform_driver); platform_driver_unregister(&platform_driver);
platform_device_unregister(platform_device); platform_device_unregister(platform_device);
......
/* /*
* drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System * hdaps.c - driver for IBM's Hard Drive Active Protection System
* *
* Copyright (C) 2005 Robert Love <rml@novell.com> * Copyright (C) 2005 Robert Love <rml@novell.com>
* Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
...@@ -88,24 +89,16 @@ struct bios_return { ...@@ -88,24 +89,16 @@ struct bios_return {
u32 value; u32 value;
}; };
struct key_entry { static const struct key_entry hp_wmi_keymap[] = {
char type; /* See KE_* below */ { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } },
u16 code; { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } },
u16 keycode; { KE_KEY, 0x20e6, { KEY_PROG1 } },
}; { KE_KEY, 0x20e8, { KEY_MEDIA } },
{ KE_KEY, 0x2142, { KEY_MEDIA } },
enum { KE_KEY, KE_END }; { KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
static struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x231b, { KEY_HELP } },
{KE_KEY, 0x02, KEY_BRIGHTNESSUP}, { KE_END, 0 }
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
{KE_KEY, 0x20e6, KEY_PROG1},
{KE_KEY, 0x20e8, KEY_MEDIA},
{KE_KEY, 0x2142, KEY_MEDIA},
{KE_KEY, 0x213b, KEY_INFO},
{KE_KEY, 0x2169, KEY_DIRECTION},
{KE_KEY, 0x231b, KEY_HELP},
{KE_END, 0}
}; };
static struct input_dev *hp_wmi_input_dev; static struct input_dev *hp_wmi_input_dev;
...@@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); ...@@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code)
{
struct key_entry *key;
for (key = hp_wmi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode)
{
struct key_entry *key;
for (key = hp_wmi_keymap; key->type != KE_END; key++)
if (key->type == KE_KEY && keycode == key->keycode)
return key;
return NULL;
}
static int hp_wmi_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int hp_wmi_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct key_entry *key;
unsigned int old_keycode;
key = hp_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!hp_wmi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void hp_wmi_notify(u32 value, void *context) static void hp_wmi_notify(u32 value, void *context)
{ {
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj; union acpi_object *obj;
u32 event_id, event_data; u32 event_id, event_data;
int key_code = 0, ret; int key_code = 0, ret;
...@@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context) ...@@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context)
sizeof(key_code)); sizeof(key_code));
if (ret) if (ret)
break; break;
key = hp_wmi_get_entry_by_scancode(key_code);
if (key) { if (!sparse_keymap_report_event(hp_wmi_input_dev,
switch (key->type) { key_code, 1, true))
case KE_KEY:
input_report_key(hp_wmi_input_dev,
key->keycode, 1);
input_sync(hp_wmi_input_dev);
input_report_key(hp_wmi_input_dev,
key->keycode, 0);
input_sync(hp_wmi_input_dev);
break;
}
} else
printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
key_code); key_code);
break; break;
...@@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context) ...@@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context)
static int __init hp_wmi_input_setup(void) static int __init hp_wmi_input_setup(void)
{ {
struct key_entry *key; acpi_status status;
int err; int err;
hp_wmi_input_dev = input_allocate_device(); hp_wmi_input_dev = input_allocate_device();
...@@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void) ...@@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_input_dev->name = "HP WMI hotkeys"; hp_wmi_input_dev->name = "HP WMI hotkeys";
hp_wmi_input_dev->phys = "wmi/input0"; hp_wmi_input_dev->phys = "wmi/input0";
hp_wmi_input_dev->id.bustype = BUS_HOST; hp_wmi_input_dev->id.bustype = BUS_HOST;
hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
for (key = hp_wmi_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, hp_wmi_input_dev->evbit);
set_bit(key->keycode, hp_wmi_input_dev->keybit);
break;
}
}
set_bit(EV_SW, hp_wmi_input_dev->evbit); __set_bit(EV_SW, hp_wmi_input_dev->evbit);
set_bit(SW_DOCK, hp_wmi_input_dev->swbit); __set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
if (err)
goto err_free_dev;
/* Set initial hardware state */ /* Set initial hardware state */
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
...@@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void) ...@@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_tablet_state()); hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev); input_sync(hp_wmi_input_dev);
err = input_register_device(hp_wmi_input_dev); status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
if (err) { err = -EIO;
input_free_device(hp_wmi_input_dev); goto err_free_keymap;
return err;
} }
err = input_register_device(hp_wmi_input_dev);
if (err)
goto err_uninstall_notifier;
return 0; return 0;
err_uninstall_notifier:
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
err_free_keymap:
sparse_keymap_free(hp_wmi_input_dev);
err_free_dev:
input_free_device(hp_wmi_input_dev);
return err;
}
static void hp_wmi_input_destroy(void)
{
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
sparse_keymap_free(hp_wmi_input_dev);
input_unregister_device(hp_wmi_input_dev);
} }
static void cleanup_sysfs(struct platform_device *device) static void cleanup_sysfs(struct platform_device *device)
...@@ -704,15 +643,9 @@ static int __init hp_wmi_init(void) ...@@ -704,15 +643,9 @@ static int __init hp_wmi_init(void)
int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
if (event_capable) { if (event_capable) {
err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
hp_wmi_notify, NULL);
if (ACPI_FAILURE(err))
return -EINVAL;
err = hp_wmi_input_setup(); err = hp_wmi_input_setup();
if (err) { if (err)
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
return err; return err;
}
} }
if (bios_capable) { if (bios_capable) {
...@@ -739,20 +672,17 @@ static int __init hp_wmi_init(void) ...@@ -739,20 +672,17 @@ static int __init hp_wmi_init(void)
err_device_alloc: err_device_alloc:
platform_driver_unregister(&hp_wmi_driver); platform_driver_unregister(&hp_wmi_driver);
err_driver_reg: err_driver_reg:
if (wmi_has_guid(HPWMI_EVENT_GUID)) { if (event_capable)
input_unregister_device(hp_wmi_input_dev); hp_wmi_input_destroy();
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
}
return err; return err;
} }
static void __exit hp_wmi_exit(void) static void __exit hp_wmi_exit(void)
{ {
if (wmi_has_guid(HPWMI_EVENT_GUID)) { if (wmi_has_guid(HPWMI_EVENT_GUID))
wmi_remove_notify_handler(HPWMI_EVENT_GUID); hp_wmi_input_destroy();
input_unregister_device(hp_wmi_input_dev);
}
if (hp_wmi_platform_dev) { if (hp_wmi_platform_dev) {
platform_device_unregister(hp_wmi_platform_dev); platform_device_unregister(hp_wmi_platform_dev);
platform_driver_unregister(&hp_wmi_driver); platform_driver_unregister(&hp_wmi_driver);
......
/*
* IBM Real-Time Linux driver
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2010
*
* Author: Keith Mannthey <kmannth@us.ibm.com>
* Vernon Mauery <vernux@us.ibm.com>
*
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/sysdev.h>
#include <linux/dmi.h>
#include <linux/mutex.h>
#include <asm/bios_ebda.h>
static bool force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
static bool debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Show debug output");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
#define RTL_ADDR_TYPE_IO 1
#define RTL_ADDR_TYPE_MMIO 2
#define RTL_CMD_ENTER_PRTM 1
#define RTL_CMD_EXIT_PRTM 2
/* The RTL table as presented by the EBDA: */
struct ibm_rtl_table {
char signature[5]; /* signature should be "_RTL_" */
u8 version;
u8 rt_status;
u8 command;
u8 command_status;
u8 cmd_address_type;
u8 cmd_granularity;
u8 cmd_offset;
u16 reserve1;
u32 cmd_port_address; /* platform dependent address */
u32 cmd_port_value; /* platform dependent value */
} __attribute__((packed));
/* to locate "_RTL_" signature do a masked 5-byte integer compare */
#define RTL_SIGNATURE 0x0000005f4c54525fULL
#define RTL_MASK 0x000000ffffffffffULL
#define RTL_DEBUG(A, ...) do { \
if (debug) \
pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \
} while (0)
static DEFINE_MUTEX(rtl_lock);
static struct ibm_rtl_table __iomem *rtl_table;
static void __iomem *ebda_map;
static void __iomem *rtl_cmd_addr;
static u8 rtl_cmd_type;
static u8 rtl_cmd_width;
static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
{
if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
return ioremap(addr, len);
return ioport_map(addr, len);
}
static void rtl_port_unmap(void __iomem *addr)
{
if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
iounmap(addr);
else
ioport_unmap(addr);
}
static int ibm_rtl_write(u8 value)
{
int ret = 0, count = 0;
static u32 cmd_port_val;
RTL_DEBUG("%s(%d)\n", __FUNCTION__, value);
value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;
mutex_lock(&rtl_lock);
if (ioread8(&rtl_table->rt_status) != value) {
iowrite8(value, &rtl_table->command);
switch (rtl_cmd_width) {
case 8:
cmd_port_val = ioread8(&rtl_table->cmd_port_value);
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
iowrite8((u8)cmd_port_val, rtl_cmd_addr);
break;
case 16:
cmd_port_val = ioread16(&rtl_table->cmd_port_value);
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
iowrite16((u16)cmd_port_val, rtl_cmd_addr);
break;
case 32:
cmd_port_val = ioread32(&rtl_table->cmd_port_value);
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
iowrite32(cmd_port_val, rtl_cmd_addr);
break;
}
while (ioread8(&rtl_table->command)) {
msleep(10);
if (count++ > 500) {
pr_err("ibm-rtl: Hardware not responding to "
"mode switch request\n");
ret = -EIO;
break;
}
}
if (ioread8(&rtl_table->command_status)) {
RTL_DEBUG("command_status reports failed command\n");
ret = -EIO;
}
}
mutex_unlock(&rtl_lock);
return ret;
}
static ssize_t rtl_show_version(struct sysdev_class * dev,
struct sysdev_class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));
}
static ssize_t rtl_show_state(struct sysdev_class *dev,
struct sysdev_class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));
}
static ssize_t rtl_set_state(struct sysdev_class *dev,
struct sysdev_class_attribute *attr,
const char *buf,
size_t count)
{
ssize_t ret;
if (count < 1 || count > 2)
return -EINVAL;
switch (buf[0]) {
case '0':
ret = ibm_rtl_write(0);
break;
case '1':
ret = ibm_rtl_write(1);
break;
default:
ret = -EINVAL;
}
if (ret >= 0)
ret = count;
return ret;
}
static struct sysdev_class class_rtl = {
.name = "ibm_rtl",
};
static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL);
static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state);
static struct sysdev_class_attribute *rtl_attributes[] = {
&attr_version,
&attr_state,
NULL
};
static int rtl_setup_sysfs(void) {
int ret, i;
ret = sysdev_class_register(&class_rtl);
if (!ret) {
for (i = 0; rtl_attributes[i]; i ++)
sysdev_class_create_file(&class_rtl, rtl_attributes[i]);
}
return ret;
}
static void rtl_teardown_sysfs(void) {
int i;
for (i = 0; rtl_attributes[i]; i ++)
sysdev_class_remove_file(&class_rtl, rtl_attributes[i]);
sysdev_class_unregister(&class_rtl);
}
static int dmi_check_cb(const struct dmi_system_id *id)
{
RTL_DEBUG("found IBM server '%s'\n", id->ident);
return 0;
}
#define ibm_dmi_entry(NAME, TYPE) \
{ \
.ident = NAME, \
.matches = { \
DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \
DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \
}, \
.callback = dmi_check_cb \
}
static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
ibm_dmi_entry("BladeCenter LS21", "7971"),
ibm_dmi_entry("BladeCenter LS22", "7901"),
ibm_dmi_entry("BladeCenter HS21 XM", "7995"),
ibm_dmi_entry("BladeCenter HS22", "7870"),
ibm_dmi_entry("BladeCenter HS22V", "7871"),
ibm_dmi_entry("System x3550 M2", "7946"),
ibm_dmi_entry("System x3650 M2", "7947"),
ibm_dmi_entry("System x3550 M3", "7944"),
ibm_dmi_entry("System x3650 M3", "7945"),
{ }
};
static int __init ibm_rtl_init(void) {
unsigned long ebda_addr, ebda_size;
unsigned int ebda_kb;
int ret = -ENODEV, i;
if (force)
pr_warning("ibm-rtl: module loaded by force\n");
/* first ensure that we are running on IBM HW */
else if (!dmi_check_system(ibm_rtl_dmi_table))
return -ENODEV;
/* Get the address for the Extended BIOS Data Area */
ebda_addr = get_bios_ebda();
if (!ebda_addr) {
RTL_DEBUG("no BIOS EBDA found\n");
return -ENODEV;
}
ebda_map = ioremap(ebda_addr, 4);
if (!ebda_map)
return -ENOMEM;
/* First word in the EDBA is the Size in KB */
ebda_kb = ioread16(ebda_map);
RTL_DEBUG("EBDA is %d kB\n", ebda_kb);
if (ebda_kb == 0)
goto out;
iounmap(ebda_map);
ebda_size = ebda_kb*1024;
/* Remap the whole table */
ebda_map = ioremap(ebda_addr, ebda_size);
if (!ebda_map)
return -ENOMEM;
/* search for the _RTL_ signature at the start of the table */
for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {
struct ibm_rtl_table __iomem * tmp;
tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);
if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {
phys_addr_t addr;
unsigned int plen;
RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp);
rtl_table = tmp;
/* The address, value, width and offset are platform
* dependent and found in the ibm_rtl_table */
rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);
rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);
RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",
rtl_cmd_width, rtl_cmd_type);
addr = ioread32(&rtl_table->cmd_port_address);
RTL_DEBUG("addr = %#llx\n", addr);
plen = rtl_cmd_width/sizeof(char);
rtl_cmd_addr = rtl_port_map(addr, plen);
RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr);
if (!rtl_cmd_addr) {
ret = -ENOMEM;
break;
}
ret = rtl_setup_sysfs();
break;
}
}
out:
if (ret) {
iounmap(ebda_map);
rtl_port_unmap(rtl_cmd_addr);
}
return ret;
}
static void __exit ibm_rtl_exit(void)
{
if (rtl_table) {
RTL_DEBUG("cleaning up");
/* do not leave the machine in SMI-free mode */
ibm_rtl_write(0);
/* unmap, unlink and remove all traces */
rtl_teardown_sysfs();
iounmap(ebda_map);
rtl_port_unmap(rtl_cmd_addr);
}
}
module_init(ibm_rtl_init);
module_exit(ibm_rtl_exit);
...@@ -35,112 +35,162 @@ ...@@ -35,112 +35,162 @@
#define IDEAPAD_DEV_KILLSW 4 #define IDEAPAD_DEV_KILLSW 4
struct ideapad_private { struct ideapad_private {
acpi_handle handle;
struct rfkill *rfk[5]; struct rfkill *rfk[5];
}; } *ideapad_priv;
static struct { static struct {
char *name; char *name;
int cfgbit;
int opcode;
int type; int type;
} ideapad_rfk_data[] = { } ideapad_rfk_data[] = {
/* camera has no rfkill */ { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES },
{ "ideapad_wlan", RFKILL_TYPE_WLAN }, { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
{ "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH }, { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
{ "ideapad_3g", RFKILL_TYPE_WWAN }, { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
{ "ideapad_killsw", RFKILL_TYPE_WLAN } { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN }
}; };
static int ideapad_dev_exists(int device) static bool no_bt_rfkill;
{ module_param(no_bt_rfkill, bool, 0444);
acpi_status status; MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
union acpi_object in_param;
struct acpi_object_list input = { 1, &in_param };
struct acpi_buffer output;
union acpi_object out_obj;
output.length = sizeof(out_obj); /*
output.pointer = &out_obj; * ACPI Helpers
*/
#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
in_param.type = ACPI_TYPE_INTEGER; static int read_method_int(acpi_handle handle, const char *method, int *val)
in_param.integer.value = device + 1; {
acpi_status status;
unsigned long long result;
status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output); status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status); *val = -1;
return -ENODEV; return -1;
} } else {
if (out_obj.type != ACPI_TYPE_INTEGER) { *val = result;
printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n"); return 0;
return -ENODEV;
} }
return out_obj.integer.value;
} }
static int ideapad_dev_get_state(int device) static int method_vpcr(acpi_handle handle, int cmd, int *ret)
{ {
acpi_status status; acpi_status status;
union acpi_object in_param; unsigned long long result;
struct acpi_object_list input = { 1, &in_param }; struct acpi_object_list params;
struct acpi_buffer output; union acpi_object in_obj;
union acpi_object out_obj;
output.length = sizeof(out_obj); params.count = 1;
output.pointer = &out_obj; params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = cmd;
in_param.type = ACPI_TYPE_INTEGER; status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
in_param.integer.value = device + 1;
status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status); *ret = -1;
return -ENODEV; return -1;
} } else {
if (out_obj.type != ACPI_TYPE_INTEGER) { *ret = result;
printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n"); return 0;
return -ENODEV;
} }
return out_obj.integer.value;
} }
static int ideapad_dev_set_state(int device, int state) static int method_vpcw(acpi_handle handle, int cmd, int data)
{ {
struct acpi_object_list params;
union acpi_object in_obj[2];
acpi_status status; acpi_status status;
union acpi_object in_params[2];
struct acpi_object_list input = { 2, in_params };
in_params[0].type = ACPI_TYPE_INTEGER; params.count = 2;
in_params[0].integer.value = device + 1; params.pointer = in_obj;
in_params[1].type = ACPI_TYPE_INTEGER; in_obj[0].type = ACPI_TYPE_INTEGER;
in_params[1].integer.value = state; in_obj[0].integer.value = cmd;
in_obj[1].type = ACPI_TYPE_INTEGER;
in_obj[1].integer.value = data;
status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL); status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
if (ACPI_FAILURE(status)) { if (status != AE_OK)
printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status); return -1;
return -ENODEV;
}
return 0; return 0;
} }
static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
{
int val;
unsigned long int end_jiffies;
if (method_vpcw(handle, 1, cmd))
return -1;
for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
time_before(jiffies, end_jiffies);) {
schedule();
if (method_vpcr(handle, 1, &val))
return -1;
if (val == 0) {
if (method_vpcr(handle, 0, &val))
return -1;
*data = val;
return 0;
}
}
pr_err("timeout in read_ec_cmd\n");
return -1;
}
static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
{
int val;
unsigned long int end_jiffies;
if (method_vpcw(handle, 0, data))
return -1;
if (method_vpcw(handle, 1, cmd))
return -1;
for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
time_before(jiffies, end_jiffies);) {
schedule();
if (method_vpcr(handle, 1, &val))
return -1;
if (val == 0)
return 0;
}
pr_err("timeout in write_ec_cmd\n");
return -1;
}
/* the above is ACPI helpers */
static ssize_t show_ideapad_cam(struct device *dev, static ssize_t show_ideapad_cam(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA); struct ideapad_private *priv = dev_get_drvdata(dev);
if (state < 0) acpi_handle handle = priv->handle;
return state; unsigned long result;
return sprintf(buf, "%d\n", state); if (read_ec_data(handle, 0x1D, &result))
return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result);
} }
static ssize_t store_ideapad_cam(struct device *dev, static ssize_t store_ideapad_cam(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct ideapad_private *priv = dev_get_drvdata(dev);
acpi_handle handle = priv->handle;
int ret, state; int ret, state;
if (!count) if (!count)
return 0; return 0;
if (sscanf(buf, "%i", &state) != 1) if (sscanf(buf, "%i", &state) != 1)
return -EINVAL; return -EINVAL;
ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state); ret = write_ec_cmd(handle, 0x1E, state);
if (ret < 0) if (ret < 0)
return ret; return ret;
return count; return count;
...@@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked) ...@@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked)
if (device == IDEAPAD_DEV_KILLSW) if (device == IDEAPAD_DEV_KILLSW)
return -EINVAL; return -EINVAL;
return ideapad_dev_set_state(device, !blocked);
return write_ec_cmd(ideapad_priv->handle,
ideapad_rfk_data[device].opcode,
!blocked);
} }
static struct rfkill_ops ideapad_rfk_ops = { static struct rfkill_ops ideapad_rfk_ops = {
...@@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = { ...@@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = {
static void ideapad_sync_rfk_state(struct acpi_device *adevice) static void ideapad_sync_rfk_state(struct acpi_device *adevice)
{ {
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW); acpi_handle handle = priv->handle;
unsigned long hw_blocked;
int i; int i;
rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked); if (read_ec_data(handle, 0x23, &hw_blocked))
for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
if (priv->rfk[i])
rfkill_set_hw_state(priv->rfk[i], hw_blocked);
if (hw_blocked)
return; return;
hw_blocked = !hw_blocked;
for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
if (priv->rfk[i]) if (priv->rfk[i])
rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i)); rfkill_set_hw_state(priv->rfk[i], hw_blocked);
} }
static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
{ {
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int ret; int ret;
unsigned long sw_blocked;
if (no_bt_rfkill &&
(ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
/* Force to enable bluetooth when no_bt_rfkill=1 */
write_ec_cmd(ideapad_priv->handle,
ideapad_rfk_data[dev].opcode, 1);
return 0;
}
priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev, priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops, ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
(void *)(long)dev); (void *)(long)dev);
if (!priv->rfk[dev]) if (!priv->rfk[dev])
return -ENOMEM; return -ENOMEM;
if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
&sw_blocked)) {
rfkill_init_sw_state(priv->rfk[dev], 0);
} else {
sw_blocked = !sw_blocked;
rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
}
ret = rfkill_register(priv->rfk[dev]); ret = rfkill_register(priv->rfk[dev]);
if (ret) { if (ret) {
rfkill_destroy(priv->rfk[dev]); rfkill_destroy(priv->rfk[dev]);
...@@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); ...@@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
static int ideapad_acpi_add(struct acpi_device *adevice) static int ideapad_acpi_add(struct acpi_device *adevice)
{ {
int i; int i, cfg;
int devs_present[5]; int devs_present[5];
struct ideapad_private *priv; struct ideapad_private *priv;
if (read_method_int(adevice->handle, "_CFG", &cfg))
return -ENODEV;
for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
devs_present[i] = ideapad_dev_exists(i); if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
if (devs_present[i] < 0) devs_present[i] = 1;
return devs_present[i]; else
devs_present[i] = 0;
} }
/* The hardware switch is always present */ /* The hardware switch is always present */
...@@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice) ...@@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice)
} }
} }
priv->handle = adevice->handle;
dev_set_drvdata(&adevice->dev, priv); dev_set_drvdata(&adevice->dev, priv);
ideapad_priv = priv;
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
if (!devs_present[i]) if (!devs_present[i])
continue; continue;
...@@ -270,7 +344,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) ...@@ -270,7 +344,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
{ {
ideapad_sync_rfk_state(adevice); acpi_handle handle = adevice->handle;
unsigned long vpc1, vpc2, vpc_bit;
if (read_ec_data(handle, 0x10, &vpc1))
return;
if (read_ec_data(handle, 0x1A, &vpc2))
return;
vpc1 = (vpc2 << 8) | vpc1;
for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
if (test_bit(vpc_bit, &vpc1)) {
if (vpc_bit == 9)
ideapad_sync_rfk_state(adevice);
}
}
} }
static struct acpi_driver ideapad_acpi_driver = { static struct acpi_driver ideapad_acpi_driver = {
......
...@@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip, ...@@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip,
if (offset < 8)/* it is GPIO */ if (offset < 8)/* it is GPIO */
rc = intel_scu_ipc_update_register(GPIO0 + offset, rc = intel_scu_ipc_update_register(GPIO0 + offset,
GPIO_DRV | GPIO_DOU | GPIO_DIR, GPIO_DRV | (value ? GPIO_DOU : 0),
GPIO_DRV | (value ? GPIO_DOU : 0)); GPIO_DRV | GPIO_DOU | GPIO_DIR);
else if (offset < 16)/* it is GPOSW */ else if (offset < 16)/* it is GPOSW */
rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, GPOSW_DRV | (value ? GPOSW_DOU : 0),
GPOSW_DRV | (value ? GPOSW_DOU : 0)); GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
else if (offset > 15 && offset < 24)/* it is GPO */ else if (offset > 15 && offset < 24)/* it is GPO */
rc = intel_scu_ipc_update_register(GPO, rc = intel_scu_ipc_update_register(GPO,
1 << (offset - 16), value ? 1 << (offset - 16) : 0,
value ? 1 << (offset - 16) : 0); 1 << (offset - 16));
else { else {
printk(KERN_ERR printk(KERN_ERR
"%s: invalid PMIC GPIO pin %d!\n", __func__, offset); "%s: invalid PMIC GPIO pin %d!\n", __func__, offset);
...@@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ...@@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{ {
if (offset < 8)/* it is GPIO */ if (offset < 8)/* it is GPIO */
intel_scu_ipc_update_register(GPIO0 + offset, intel_scu_ipc_update_register(GPIO0 + offset,
GPIO_DRV | GPIO_DOU, GPIO_DRV | (value ? GPIO_DOU : 0),
GPIO_DRV | (value ? GPIO_DOU : 0)); GPIO_DRV | GPIO_DOU);
else if (offset < 16)/* it is GPOSW */ else if (offset < 16)/* it is GPOSW */
intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, GPOSW_DRV | (value ? GPOSW_DOU : 0),
GPOSW_DRV | (value ? GPOSW_DOU : 0)); GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
else if (offset > 15 && offset < 24) /* it is GPO */ else if (offset > 15 && offset < 24) /* it is GPO */
intel_scu_ipc_update_register(GPO, intel_scu_ipc_update_register(GPO,
1 << (offset - 16), value ? 1 << (offset - 16) : 0,
value ? 1 << (offset - 16) : 0); 1 << (offset - 16));
} }
static int pmic_irq_type(unsigned irq, unsigned type) static int pmic_irq_type(unsigned irq, unsigned type)
...@@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type) ...@@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type)
u32 gpio = irq - pg->irq_base; u32 gpio = irq - pg->irq_base;
unsigned long flags; unsigned long flags;
if (gpio > pg->chip.ngpio) if (gpio >= pg->chip.ngpio)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&pg->irqtypes.lock, flags); spin_lock_irqsave(&pg->irqtypes.lock, flags);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sfi.h>
#include <asm/mrst.h> #include <asm/mrst.h>
#include <asm/intel_scu_ipc.h> #include <asm/intel_scu_ipc.h>
......
...@@ -128,6 +128,7 @@ ...@@ -128,6 +128,7 @@
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#ifndef ACPI_HOTKEY_COMPONENT #ifndef ACPI_HOTKEY_COMPONENT
...@@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = { ...@@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = {
}, },
}; };
#define KEYMAP_SIZE 11 static const struct key_entry panasonic_keymap[] = {
static const unsigned int initial_keymap[KEYMAP_SIZE] = { { KE_KEY, 0, { KEY_RESERVED } },
/* 0 */ KEY_RESERVED, { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
/* 1 */ KEY_BRIGHTNESSDOWN, { KE_KEY, 2, { KEY_BRIGHTNESSUP } },
/* 2 */ KEY_BRIGHTNESSUP, { KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
/* 3 */ KEY_DISPLAYTOGGLE, { KE_KEY, 4, { KEY_MUTE } },
/* 4 */ KEY_MUTE, { KE_KEY, 5, { KEY_VOLUMEDOWN } },
/* 5 */ KEY_VOLUMEDOWN, { KE_KEY, 6, { KEY_VOLUMEUP } },
/* 6 */ KEY_VOLUMEUP, { KE_KEY, 7, { KEY_SLEEP } },
/* 7 */ KEY_SLEEP, { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
/* 8 */ KEY_PROG1, /* Change CPU boost */ { KE_KEY, 9, { KEY_BATTERY } },
/* 9 */ KEY_BATTERY, { KE_KEY, 10, { KEY_SUSPEND } },
/* 10 */ KEY_SUSPEND, { KE_END, 0 }
}; };
struct pcc_acpi { struct pcc_acpi {
acpi_handle handle; acpi_handle handle;
unsigned long num_sifr; unsigned long num_sifr;
int sticky_mode; int sticky_mode;
u32 *sinf; u32 *sinf;
struct acpi_device *device; struct acpi_device *device;
struct input_dev *input_dev; struct input_dev *input_dev;
struct backlight_device *backlight; struct backlight_device *backlight;
unsigned int keymap[KEYMAP_SIZE];
}; };
struct pcc_keyinput { struct pcc_keyinput {
...@@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device) ...@@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device)
} }
} }
static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
{ {
acpi_status status; acpi_status status;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
...@@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) ...@@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
hkey = buffer.pointer; hkey = buffer.pointer;
if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
status = AE_ERROR;
goto end; goto end;
} }
...@@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) ...@@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
for (i = 0; i < hkey->package.count; i++) { for (i = 0; i < hkey->package.count; i++) {
union acpi_object *element = &(hkey->package.elements[i]); union acpi_object *element = &(hkey->package.elements[i]);
if (likely(element->type == ACPI_TYPE_INTEGER)) { if (likely(element->type == ACPI_TYPE_INTEGER)) {
sinf[i] = element->integer.value; pcc->sinf[i] = element->integer.value;
} else } else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid HKEY.SINF data\n")); "Invalid HKEY.SINF data\n"));
} }
sinf[hkey->package.count] = -1; pcc->sinf[hkey->package.count] = -1;
end: end:
kfree(buffer.pointer); kfree(buffer.pointer);
...@@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd) ...@@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd)
{ {
struct pcc_acpi *pcc = bl_get_data(bd); struct pcc_acpi *pcc = bl_get_data(bd);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO; return -EIO;
return pcc->sinf[SINF_AC_CUR_BRIGHT]; return pcc->sinf[SINF_AC_CUR_BRIGHT];
...@@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd) ...@@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd)
int bright = bd->props.brightness; int bright = bd->props.brightness;
int rc; int rc;
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO; return -EIO;
if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
...@@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, ...@@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev); struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi); struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO; return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
...@@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, ...@@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev); struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi); struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO; return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
...@@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr, ...@@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev); struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi); struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO; return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
...@@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, ...@@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev); struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi); struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO; return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
...@@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = { ...@@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */ /* hotkey input device driver */
static int pcc_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct pcc_acpi *pcc = input_get_drvdata(dev);
if (scancode >= ARRAY_SIZE(pcc->keymap))
return -EINVAL;
*keycode = pcc->keymap[scancode];
return 0;
}
static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
if (pcc->keymap[i] == keycode)
return i+1;
}
return 0;
}
static int pcc_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct pcc_acpi *pcc = input_get_drvdata(dev);
int oldkeycode;
if (scancode >= ARRAY_SIZE(pcc->keymap))
return -EINVAL;
oldkeycode = pcc->keymap[scancode];
pcc->keymap[scancode] = keycode;
set_bit(keycode, dev->keybit);
if (!keymap_get_by_keycode(pcc, oldkeycode))
clear_bit(oldkeycode, dev->keybit);
return 0;
}
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{ {
struct input_dev *hotk_input_dev = pcc->input_dev; struct input_dev *hotk_input_dev = pcc->input_dev;
int rc; int rc;
int key_code, hkey_num;
unsigned long long result; unsigned long long result;
rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
...@@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) ...@@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
hkey_num = result & 0xf; if (!sparse_keymap_report_event(hotk_input_dev,
result & 0xf, result & 0x80, false))
if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"hotkey number out of range: %d\n", "Unknown hotkey event: %d\n", result));
hkey_num));
return;
}
key_code = pcc->keymap[hkey_num];
if (key_code != KEY_RESERVED) {
int pushed = (result & 0x80) ? TRUE : FALSE;
input_report_key(hotk_input_dev, key_code, pushed);
input_sync(hotk_input_dev);
}
return;
} }
static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
...@@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) ...@@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
static int acpi_pcc_init_input(struct pcc_acpi *pcc) static int acpi_pcc_init_input(struct pcc_acpi *pcc)
{ {
int i, rc; struct input_dev *input_dev;
int error;
pcc->input_dev = input_allocate_device(); input_dev = input_allocate_device();
if (!pcc->input_dev) { if (!input_dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't allocate input device for hotkey")); "Couldn't allocate input device for hotkey"));
return -ENOMEM; return -ENOMEM;
} }
pcc->input_dev->evbit[0] = BIT(EV_KEY); input_dev->name = ACPI_PCC_DRIVER_NAME;
input_dev->phys = ACPI_PCC_INPUT_PHYS;
pcc->input_dev->name = ACPI_PCC_DRIVER_NAME; input_dev->id.bustype = BUS_HOST;
pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS; input_dev->id.vendor = 0x0001;
pcc->input_dev->id.bustype = BUS_HOST; input_dev->id.product = 0x0001;
pcc->input_dev->id.vendor = 0x0001; input_dev->id.version = 0x0100;
pcc->input_dev->id.product = 0x0001;
pcc->input_dev->id.version = 0x0100;
pcc->input_dev->getkeycode = pcc_getkeycode;
pcc->input_dev->setkeycode = pcc_setkeycode;
/* load initial keymap */ error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap)); if (error) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to setup input device keymap\n"));
goto err_free_dev;
}
for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) error = input_register_device(input_dev);
__set_bit(pcc->keymap[i], pcc->input_dev->keybit); if (error) {
__clear_bit(KEY_RESERVED, pcc->input_dev->keybit); ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to register input device\n"));
goto err_free_keymap;
}
input_set_drvdata(pcc->input_dev, pcc); pcc->input_dev = input_dev;
return 0;
rc = input_register_device(pcc->input_dev); err_free_keymap:
if (rc < 0) sparse_keymap_free(input_dev);
input_free_device(pcc->input_dev); err_free_dev:
input_free_device(input_dev);
return error;
}
return rc; static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
{
sparse_keymap_free(pcc->input_dev);
input_unregister_device(pcc->input_dev);
/*
* No need to input_free_device() since core input API refcounts
* and free()s the device.
*/
} }
/* kernel module interface */ /* kernel module interface */
...@@ -636,12 +591,13 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) ...@@ -636,12 +591,13 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
if (result) { if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error installing keyinput handler\n")); "Error installing keyinput handler\n"));
goto out_hotkey; goto out_sinf;
} }
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { if (!acpi_pcc_retrieve_biosdata(pcc)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't retrieve BIOS data\n")); "Couldn't retrieve BIOS data\n"));
result = -EIO;
goto out_input; goto out_input;
} }
/* initialize backlight */ /* initialize backlight */
...@@ -651,7 +607,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) ...@@ -651,7 +607,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
&pcc_backlight_ops, &props); &pcc_backlight_ops, &props);
if (IS_ERR(pcc->backlight)) { if (IS_ERR(pcc->backlight)) {
result = PTR_ERR(pcc->backlight); result = PTR_ERR(pcc->backlight);
goto out_sinf; goto out_input;
} }
/* read the initial brightness setting from the hardware */ /* read the initial brightness setting from the hardware */
...@@ -669,12 +625,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) ...@@ -669,12 +625,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
out_backlight: out_backlight:
backlight_device_unregister(pcc->backlight); backlight_device_unregister(pcc->backlight);
out_input:
acpi_pcc_destroy_input(pcc);
out_sinf: out_sinf:
kfree(pcc->sinf); kfree(pcc->sinf);
out_input:
input_unregister_device(pcc->input_dev);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
out_hotkey: out_hotkey:
kfree(pcc); kfree(pcc);
...@@ -709,9 +663,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) ...@@ -709,9 +663,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
backlight_device_unregister(pcc->backlight); backlight_device_unregister(pcc->backlight);
input_unregister_device(pcc->input_dev); acpi_pcc_destroy_input(pcc);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
kfree(pcc->sinf); kfree(pcc->sinf);
kfree(pcc); kfree(pcc);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#define ACPI_TOPSTAR_CLASS "topstar" #define ACPI_TOPSTAR_CLASS "topstar"
...@@ -26,52 +27,37 @@ struct topstar_hkey { ...@@ -26,52 +27,37 @@ struct topstar_hkey {
struct input_dev *inputdev; struct input_dev *inputdev;
}; };
struct tps_key_entry { static const struct key_entry topstar_keymap[] = {
u8 code; { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
u16 keycode; { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
}; { KE_KEY, 0x83, { KEY_VOLUMEUP } },
{ KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
static struct tps_key_entry topstar_keymap[] = { { KE_KEY, 0x85, { KEY_MUTE } },
{ 0x80, KEY_BRIGHTNESSUP }, { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
{ 0x81, KEY_BRIGHTNESSDOWN }, { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
{ 0x83, KEY_VOLUMEUP }, { KE_KEY, 0x88, { KEY_WLAN } },
{ 0x84, KEY_VOLUMEDOWN }, { KE_KEY, 0x8a, { KEY_WWW } },
{ 0x85, KEY_MUTE }, { KE_KEY, 0x8b, { KEY_MAIL } },
{ 0x86, KEY_SWITCHVIDEOMODE }, { KE_KEY, 0x8c, { KEY_MEDIA } },
{ 0x87, KEY_F13 }, /* touchpad enable/disable key */
{ 0x88, KEY_WLAN },
{ 0x8a, KEY_WWW },
{ 0x8b, KEY_MAIL },
{ 0x8c, KEY_MEDIA },
{ 0x96, KEY_F14 }, /* G key? */
{ }
};
static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code)
{
struct tps_key_entry *key;
for (key = topstar_keymap; key->code; key++)
if (code == key->code)
return key;
return NULL; /* Known non hotkey events don't handled or that we don't care yet */
} { KE_IGNORE, 0x8e, },
{ KE_IGNORE, 0x8f, },
static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) { KE_IGNORE, 0x90, },
{
struct tps_key_entry *key;
for (key = topstar_keymap; key->code; key++) /*
if (code == key->keycode) * 'G key' generate two event codes, convert to only
return key; * one event/key code for now, consider replacing by
* a switch (3G switch - SW_3G?)
*/
{ KE_KEY, 0x96, { KEY_F14 } },
{ KE_KEY, 0x97, { KEY_F14 } },
return NULL; { KE_END, 0 }
} };
static void acpi_topstar_notify(struct acpi_device *device, u32 event) static void acpi_topstar_notify(struct acpi_device *device, u32 event)
{ {
struct tps_key_entry *key;
static bool dup_evnt[2]; static bool dup_evnt[2];
bool *dup; bool *dup;
struct topstar_hkey *hkey = acpi_driver_data(device); struct topstar_hkey *hkey = acpi_driver_data(device);
...@@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event) ...@@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event)
*dup = true; *dup = true;
} }
/* if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
* 'G key' generate two event codes, convert to only pr_info("unknown event = 0x%02x\n", event);
* one event/key code for now (3G switch?)
*/
if (event == 0x97)
event = 0x96;
key = tps_get_key_by_scancode(event);
if (key) {
input_report_key(hkey->inputdev, key->keycode, 1);
input_sync(hkey->inputdev);
input_report_key(hkey->inputdev, key->keycode, 0);
input_sync(hkey->inputdev);
return;
}
/* Known non hotkey events don't handled or that we don't care yet */
if (event == 0x8e || event == 0x8f || event == 0x90)
return;
pr_info("unknown event = 0x%02x\n", event);
} }
static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
...@@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) ...@@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
return 0; return 0;
} }
static int topstar_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
if (!key)
return -EINVAL;
*keycode = key->keycode;
return 0;
}
static int topstar_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct tps_key_entry *key;
int old_keycode;
key = tps_get_key_by_scancode(scancode);
if (!key)
return -EINVAL;
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!tps_get_key_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
{ {
struct tps_key_entry *key; struct input_dev *input;
int error;
hkey->inputdev = input_allocate_device(); input = input_allocate_device();
if (!hkey->inputdev) { if (!input) {
pr_err("Unable to allocate input device\n"); pr_err("Unable to allocate input device\n");
return -ENODEV; return -ENOMEM;
} }
hkey->inputdev->name = "Topstar Laptop extra buttons";
hkey->inputdev->phys = "topstar/input0"; input->name = "Topstar Laptop extra buttons";
hkey->inputdev->id.bustype = BUS_HOST; input->phys = "topstar/input0";
hkey->inputdev->getkeycode = topstar_getkeycode; input->id.bustype = BUS_HOST;
hkey->inputdev->setkeycode = topstar_setkeycode;
for (key = topstar_keymap; key->code; key++) { error = sparse_keymap_setup(input, topstar_keymap, NULL);
set_bit(EV_KEY, hkey->inputdev->evbit); if (error) {
set_bit(key->keycode, hkey->inputdev->keybit); pr_err("Unable to setup input device keymap\n");
goto err_free_dev;
} }
if (input_register_device(hkey->inputdev)) {
error = input_register_device(input);
if (error) {
pr_err("Unable to register input device\n"); pr_err("Unable to register input device\n");
input_free_device(hkey->inputdev); goto err_free_keymap;
return -ENODEV;
} }
hkey->inputdev = input;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(input);
err_free_dev:
input_free_device(input);
return error;
} }
static int acpi_topstar_add(struct acpi_device *device) static int acpi_topstar_add(struct acpi_device *device)
...@@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type) ...@@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type)
acpi_topstar_fncx_switch(device, false); acpi_topstar_fncx_switch(device, false);
sparse_keymap_free(tps_hkey->inputdev);
input_unregister_device(tps_hkey->inputdev); input_unregister_device(tps_hkey->inputdev);
kfree(tps_hkey); kfree(tps_hkey);
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = { ...@@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = {
}; };
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
struct key_entry { static const struct key_entry toshiba_acpi_keymap[] __initconst = {
char type; { KE_KEY, 0x101, { KEY_MUTE } },
u16 code; { KE_KEY, 0x102, { KEY_ZOOMOUT } },
u16 keycode; { KE_KEY, 0x103, { KEY_ZOOMIN } },
}; { KE_KEY, 0x13b, { KEY_COFFEE } },
{ KE_KEY, 0x13c, { KEY_BATTERY } },
enum {KE_KEY, KE_END}; { KE_KEY, 0x13d, { KEY_SLEEP } },
{ KE_KEY, 0x13e, { KEY_SUSPEND } },
static struct key_entry toshiba_acpi_keymap[] = { { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
{KE_KEY, 0x101, KEY_MUTE}, { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
{KE_KEY, 0x102, KEY_ZOOMOUT}, { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
{KE_KEY, 0x103, KEY_ZOOMIN}, { KE_KEY, 0x142, { KEY_WLAN } },
{KE_KEY, 0x13b, KEY_COFFEE}, { KE_KEY, 0x143, { KEY_PROG1 } },
{KE_KEY, 0x13c, KEY_BATTERY}, { KE_KEY, 0xb05, { KEY_PROG2 } },
{KE_KEY, 0x13d, KEY_SLEEP}, { KE_KEY, 0xb06, { KEY_WWW } },
{KE_KEY, 0x13e, KEY_SUSPEND}, { KE_KEY, 0xb07, { KEY_MAIL } },
{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, { KE_KEY, 0xb30, { KEY_STOP } },
{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
{KE_KEY, 0x141, KEY_BRIGHTNESSUP}, { KE_KEY, 0xb32, { KEY_NEXTSONG } },
{KE_KEY, 0x142, KEY_WLAN}, { KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
{KE_KEY, 0x143, KEY_PROG1}, { KE_KEY, 0xb5a, { KEY_MEDIA } },
{KE_KEY, 0xb05, KEY_PROG2}, { KE_END, 0 },
{KE_KEY, 0xb06, KEY_WWW},
{KE_KEY, 0xb07, KEY_MAIL},
{KE_KEY, 0xb30, KEY_STOP},
{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
{KE_KEY, 0xb32, KEY_NEXTSONG},
{KE_KEY, 0xb33, KEY_PLAYPAUSE},
{KE_KEY, 0xb5a, KEY_MEDIA},
{KE_END, 0, 0},
}; };
/* utility /* utility
...@@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = { ...@@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status, .update_status = set_lcd_status,
}; };
static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
{
struct key_entry *key;
for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
{
struct key_entry *key;
for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
if (code == key->keycode && key->type == KE_KEY)
return key;
return NULL;
}
static int toshiba_acpi_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int toshiba_acpi_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct key_entry *key;
unsigned int old_keycode;
key = toshiba_acpi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
{ {
u32 hci_result, value; u32 hci_result, value;
struct key_entry *key;
if (event != 0x80) if (event != 0x80)
return; return;
...@@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) ...@@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
if (value & 0x80) if (value & 0x80)
continue; continue;
key = toshiba_acpi_get_entry_by_scancode if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
(value); value, 1, true)) {
if (!key) {
printk(MY_INFO "Unknown key %x\n", printk(MY_INFO "Unknown key %x\n",
value); value);
continue;
} }
input_report_key(toshiba_acpi.hotkey_dev,
key->keycode, 1);
input_sync(toshiba_acpi.hotkey_dev);
input_report_key(toshiba_acpi.hotkey_dev,
key->keycode, 0);
input_sync(toshiba_acpi.hotkey_dev);
} else if (hci_result == HCI_NOT_SUPPORTED) { } else if (hci_result == HCI_NOT_SUPPORTED) {
/* This is a workaround for an unresolved issue on /* This is a workaround for an unresolved issue on
* some machines where system events sporadically * some machines where system events sporadically
...@@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) ...@@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
} while (hci_result != HCI_EMPTY); } while (hci_result != HCI_EMPTY);
} }
static int toshiba_acpi_setup_keyboard(char *device) static int __init toshiba_acpi_setup_keyboard(char *device)
{ {
acpi_status status; acpi_status status;
acpi_handle handle; int error;
int result;
const struct key_entry *key;
status = acpi_get_handle(NULL, device, &handle); status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to get notification device\n"); printk(MY_INFO "Unable to get notification device\n");
return -ENODEV; return -ENODEV;
} }
toshiba_acpi.handle = handle;
status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to enable hotkeys\n");
return -ENODEV;
}
status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
toshiba_acpi_notify, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to install hotkey notification\n");
return -ENODEV;
}
toshiba_acpi.hotkey_dev = input_allocate_device(); toshiba_acpi.hotkey_dev = input_allocate_device();
if (!toshiba_acpi.hotkey_dev) { if (!toshiba_acpi.hotkey_dev) {
printk(MY_INFO "Unable to register input device\n"); printk(MY_INFO "Unable to register input device\n");
...@@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device) ...@@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device)
toshiba_acpi.hotkey_dev->name = "Toshiba input device"; toshiba_acpi.hotkey_dev->name = "Toshiba input device";
toshiba_acpi.hotkey_dev->phys = device; toshiba_acpi.hotkey_dev->phys = device;
toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); toshiba_acpi_keymap, NULL);
set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); if (error)
goto err_free_dev;
status = acpi_install_notify_handler(toshiba_acpi.handle,
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to install hotkey notification\n");
error = -ENODEV;
goto err_free_keymap;
}
status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to enable hotkeys\n");
error = -ENODEV;
goto err_remove_notify;
} }
result = input_register_device(toshiba_acpi.hotkey_dev); error = input_register_device(toshiba_acpi.hotkey_dev);
if (result) { if (error) {
printk(MY_INFO "Unable to register input device\n"); printk(MY_INFO "Unable to register input device\n");
return result; goto err_remove_notify;
} }
return 0; return 0;
err_remove_notify:
acpi_remove_notify_handler(toshiba_acpi.handle,
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
err_free_keymap:
sparse_keymap_free(toshiba_acpi.hotkey_dev);
err_free_dev:
input_free_device(toshiba_acpi.hotkey_dev);
toshiba_acpi.hotkey_dev = NULL;
return error;
} }
static void toshiba_acpi_exit(void) static void toshiba_acpi_exit(void)
{ {
if (toshiba_acpi.hotkey_dev) if (toshiba_acpi.hotkey_dev) {
acpi_remove_notify_handler(toshiba_acpi.handle,
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
sparse_keymap_free(toshiba_acpi.hotkey_dev);
input_unregister_device(toshiba_acpi.hotkey_dev); input_unregister_device(toshiba_acpi.hotkey_dev);
}
if (toshiba_acpi.bt_rfk) { if (toshiba_acpi.bt_rfk) {
rfkill_unregister(toshiba_acpi.bt_rfk); rfkill_unregister(toshiba_acpi.bt_rfk);
...@@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void) ...@@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void)
if (toshiba_proc_dir) if (toshiba_proc_dir)
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
toshiba_acpi_notify);
if (toshiba_acpi.illumination_installed) if (toshiba_acpi.illumination_installed)
led_classdev_unregister(&toshiba_led); led_classdev_unregister(&toshiba_led);
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -44,9 +46,8 @@ MODULE_LICENSE("GPL"); ...@@ -44,9 +46,8 @@ MODULE_LICENSE("GPL");
#define ACPI_WMI_CLASS "wmi" #define ACPI_WMI_CLASS "wmi"
#define PREFIX "ACPI: WMI: "
static DEFINE_MUTEX(wmi_data_lock); static DEFINE_MUTEX(wmi_data_lock);
static LIST_HEAD(wmi_block_list);
struct guid_block { struct guid_block {
char guid[16]; char guid[16];
...@@ -67,10 +68,9 @@ struct wmi_block { ...@@ -67,10 +68,9 @@ struct wmi_block {
acpi_handle handle; acpi_handle handle;
wmi_notify_handler handler; wmi_notify_handler handler;
void *handler_data; void *handler_data;
struct device *dev; struct device dev;
}; };
static struct wmi_block wmi_blocks;
/* /*
* If the GUID data block is marked as expensive, we must enable and * If the GUID data block is marked as expensive, we must enable and
...@@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { ...@@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = {
.add = acpi_wmi_add, .add = acpi_wmi_add,
.remove = acpi_wmi_remove, .remove = acpi_wmi_remove,
.notify = acpi_wmi_notify, .notify = acpi_wmi_notify,
}, },
}; };
/* /*
...@@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = { ...@@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = {
*/ */
static int wmi_parse_hexbyte(const u8 *src) static int wmi_parse_hexbyte(const u8 *src)
{ {
unsigned int x; /* For correct wrapping */
int h; int h;
int value;
/* high part */ /* high part */
x = src[0]; h = value = hex_to_bin(src[0]);
if (x - '0' <= '9' - '0') { if (value < 0)
h = x - '0';
} else if (x - 'a' <= 'f' - 'a') {
h = x - 'a' + 10;
} else if (x - 'A' <= 'F' - 'A') {
h = x - 'A' + 10;
} else {
return -1; return -1;
}
h <<= 4;
/* low part */ /* low part */
x = src[1]; value = hex_to_bin(src[1]);
if (x - '0' <= '9' - '0') if (value >= 0)
return h | (x - '0'); return (h << 4) | value;
if (x - 'a' <= 'f' - 'a')
return h | (x - 'a' + 10);
if (x - 'A' <= 'F' - 'A')
return h | (x - 'A' + 10);
return -1; return -1;
} }
...@@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out) ...@@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out)
for (i = 10; i <= 15; i++) for (i = 10; i <= 15; i++)
out += sprintf(out, "%02X", in[i] & 0xFF); out += sprintf(out, "%02X", in[i] & 0xFF);
out = '\0'; *out = '\0';
return 0; return 0;
} }
...@@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) ...@@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
wmi_parse_guid(guid_string, tmp); wmi_parse_guid(guid_string, tmp);
wmi_swap_bytes(tmp, guid_input); wmi_swap_bytes(tmp, guid_input);
list_for_each(p, &wmi_blocks.list) { list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list); wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock; block = &wblock->gblock;
...@@ -487,30 +475,29 @@ const struct acpi_buffer *in) ...@@ -487,30 +475,29 @@ const struct acpi_buffer *in)
} }
EXPORT_SYMBOL_GPL(wmi_set_block); EXPORT_SYMBOL_GPL(wmi_set_block);
static void wmi_dump_wdg(struct guid_block *g) static void wmi_dump_wdg(const struct guid_block *g)
{ {
char guid_string[37]; char guid_string[37];
wmi_gtoa(g->guid, guid_string); wmi_gtoa(g->guid, guid_string);
printk(KERN_INFO PREFIX "%s:\n", guid_string);
printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", pr_info("%s:\n", guid_string);
g->object_id[0], g->object_id[1]); pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); pr_info("\tnotify_id: %02X\n", g->notify_id);
printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); pr_info("\treserved: %02X\n", g->reserved);
printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); pr_info("\tinstance_count: %d\n", g->instance_count);
printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); pr_info("\tflags: %#x ", g->flags);
if (g->flags) { if (g->flags) {
printk(" ");
if (g->flags & ACPI_WMI_EXPENSIVE) if (g->flags & ACPI_WMI_EXPENSIVE)
printk("ACPI_WMI_EXPENSIVE "); pr_cont("ACPI_WMI_EXPENSIVE ");
if (g->flags & ACPI_WMI_METHOD) if (g->flags & ACPI_WMI_METHOD)
printk("ACPI_WMI_METHOD "); pr_cont("ACPI_WMI_METHOD ");
if (g->flags & ACPI_WMI_STRING) if (g->flags & ACPI_WMI_STRING)
printk("ACPI_WMI_STRING "); pr_cont("ACPI_WMI_STRING ");
if (g->flags & ACPI_WMI_EVENT) if (g->flags & ACPI_WMI_EVENT)
printk("ACPI_WMI_EVENT "); pr_cont("ACPI_WMI_EVENT ");
} }
printk("\n"); pr_cont("\n");
} }
...@@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context) ...@@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context)
status = wmi_get_event_data(value, &response); status = wmi_get_event_data(value, &response);
if (status != AE_OK) { if (status != AE_OK) {
printk(KERN_INFO "wmi: bad event status 0x%x\n", status); pr_info("bad event status 0x%x\n", status);
return; return;
} }
...@@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context) ...@@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context)
if (!obj) if (!obj)
return; return;
printk(KERN_INFO PREFIX "DEBUG Event "); pr_info("DEBUG Event ");
switch(obj->type) { switch(obj->type) {
case ACPI_TYPE_BUFFER: case ACPI_TYPE_BUFFER:
printk("BUFFER_TYPE - length %d\n", obj->buffer.length); pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
break; break;
case ACPI_TYPE_STRING: case ACPI_TYPE_STRING:
printk("STRING_TYPE - %s\n", obj->string.pointer); pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
break; break;
case ACPI_TYPE_INTEGER: case ACPI_TYPE_INTEGER:
printk("INTEGER_TYPE - %llu\n", obj->integer.value); pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
break; break;
case ACPI_TYPE_PACKAGE: case ACPI_TYPE_PACKAGE:
printk("PACKAGE_TYPE - %d elements\n", obj->package.count); pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
break; break;
default: default:
printk("object type 0x%X\n", obj->type); pr_cont("object type 0x%X\n", obj->type);
} }
kfree(obj); kfree(obj);
} }
...@@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) ...@@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
params[0].type = ACPI_TYPE_INTEGER; params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event; params[0].integer.value = event;
list_for_each(p, &wmi_blocks.list) { list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list); wblock = list_entry(p, struct wmi_block, list);
gblock = &wblock->gblock; gblock = &wblock->gblock;
...@@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); ...@@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
/* /*
* sysfs interface * sysfs interface
*/ */
static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
char guid_string[37]; char guid_string[37];
...@@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, ...@@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "wmi:%s\n", guid_string); return sprintf(buf, "wmi:%s\n", guid_string);
} }
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
static struct device_attribute wmi_dev_attrs[] = {
__ATTR_RO(modalias),
__ATTR_NULL
};
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{ {
...@@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
static void wmi_dev_free(struct device *dev) static void wmi_dev_free(struct device *dev)
{ {
kfree(dev); struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
kfree(wmi_block);
} }
static struct class wmi_class = { static struct class wmi_class = {
.name = "wmi", .name = "wmi",
.dev_release = wmi_dev_free, .dev_release = wmi_dev_free,
.dev_uevent = wmi_dev_uevent, .dev_uevent = wmi_dev_uevent,
.dev_attrs = wmi_dev_attrs,
}; };
static int wmi_create_devs(void) static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
acpi_handle handle)
{ {
int result;
char guid_string[37];
struct guid_block *gblock;
struct wmi_block *wblock; struct wmi_block *wblock;
struct list_head *p; int error;
struct device *guid_dev; char guid_string[37];
/* Create devices for all the GUIDs */
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!guid_dev)
return -ENOMEM;
wblock->dev = guid_dev;
guid_dev->class = &wmi_class;
dev_set_drvdata(guid_dev, wblock);
gblock = &wblock->gblock;
wmi_gtoa(gblock->guid, guid_string);
dev_set_name(guid_dev, guid_string);
result = device_register(guid_dev);
if (result)
return result;
result = device_create_file(guid_dev, &dev_attr_modalias); wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (result) if (!wblock) {
return result; error = -ENOMEM;
goto err_out;
} }
return 0; wblock->handle = handle;
} wblock->gblock = *gblock;
static void wmi_remove_devs(void) wblock->dev.class = &wmi_class;
{
struct guid_block *gblock;
struct wmi_block *wblock;
struct list_head *p;
struct device *guid_dev;
/* Delete devices for all the GUIDs */ wmi_gtoa(gblock->guid, guid_string);
list_for_each(p, &wmi_blocks.list) { dev_set_name(&wblock->dev, guid_string);
wblock = list_entry(p, struct wmi_block, list);
guid_dev = wblock->dev; dev_set_drvdata(&wblock->dev, wblock);
gblock = &wblock->gblock;
device_remove_file(guid_dev, &dev_attr_modalias); error = device_register(&wblock->dev);
if (error)
goto err_free;
device_unregister(guid_dev); list_add_tail(&wblock->list, &wmi_block_list);
} return wblock;
}
static void wmi_class_exit(void) err_free:
{ kfree(wblock);
wmi_remove_devs(); err_out:
class_unregister(&wmi_class); return ERR_PTR(error);
} }
static int wmi_class_init(void) static void wmi_free_devices(void)
{ {
int ret; struct wmi_block *wblock, *next;
ret = class_register(&wmi_class);
if (ret)
return ret;
ret = wmi_create_devs(); /* Delete devices for all the GUIDs */
if (ret) list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
wmi_class_exit(); device_unregister(&wblock->dev);
return ret;
} }
static bool guid_already_parsed(const char *guid_string) static bool guid_already_parsed(const char *guid_string)
{ {
struct guid_block *gblock;
struct wmi_block *wblock; struct wmi_block *wblock;
struct list_head *p;
list_for_each(p, &wmi_blocks.list) { list_for_each_entry(wblock, &wmi_block_list, list)
wblock = list_entry(p, struct wmi_block, list); if (strncmp(wblock->gblock.guid, guid_string, 16) == 0)
gblock = &wblock->gblock;
if (strncmp(gblock->guid, guid_string, 16) == 0)
return true; return true;
}
return false; return false;
} }
...@@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle) ...@@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle)
{ {
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj; union acpi_object *obj;
struct guid_block *gblock; const struct guid_block *gblock;
struct wmi_block *wblock; struct wmi_block *wblock;
char guid_string[37]; char guid_string[37];
acpi_status status; acpi_status status;
int retval;
u32 i, total; u32 i, total;
status = acpi_evaluate_object(handle, "_WDG", NULL, &out); status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return status; return -ENXIO;
obj = (union acpi_object *) out.pointer; obj = (union acpi_object *) out.pointer;
if (!obj)
return -ENXIO;
if (obj->type != ACPI_TYPE_BUFFER) if (obj->type != ACPI_TYPE_BUFFER) {
return AE_ERROR; retval = -ENXIO;
total = obj->buffer.length / sizeof(struct guid_block);
gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
if (!gblock) {
status = AE_NO_MEMORY;
goto out_free_pointer; goto out_free_pointer;
} }
gblock = (const struct guid_block *)obj->buffer.pointer;
total = obj->buffer.length / sizeof(struct guid_block);
for (i = 0; i < total; i++) { for (i = 0; i < total; i++) {
/* /*
Some WMI devices, like those for nVidia hooks, have a Some WMI devices, like those for nVidia hooks, have a
...@@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle) ...@@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle)
*/ */
if (guid_already_parsed(gblock[i].guid) == true) { if (guid_already_parsed(gblock[i].guid) == true) {
wmi_gtoa(gblock[i].guid, guid_string); wmi_gtoa(gblock[i].guid, guid_string);
printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", pr_info("Skipping duplicate GUID %s\n", guid_string);
guid_string);
continue; continue;
} }
if (debug_dump_wdg) if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]); wmi_dump_wdg(&gblock[i]);
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); wblock = wmi_create_device(&gblock[i], handle);
if (!wblock) { if (IS_ERR(wblock)) {
status = AE_NO_MEMORY; retval = PTR_ERR(wblock);
goto out_free_gblock; wmi_free_devices();
break;
} }
wblock->gblock = gblock[i];
wblock->handle = handle;
if (debug_event) { if (debug_event) {
wblock->handler = wmi_notify_debug; wblock->handler = wmi_notify_debug;
status = wmi_method_enable(wblock, 1); wmi_method_enable(wblock, 1);
} }
list_add_tail(&wblock->list, &wmi_blocks.list);
} }
out_free_gblock: retval = 0;
kfree(gblock);
out_free_pointer: out_free_pointer:
kfree(out.pointer); kfree(out.pointer);
return status; return retval;
} }
/* /*
...@@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) ...@@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
struct list_head *p; struct list_head *p;
char guid_string[37]; char guid_string[37];
list_for_each(p, &wmi_blocks.list) { list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list); wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock; block = &wblock->gblock;
...@@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) ...@@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
wblock->handler(event, wblock->handler_data); wblock->handler(event, wblock->handler_data);
if (debug_event) { if (debug_event) {
wmi_gtoa(wblock->gblock.guid, guid_string); wmi_gtoa(wblock->gblock.guid, guid_string);
printk(KERN_INFO PREFIX "DEBUG Event GUID:" pr_info("DEBUG Event GUID: %s\n", guid_string);
" %s\n", guid_string);
} }
acpi_bus_generate_netlink_event( acpi_bus_generate_netlink_event(
...@@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) ...@@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
{ {
acpi_remove_address_space_handler(device->handle, acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
wmi_free_devices();
return 0; return 0;
} }
...@@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) ...@@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
static int acpi_wmi_add(struct acpi_device *device) static int acpi_wmi_add(struct acpi_device *device)
{ {
acpi_status status; acpi_status status;
int result = 0; int error;
status = acpi_install_address_space_handler(device->handle, status = acpi_install_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC, ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler, &acpi_wmi_ec_space_handler,
NULL, NULL); NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
status = parse_wdg(device->handle);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "Error installing EC region handler\n"); pr_err("Error installing EC region handler\n");
return -ENODEV; return -ENODEV;
} }
return result; error = parse_wdg(device->handle);
if (error) {
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler);
pr_err("Failed to parse WDG method\n");
return error;
}
return 0;
} }
static int __init acpi_wmi_init(void) static int __init acpi_wmi_init(void)
{ {
int result; int error;
INIT_LIST_HEAD(&wmi_blocks.list);
if (acpi_disabled) if (acpi_disabled)
return -ENODEV; return -ENODEV;
result = acpi_bus_register_driver(&acpi_wmi_driver); error = class_register(&wmi_class);
if (error)
return error;
if (result < 0) { error = acpi_bus_register_driver(&acpi_wmi_driver);
printk(KERN_INFO PREFIX "Error loading mapper\n"); if (error) {
return -ENODEV; pr_err("Error loading mapper\n");
class_unregister(&wmi_class);
return error;
} }
result = wmi_class_init(); pr_info("Mapper loaded\n");
if (result) { return 0;
acpi_bus_unregister_driver(&acpi_wmi_driver);
return result;
}
printk(KERN_INFO PREFIX "Mapper loaded\n");
return result;
} }
static void __exit acpi_wmi_exit(void) static void __exit acpi_wmi_exit(void)
{ {
struct list_head *p, *tmp;
struct wmi_block *wblock;
wmi_class_exit();
acpi_bus_unregister_driver(&acpi_wmi_driver); acpi_bus_unregister_driver(&acpi_wmi_driver);
class_unregister(&wmi_class);
list_for_each_safe(p, tmp, &wmi_blocks.list) { pr_info("Mapper unloaded\n");
wblock = list_entry(p, struct wmi_block, list);
list_del(p);
kfree(wblock);
}
printk(KERN_INFO PREFIX "Mapper unloaded\n");
} }
subsys_initcall(acpi_wmi_init); subsys_initcall(acpi_wmi_init);
......
/*
* Support for rfkill through the OLPC XO-1 laptop embedded controller
*
* Copyright (C) 2010 One Laptop per Child
*
* 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.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <asm/olpc.h>
static int rfkill_set_block(void *data, bool blocked)
{
unsigned char cmd;
if (blocked)
cmd = EC_WLAN_ENTER_RESET;
else
cmd = EC_WLAN_LEAVE_RESET;
return olpc_ec_cmd(cmd, NULL, 0, NULL, 0);
}
static const struct rfkill_ops rfkill_ops = {
.set_block = rfkill_set_block,
};
static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
{
struct rfkill *rfk;
int r;
rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN,
&rfkill_ops, NULL);
if (!rfk)
return -ENOMEM;
r = rfkill_register(rfk);
if (r) {
rfkill_destroy(rfk);
return r;
}
platform_set_drvdata(pdev, rfk);
return 0;
}
static int __devexit xo1_rfkill_remove(struct platform_device *pdev)
{
struct rfkill *rfk = platform_get_drvdata(pdev);
rfkill_unregister(rfk);
rfkill_destroy(rfk);
return 0;
}
static struct platform_driver xo1_rfkill_driver = {
.driver = {
.name = "xo1-rfkill",
.owner = THIS_MODULE,
},
.probe = xo1_rfkill_probe,
.remove = __devexit_p(xo1_rfkill_remove),
};
static int __init xo1_rfkill_init(void)
{
return platform_driver_register(&xo1_rfkill_driver);
}
static void __exit xo1_rfkill_exit(void)
{
platform_driver_unregister(&xo1_rfkill_driver);
}
MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:xo1-rfkill");
module_init(xo1_rfkill_init);
module_exit(xo1_rfkill_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment