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,17 +639,19 @@ static int asus_backlight_notify(struct asus_laptop *asus) ...@@ -639,17 +639,19 @@ 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)
return 0;
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 15; props.max_brightness = 15;
bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, bd = backlight_device_register(ASUS_LAPTOP_FILE,
asus, &asusbl_ops, &props); &asus->platform_device->dev, asus,
&asusbl_ops, &props);
if (IS_ERR(bd)) { if (IS_ERR(bd)) {
pr_err("Could not register asus backlight device\n"); pr_err("Could not register asus backlight device\n");
asus->backlight_device = NULL; asus->backlight_device = NULL;
...@@ -657,11 +659,9 @@ static int asus_backlight_init(struct asus_laptop *asus) ...@@ -657,11 +659,9 @@ static int asus_backlight_init(struct asus_laptop *asus)
} }
asus->backlight_device = bd; asus->backlight_device = bd;
bd->props.power = FB_BLANK_UNBLANK;
bd->props.brightness = asus_read_brightness(bd); bd->props.brightness = asus_read_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK;
backlight_update_status(bd); 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)) {
err = device_create_file(&device->dev, &dev_attr_ledd);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && } else if (attr == &dev_attr_ls_switch.attr ||
!acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { attr == &dev_attr_ls_level.attr) {
err = device_create_file(&device->dev, &dev_attr_ls_switch); supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
if (err) !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
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);
......
This diff is collapsed.
...@@ -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);
status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
err = -EIO;
goto err_free_keymap;
}
err = input_register_device(hp_wmi_input_dev); err = input_register_device(hp_wmi_input_dev);
if (err)
goto err_uninstall_notifier;
return 0;
if (err) { 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); input_free_device(hp_wmi_input_dev);
return err; return err;
} }
return 0; 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,16 +643,10 @@ static int __init hp_wmi_init(void) ...@@ -704,16 +643,10 @@ 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) {
err = platform_driver_register(&hp_wmi_driver); err = platform_driver_register(&hp_wmi_driver);
...@@ -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;
}
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; 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)
{ {
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); 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>
......
This diff is collapsed.
...@@ -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,26 +72,7 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event) ...@@ -86,26 +72,7 @@ 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
* 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); pr_info("unknown event = 0x%02x\n", event);
} }
...@@ -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);
......
This diff is collapsed.
/*
* 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