提交 27b79027 编写于 作者: L Linus Torvalds

Merge tag 'platform-drivers-x86-v4.8-1' of...

Merge tag 'platform-drivers-x86-v4.8-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86

Pull x8 platform driver updates from Darren Hart:
 "Several new quirks and tweaks for new platforms to existing laptop
  drivers.  A new ACPI virtual power button driver, similar to the
  intel-hid driver.  A rework of the dell keymap, using a single sparse
  keymap for all machines.  A few fixes and cleanups.

  Summary:

  intel-vbtn:
   - new driver for Intel Virtual Button

  intel_pmc_core:
   - Convert to DEFINE_DEBUGFS_ATTRIBUTE

  fujitsu-laptop:
   - Rework brightness of eco led

  asus-wmi:
   - Add quirk_no_rfkill_wapf4 for the Asus X456UA
   - Add quirk_no_rfkill_wapf4 for the Asus X456UF
   - Add quirk_no_rfkill for the Asus Z550MA
   - Add quirk_no_rfkill for the Asus U303LB
   - Add quirk_no_rfkill for the Asus N552VW
   - Create quirk for airplane_mode LED
   - Add ambient light sensor toggle key

  asus-wireless:
   - Toggle airplane mode LED

  intel_telemetry:
   - Remove Monitor MWAIT feature dependency

  intel-hid:
   - Remove duplicated acpi_remove_notify_handler

  fujitsu-laptop:
   - Add support for eco LED
   - Support touchpad toggle hotkey on Skylake-based models
   - Remove unused macros
   - Use module name in debug messages

  hp-wmi:
   - Fix wifi cannot be hard-unblocked

  toshiba_acpi:
   - Bump driver version and update copyright year
   - Remove the position sysfs entry
   - Add IIO interface for accelerometer axis data

  dell-wmi:
   - Add a WMI event code for display on/off
   - Generate one sparse keymap for all machines
   - Add information about other WMI event codes
   - Sort WMI event codes and update comments
   - Ignore WMI event code 0xe045"

* tag 'platform-drivers-x86-v4.8-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (26 commits)
  intel-vbtn: new driver for Intel Virtual Button
  intel_pmc_core: Convert to DEFINE_DEBUGFS_ATTRIBUTE
  fujitsu-laptop: Rework brightness of eco led
  asus-wmi: Add quirk_no_rfkill_wapf4 for the Asus X456UA
  asus-wmi: Add quirk_no_rfkill_wapf4 for the Asus X456UF
  asus-wmi: Add quirk_no_rfkill for the Asus Z550MA
  asus-wmi: Add quirk_no_rfkill for the Asus U303LB
  asus-wmi: Add quirk_no_rfkill for the Asus N552VW
  asus-wmi: Create quirk for airplane_mode LED
  asus-wireless: Toggle airplane mode LED
  intel_telemetry: Remove Monitor MWAIT feature dependency
  intel-hid: Remove duplicated acpi_remove_notify_handler
  asus-wmi: Add ambient light sensor toggle key
  fujitsu-laptop: Add support for eco LED
  fujitsu-laptop: Support touchpad toggle hotkey on Skylake-based models
  fujitsu-laptop: Remove unused macros
  fujitsu-laptop: Use module name in debug messages
  hp-wmi: Fix wifi cannot be hard-unblocked
  toshiba_acpi: Bump driver version and update copyright year
  toshiba_acpi: Remove the position sysfs entry
  ...
...@@ -5921,6 +5921,12 @@ L: platform-driver-x86@vger.kernel.org ...@@ -5921,6 +5921,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained S: Maintained
F: drivers/platform/x86/intel-hid.c F: drivers/platform/x86/intel-hid.c
INTEL VIRTUAL BUTTON DRIVER
M: AceLan Kao <acelan.kao@canonical.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/intel-vbtn.c
INTEL IDLE DRIVER INTEL IDLE DRIVER
M: Len Brown <lenb@kernel.org> M: Len Brown <lenb@kernel.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
......
...@@ -603,6 +603,8 @@ config ASUS_WIRELESS ...@@ -603,6 +603,8 @@ config ASUS_WIRELESS
tristate "Asus Wireless Radio Control Driver" tristate "Asus Wireless Radio Control Driver"
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
select NEW_LEDS
select LEDS_CLASS
---help--- ---help---
The Asus Wireless Radio Control handles the airplane mode hotkey The Asus Wireless Radio Control handles the airplane mode hotkey
present on some Asus laptops. present on some Asus laptops.
...@@ -668,6 +670,7 @@ config ACPI_TOSHIBA ...@@ -668,6 +670,7 @@ config ACPI_TOSHIBA
depends on SERIO_I8042 || SERIO_I8042 = n depends on SERIO_I8042 || SERIO_I8042 = n
depends on ACPI_VIDEO || ACPI_VIDEO = n depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
depends on IIO
select INPUT_POLLDEV select INPUT_POLLDEV
select INPUT_SPARSEKMAP select INPUT_SPARSEKMAP
---help--- ---help---
...@@ -770,6 +773,18 @@ config INTEL_HID_EVENT ...@@ -770,6 +773,18 @@ config INTEL_HID_EVENT
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called intel_hid. be called intel_hid.
config INTEL_VBTN
tristate "INTEL VIRTUAL BUTTON"
depends on ACPI
depends on INPUT
select INPUT_SPARSEKMAP
help
This driver provides support for the Intel Virtual Button interface.
Some laptops require this driver for power button support.
To compile this driver as a module, choose M here: the module will
be called intel_vbtn.
config INTEL_SCU_IPC config INTEL_SCU_IPC
bool "Intel SCU IPC Support" bool "Intel SCU IPC Support"
depends on X86_INTEL_MID depends on X86_INTEL_MID
......
...@@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o ...@@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
......
...@@ -78,6 +78,15 @@ static struct quirk_entry quirk_asus_x200ca = { ...@@ -78,6 +78,15 @@ static struct quirk_entry quirk_asus_x200ca = {
.wapf = 2, .wapf = 2,
}; };
static struct quirk_entry quirk_no_rfkill = {
.no_rfkill = true,
};
static struct quirk_entry quirk_no_rfkill_wapf4 = {
.wapf = 4,
.no_rfkill = true,
};
static int dmi_matched(const struct dmi_system_id *dmi) static int dmi_matched(const struct dmi_system_id *dmi)
{ {
quirks = dmi->driver_data; quirks = dmi->driver_data;
...@@ -133,7 +142,7 @@ static const struct dmi_system_id asus_quirks[] = { ...@@ -133,7 +142,7 @@ static const struct dmi_system_id asus_quirks[] = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"), DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"),
}, },
.driver_data = &quirk_asus_wapf4, .driver_data = &quirk_no_rfkill_wapf4,
}, },
{ {
.callback = dmi_matched, .callback = dmi_matched,
...@@ -142,7 +151,7 @@ static const struct dmi_system_id asus_quirks[] = { ...@@ -142,7 +151,7 @@ static const struct dmi_system_id asus_quirks[] = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"), DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"),
}, },
.driver_data = &quirk_asus_wapf4, .driver_data = &quirk_no_rfkill_wapf4,
}, },
{ {
.callback = dmi_matched, .callback = dmi_matched,
...@@ -306,6 +315,42 @@ static const struct dmi_system_id asus_quirks[] = { ...@@ -306,6 +315,42 @@ static const struct dmi_system_id asus_quirks[] = {
}, },
.driver_data = &quirk_asus_x200ca, .driver_data = &quirk_asus_x200ca,
}, },
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X555UB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X555UB"),
},
.driver_data = &quirk_no_rfkill,
},
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. N552VW",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "N552VW"),
},
.driver_data = &quirk_no_rfkill,
},
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. U303LB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "U303LB"),
},
.driver_data = &quirk_no_rfkill,
},
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. Z550MA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "Z550MA"),
},
.driver_data = &quirk_no_rfkill,
},
{}, {},
}; };
...@@ -356,6 +401,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { ...@@ -356,6 +401,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */ { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_IGNORE, 0x6E, }, /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } }, { KE_KEY, 0x82, { KEY_CAMERA } },
......
...@@ -15,11 +15,78 @@ ...@@ -15,11 +15,78 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/pci_ids.h> #include <linux/pci_ids.h>
#include <linux/leds.h>
#define ASUS_WIRELESS_LED_STATUS 0x2
#define ASUS_WIRELESS_LED_OFF 0x4
#define ASUS_WIRELESS_LED_ON 0x5
struct asus_wireless_data { struct asus_wireless_data {
struct input_dev *idev; struct input_dev *idev;
struct acpi_device *adev;
struct workqueue_struct *wq;
struct work_struct led_work;
struct led_classdev led;
int led_state;
}; };
static u64 asus_wireless_method(acpi_handle handle, const char *method,
int param)
{
struct acpi_object_list p;
union acpi_object obj;
acpi_status s;
u64 ret;
acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
method, param);
obj.type = ACPI_TYPE_INTEGER;
obj.integer.value = param;
p.count = 1;
p.pointer = &obj;
s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret);
if (ACPI_FAILURE(s))
acpi_handle_err(handle,
"Failed to eval method %s, param %#x (%d)\n",
method, param, s);
acpi_handle_debug(handle, "%s returned %#x\n", method, (uint) ret);
return ret;
}
static enum led_brightness led_state_get(struct led_classdev *led)
{
struct asus_wireless_data *data;
int s;
data = container_of(led, struct asus_wireless_data, led);
s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
ASUS_WIRELESS_LED_STATUS);
if (s == ASUS_WIRELESS_LED_ON)
return LED_FULL;
return LED_OFF;
}
static void led_state_update(struct work_struct *work)
{
struct asus_wireless_data *data;
data = container_of(work, struct asus_wireless_data, led_work);
asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
data->led_state);
}
static void led_state_set(struct led_classdev *led,
enum led_brightness value)
{
struct asus_wireless_data *data;
data = container_of(led, struct asus_wireless_data, led);
data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
ASUS_WIRELESS_LED_ON;
queue_work(data->wq, &data->led_work);
}
static void asus_wireless_notify(struct acpi_device *adev, u32 event) static void asus_wireless_notify(struct acpi_device *adev, u32 event)
{ {
struct asus_wireless_data *data = acpi_driver_data(adev); struct asus_wireless_data *data = acpi_driver_data(adev);
...@@ -37,6 +104,7 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event) ...@@ -37,6 +104,7 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
static int asus_wireless_add(struct acpi_device *adev) static int asus_wireless_add(struct acpi_device *adev)
{ {
struct asus_wireless_data *data; struct asus_wireless_data *data;
int err;
data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
...@@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev) ...@@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev)
data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK; data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
set_bit(EV_KEY, data->idev->evbit); set_bit(EV_KEY, data->idev->evbit);
set_bit(KEY_RFKILL, data->idev->keybit); set_bit(KEY_RFKILL, data->idev->keybit);
return input_register_device(data->idev); err = input_register_device(data->idev);
if (err)
return err;
data->adev = adev;
data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
if (!data->wq)
return -ENOMEM;
INIT_WORK(&data->led_work, led_state_update);
data->led.name = "asus-wireless::airplane";
data->led.brightness_set = led_state_set;
data->led.brightness_get = led_state_get;
data->led.flags = LED_CORE_SUSPENDRESUME;
data->led.max_brightness = 1;
err = devm_led_classdev_register(&adev->dev, &data->led);
if (err)
destroy_workqueue(data->wq);
return err;
} }
static int asus_wireless_remove(struct acpi_device *adev) static int asus_wireless_remove(struct acpi_device *adev)
{ {
struct asus_wireless_data *data = acpi_driver_data(adev);
if (data->wq)
destroy_workqueue(data->wq);
return 0; return 0;
} }
......
...@@ -2069,9 +2069,11 @@ static int asus_wmi_add(struct platform_device *pdev) ...@@ -2069,9 +2069,11 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err) if (err)
goto fail_leds; goto fail_leds;
err = asus_wmi_rfkill_init(asus); if (!asus->driver->quirks->no_rfkill) {
if (err) err = asus_wmi_rfkill_init(asus);
goto fail_rfkill; if (err)
goto fail_rfkill;
}
/* Some Asus desktop boards export an acpi-video backlight interface, /* Some Asus desktop boards export an acpi-video backlight interface,
stop this from showing up */ stop this from showing up */
......
...@@ -38,6 +38,7 @@ struct key_entry; ...@@ -38,6 +38,7 @@ struct key_entry;
struct asus_wmi; struct asus_wmi;
struct quirk_entry { struct quirk_entry {
bool no_rfkill;
bool hotplug_wireless; bool hotplug_wireless;
bool scalar_panel_brightness; bool scalar_panel_brightness;
bool store_backlight_power; bool store_backlight_power;
......
...@@ -80,66 +80,115 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = { ...@@ -80,66 +80,115 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
}; };
/* /*
* Keymap for WMI events of type 0x0000
*
* 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 const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } }, { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
{ KE_KEY, 0xe045, { KEY_PROG1 } }, /* Key code is followed by brightness level */
{ KE_KEY, 0xe009, { KEY_EJECTCD } }, { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
/* These also contain the brightness level at offset 6 */
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
/* Battery health status button */ /* Battery health status button */
{ KE_KEY, 0xe007, { KEY_BATTERY } }, { KE_KEY, 0xe007, { KEY_BATTERY } },
/* Radio devices state change */ /* Radio devices state change, key code is followed by other values */
{ KE_IGNORE, 0xe008, { KEY_RFKILL } }, { KE_IGNORE, 0xe008, { KEY_RFKILL } },
/* The next device is at offset 6, the active devices are at { KE_KEY, 0xe009, { KEY_EJECTCD } },
offset 8 and the attached devices at offset 10 */
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
/* Key code is followed by: next, active and attached devices */
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
/* Key code is followed by keyboard illumination level */
{ 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 } },
/* Unknown, defined in ACPI DSDT */
/* { KE_IGNORE, 0xe00e, { 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 } },
/* Unknown, defined in ACPI DSDT */
/* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */
/* Untested, Dell Instant Launch key on Inspiron 7520 */
/* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */
/* Dell Instant Launch key */ /* Dell Instant Launch key */
{ KE_KEY, 0xe025, { KEY_PROG4 } }, { KE_KEY, 0xe025, { KEY_PROG4 } },
{ KE_KEY, 0xe029, { KEY_PROG4 } },
/* Audio panel key */ /* Audio panel key */
{ KE_IGNORE, 0xe026, { KEY_RESERVED } }, { KE_IGNORE, 0xe026, { KEY_RESERVED } },
/* LCD Display On/Off Control key */
{ KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } },
/* Untested, Multimedia key on Dell Vostro 3560 */
/* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */
/* Dell Instant Launch key */
{ KE_KEY, 0xe029, { KEY_PROG4 } },
/* Untested, Windows Mobility Center button on Inspiron 7520 */
/* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */
/* Unknown, defined in ACPI DSDT */
/* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */
/* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */
/* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */
{ 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 } },
/* NIC Link is Up */
{ KE_IGNORE, 0xe043, { KEY_RESERVED } },
/* NIC Link is Down */
{ KE_IGNORE, 0xe044, { KEY_RESERVED } },
/*
* This entry is very suspicious!
* Originally Matthew Garrett created this dell-wmi driver specially for
* "button with a picture of a battery" which has event code 0xe045.
* Later Mario Limonciello from Dell told us that event code 0xe045 is
* reported by Num Lock and should be ignored because key is send also
* by keyboard controller.
* So for now we will ignore this event to prevent potential double
* Num Lock key press.
*/
{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
/* Scroll lock and also going to tablet mode on portable devices */
{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
/* Untested, going from tablet mode on portable devices */
/* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */
/* Dell Support Center key */
{ KE_IGNORE, 0xe06e, { KEY_RESERVED } },
{ KE_IGNORE, 0xe0f7, { KEY_MUTE } }, { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
{ KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } }, { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
{ KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } }, { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
{ KE_END, 0 }
}; };
static bool dell_new_hk_type;
struct dell_bios_keymap_entry { struct dell_bios_keymap_entry {
u16 scancode; u16 scancode;
u16 keycode; u16 keycode;
...@@ -153,6 +202,7 @@ struct dell_bios_hotkey_table { ...@@ -153,6 +202,7 @@ struct dell_bios_hotkey_table {
struct dell_dmi_results { struct dell_dmi_results {
int err; int err;
int keymap_size;
struct key_entry *keymap; struct key_entry *keymap;
}; };
...@@ -201,10 +251,12 @@ static const u16 bios_to_linux_keycode[256] __initconst = { ...@@ -201,10 +251,12 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
}; };
/* /*
* Keymap for WMI events of type 0x0010
*
* These are applied if the 0xB2 DMI hotkey table is present and doesn't * These are applied if the 0xB2 DMI hotkey table is present and doesn't
* override them. * override them.
*/ */
static const struct key_entry dell_wmi_extra_keymap[] __initconst = { static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
/* Fn-lock */ /* Fn-lock */
{ KE_IGNORE, 0x151, { KEY_RESERVED } }, { KE_IGNORE, 0x151, { KEY_RESERVED } },
...@@ -224,21 +276,39 @@ static const struct key_entry dell_wmi_extra_keymap[] __initconst = { ...@@ -224,21 +276,39 @@ static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
{ KE_IGNORE, 0x155, { KEY_RESERVED } }, { KE_IGNORE, 0x155, { KEY_RESERVED } },
}; };
/*
* Keymap for WMI events of type 0x0011
*/
static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
/* Battery unplugged */
{ KE_IGNORE, 0xfff0, { KEY_RESERVED } },
/* Battery inserted */
{ KE_IGNORE, 0xfff1, { KEY_RESERVED } },
/* Keyboard backlight level changed */
{ KE_IGNORE, 0x01e1, { KEY_RESERVED } },
{ KE_IGNORE, 0x02ea, { KEY_RESERVED } },
{ KE_IGNORE, 0x02eb, { KEY_RESERVED } },
{ KE_IGNORE, 0x02ec, { KEY_RESERVED } },
{ KE_IGNORE, 0x02f6, { KEY_RESERVED } },
};
static struct input_dev *dell_wmi_input_dev; static struct input_dev *dell_wmi_input_dev;
static void dell_wmi_process_key(int reported_key) static void dell_wmi_process_key(int type, int code)
{ {
const struct key_entry *key; const struct key_entry *key;
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
reported_key); (type << 16) | code);
if (!key) { if (!key) {
pr_info("Unknown key with scancode 0x%x pressed\n", pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
reported_key); type, code);
return; return;
} }
pr_debug("Key %x pressed\n", reported_key); pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code);
/* Don't report brightness notifications that will also come via ACPI */ /* Don't report brightness notifications that will also come via ACPI */
if ((key->keycode == KEY_BRIGHTNESSUP || if ((key->keycode == KEY_BRIGHTNESSUP ||
...@@ -246,7 +316,7 @@ static void dell_wmi_process_key(int reported_key) ...@@ -246,7 +316,7 @@ static void dell_wmi_process_key(int reported_key)
acpi_video_handles_brightness_key_presses()) acpi_video_handles_brightness_key_presses())
return; return;
if (reported_key == 0xe025 && !wmi_requires_smbios_request) if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
return; return;
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true); sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
...@@ -284,18 +354,6 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -284,18 +354,6 @@ static void dell_wmi_notify(u32 value, void *context)
buffer_entry = (u16 *)obj->buffer.pointer; buffer_entry = (u16 *)obj->buffer.pointer;
buffer_size = obj->buffer.length/2; buffer_size = obj->buffer.length/2;
if (!dell_new_hk_type) {
if (buffer_size >= 3 && buffer_entry[1] == 0x0)
dell_wmi_process_key(buffer_entry[2]);
else if (buffer_size >= 2)
dell_wmi_process_key(buffer_entry[1]);
else
pr_info("Received unknown WMI event\n");
kfree(obj);
return;
}
buffer_end = buffer_entry + buffer_size; buffer_end = buffer_entry + buffer_size;
/* /*
...@@ -330,62 +388,18 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -330,62 +388,18 @@ static void dell_wmi_notify(u32 value, void *context)
pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry); pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
switch (buffer_entry[1]) { switch (buffer_entry[1]) {
case 0x00: case 0x0000: /* One key pressed or event occurred */
for (i = 2; i < len; ++i) { if (len > 2)
switch (buffer_entry[i]) { dell_wmi_process_key(0x0000, buffer_entry[2]);
case 0xe043: /* Other entries could contain additional information */
/* NIC Link is Up */
pr_debug("NIC Link is Up\n");
break;
case 0xe044:
/* NIC Link is Down */
pr_debug("NIC Link is Down\n");
break;
case 0xe045:
/* Unknown event but defined in DSDT */
default:
/* Unknown event */
pr_info("Unknown WMI event type 0x00: "
"0x%x\n", (int)buffer_entry[i]);
break;
}
}
break; break;
case 0x10: case 0x0010: /* Sequence of keys pressed */
/* Keys pressed */ case 0x0011: /* Sequence of events occurred */
for (i = 2; i < len; ++i) for (i = 2; i < len; ++i)
dell_wmi_process_key(buffer_entry[i]); dell_wmi_process_key(buffer_entry[1],
break; buffer_entry[i]);
case 0x11:
for (i = 2; i < len; ++i) {
switch (buffer_entry[i]) {
case 0xfff0:
/* Battery unplugged */
pr_debug("Battery unplugged\n");
break;
case 0xfff1:
/* Battery inserted */
pr_debug("Battery inserted\n");
break;
case 0x01e1:
case 0x02ea:
case 0x02eb:
case 0x02ec:
case 0x02f6:
/* Keyboard backlight level changed */
pr_debug("Keyboard backlight level "
"changed\n");
break;
default:
/* Unknown event */
pr_info("Unknown WMI event type 0x11: "
"0x%x\n", (int)buffer_entry[i]);
break;
}
}
break; break;
default: default: /* Unknown event */
/* Unknown event */
pr_info("Unknown WMI event type 0x%x\n", pr_info("Unknown WMI event type 0x%x\n",
(int)buffer_entry[1]); (int)buffer_entry[1]);
break; break;
...@@ -410,7 +424,6 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) ...@@ -410,7 +424,6 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
} }
static void __init handle_dmi_entry(const struct dmi_header *dm, static void __init handle_dmi_entry(const struct dmi_header *dm,
void *opaque) void *opaque)
{ {
...@@ -418,7 +431,6 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, ...@@ -418,7 +431,6 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
struct dell_bios_hotkey_table *table; struct dell_bios_hotkey_table *table;
int hotkey_num, i, pos = 0; int hotkey_num, i, pos = 0;
struct key_entry *keymap; struct key_entry *keymap;
int num_bios_keys;
if (results->err || results->keymap) if (results->err || results->keymap)
return; /* We already found the hotkey table. */ return; /* We already found the hotkey table. */
...@@ -442,8 +454,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, ...@@ -442,8 +454,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
return; return;
} }
keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1, keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL);
sizeof(struct key_entry), GFP_KERNEL);
if (!keymap) { if (!keymap) {
results->err = -ENOMEM; results->err = -ENOMEM;
return; return;
...@@ -480,31 +491,15 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, ...@@ -480,31 +491,15 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
pos++; pos++;
} }
num_bios_keys = pos;
for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
const struct key_entry *entry = &dell_wmi_extra_keymap[i];
/*
* Check if we've already found this scancode. This takes
* quadratic time, but it doesn't matter unless the list
* of extra keys gets very long.
*/
if (!have_scancode(entry->code, keymap, num_bios_keys)) {
keymap[pos] = *entry;
pos++;
}
}
keymap[pos].type = KE_END;
results->keymap = keymap; results->keymap = keymap;
results->keymap_size = pos;
} }
static int __init dell_wmi_input_setup(void) static int __init dell_wmi_input_setup(void)
{ {
struct dell_dmi_results dmi_results = {}; struct dell_dmi_results dmi_results = {};
int err; struct key_entry *keymap;
int err, i, pos = 0;
dell_wmi_input_dev = input_allocate_device(); dell_wmi_input_dev = input_allocate_device();
if (!dell_wmi_input_dev) if (!dell_wmi_input_dev)
...@@ -528,21 +523,71 @@ static int __init dell_wmi_input_setup(void) ...@@ -528,21 +523,71 @@ static int __init dell_wmi_input_setup(void)
goto err_free_dev; goto err_free_dev;
} }
if (dmi_results.keymap) { keymap = kcalloc(dmi_results.keymap_size +
dell_new_hk_type = true; ARRAY_SIZE(dell_wmi_keymap_type_0000) +
ARRAY_SIZE(dell_wmi_keymap_type_0010) +
ARRAY_SIZE(dell_wmi_keymap_type_0011) +
1,
sizeof(struct key_entry), GFP_KERNEL);
if (!keymap) {
kfree(dmi_results.keymap);
err = -ENOMEM;
goto err_free_dev;
}
/* Append table with events of type 0x0010 which comes from DMI */
for (i = 0; i < dmi_results.keymap_size; i++) {
keymap[pos] = dmi_results.keymap[i];
keymap[pos].code |= (0x0010 << 16);
pos++;
}
kfree(dmi_results.keymap);
err = sparse_keymap_setup(dell_wmi_input_dev, /* Append table with extra events of type 0x0010 which are not in DMI */
dmi_results.keymap, NULL); for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) {
const struct key_entry *entry = &dell_wmi_keymap_type_0010[i];
/* /*
* Sparse keymap library makes a copy of keymap so we * Check if we've already found this scancode. This takes
* don't need the original one that was allocated. * quadratic time, but it doesn't matter unless the list
* of extra keys gets very long.
*/ */
kfree(dmi_results.keymap); if (dmi_results.keymap_size &&
} else { have_scancode(entry->code | (0x0010 << 16),
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, dmi_results.keymap_size)
dell_wmi_legacy_keymap, NULL); )
continue;
keymap[pos] = *entry;
keymap[pos].code |= (0x0010 << 16);
pos++;
}
/* Append table with events of type 0x0011 */
for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) {
keymap[pos] = dell_wmi_keymap_type_0011[i];
keymap[pos].code |= (0x0011 << 16);
pos++;
} }
/*
* Now append also table with "legacy" events of type 0x0000. Some of
* them are reported also on laptops which have scancodes in DMI.
*/
for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) {
keymap[pos] = dell_wmi_keymap_type_0000[i];
pos++;
}
keymap[pos].type = KE_END;
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
/*
* Sparse keymap library makes a copy of keymap so we don't need the
* original one that was allocated.
*/
kfree(keymap);
if (err) if (err)
goto err_free_dev; goto err_free_dev;
......
...@@ -88,9 +88,6 @@ ...@@ -88,9 +88,6 @@
#define ACPI_FUJITSU_NOTIFY_CODE1 0x80 #define ACPI_FUJITSU_NOTIFY_CODE1 0x80
#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
/* FUNC interface - command values */ /* FUNC interface - command values */
#define FUNC_RFKILL 0x1000 #define FUNC_RFKILL 0x1000
#define FUNC_LEDS 0x1001 #define FUNC_LEDS 0x1001
...@@ -108,6 +105,8 @@ ...@@ -108,6 +105,8 @@
#define LOGOLAMP_POWERON 0x2000 #define LOGOLAMP_POWERON 0x2000
#define LOGOLAMP_ALWAYS 0x4000 #define LOGOLAMP_ALWAYS 0x4000
#define RADIO_LED_ON 0x20 #define RADIO_LED_ON 0x20
#define ECO_LED 0x10000
#define ECO_LED_ON 0x80000
#endif #endif
/* Hotkey details */ /* Hotkey details */
...@@ -121,13 +120,6 @@ ...@@ -121,13 +120,6 @@
#define RINGBUFFERSIZE 40 #define RINGBUFFERSIZE 40
/* Debugging */ /* Debugging */
#define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": "
#define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG
#define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG
#define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG
#define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG
#define FUJLAPTOP_DBG_ALL 0xffff
#define FUJLAPTOP_DBG_ERROR 0x0001 #define FUJLAPTOP_DBG_ERROR 0x0001
#define FUJLAPTOP_DBG_WARN 0x0002 #define FUJLAPTOP_DBG_WARN 0x0002
#define FUJLAPTOP_DBG_INFO 0x0004 #define FUJLAPTOP_DBG_INFO 0x0004
...@@ -136,7 +128,7 @@ ...@@ -136,7 +128,7 @@
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \ #define vdbg_printk(a_dbg_level, format, arg...) \
do { if (dbg_level & a_dbg_level) \ do { if (dbg_level & a_dbg_level) \
printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
} while (0) } while (0)
#else #else
#define vdbg_printk(a_dbg_level, format, arg...) \ #define vdbg_printk(a_dbg_level, format, arg...) \
...@@ -176,6 +168,7 @@ struct fujitsu_hotkey_t { ...@@ -176,6 +168,7 @@ struct fujitsu_hotkey_t {
int logolamp_registered; int logolamp_registered;
int kblamps_registered; int kblamps_registered;
int radio_led_registered; int radio_led_registered;
int eco_led_registered;
}; };
static struct fujitsu_hotkey_t *fujitsu_hotkey; static struct fujitsu_hotkey_t *fujitsu_hotkey;
...@@ -212,6 +205,16 @@ static struct led_classdev radio_led = { ...@@ -212,6 +205,16 @@ static struct led_classdev radio_led = {
.brightness_get = radio_led_get, .brightness_get = radio_led_get,
.brightness_set = radio_led_set .brightness_set = radio_led_set
}; };
static enum led_brightness eco_led_get(struct led_classdev *cdev);
static void eco_led_set(struct led_classdev *cdev,
enum led_brightness brightness);
static struct led_classdev eco_led = {
.name = "fujitsu::eco_led",
.brightness_get = eco_led_get,
.brightness_set = eco_led_set
};
#endif #endif
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
...@@ -296,6 +299,18 @@ static void radio_led_set(struct led_classdev *cdev, ...@@ -296,6 +299,18 @@ static void radio_led_set(struct led_classdev *cdev,
call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0); call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
} }
static void eco_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
int curr;
curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
if (brightness >= LED_FULL)
call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON);
else
call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON);
}
static enum led_brightness logolamp_get(struct led_classdev *cdev) static enum led_brightness logolamp_get(struct led_classdev *cdev)
{ {
enum led_brightness brightness = LED_OFF; enum led_brightness brightness = LED_OFF;
...@@ -330,6 +345,16 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev) ...@@ -330,6 +345,16 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev)
return brightness; return brightness;
} }
static enum led_brightness eco_led_get(struct led_classdev *cdev)
{
enum led_brightness brightness = LED_OFF;
if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
brightness = LED_FULL;
return brightness;
}
#endif #endif
/* Hardware access for LCD brightness control */ /* Hardware access for LCD brightness control */
...@@ -856,6 +881,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) ...@@ -856,6 +881,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
set_bit(fujitsu->keycode3, input->keybit); set_bit(fujitsu->keycode3, input->keybit);
set_bit(fujitsu->keycode4, input->keybit); set_bit(fujitsu->keycode4, input->keybit);
set_bit(fujitsu->keycode5, input->keybit); set_bit(fujitsu->keycode5, input->keybit);
set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit);
set_bit(KEY_UNKNOWN, input->keybit); set_bit(KEY_UNKNOWN, input->keybit);
error = input_register_device(input); error = input_register_device(input);
...@@ -943,6 +969,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) ...@@ -943,6 +969,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
result); result);
} }
} }
/* Support for eco led is not always signaled in bit corresponding
* to the bit used to control the led. According to the DSDT table,
* bit 14 seems to indicate presence of said led as well.
* Confirm by testing the status.
*/
if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
(call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
result = led_classdev_register(&fujitsu->pf_device->dev,
&eco_led);
if (result == 0) {
fujitsu_hotkey->eco_led_registered = 1;
} else {
pr_err("Could not register LED handler for eco LED, error %i\n",
result);
}
}
#endif #endif
return result; return result;
...@@ -972,6 +1015,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) ...@@ -972,6 +1015,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
if (fujitsu_hotkey->radio_led_registered) if (fujitsu_hotkey->radio_led_registered)
led_classdev_unregister(&radio_led); led_classdev_unregister(&radio_led);
if (fujitsu_hotkey->eco_led_registered)
led_classdev_unregister(&eco_led);
#endif #endif
input_unregister_device(input); input_unregister_device(input);
...@@ -1060,6 +1106,19 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) ...@@ -1060,6 +1106,19 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
} }
} }
/* On some models (first seen on the Skylake-based Lifebook
* E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
* handled in software; its state is queried using FUNC_RFKILL
*/
if ((fujitsu_hotkey->rfkill_supported & BIT(26)) &&
(call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) {
keycode = KEY_TOUCHPAD_TOGGLE;
input_report_key(input, keycode, 1);
input_sync(input);
input_report_key(input, keycode, 0);
input_sync(input);
}
break; break;
default: default:
keycode = KEY_UNKNOWN; keycode = KEY_UNKNOWN;
......
...@@ -718,6 +718,11 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device) ...@@ -718,6 +718,11 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
if (err) if (err)
return err; return err;
err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, &wireless,
sizeof(wireless), 0);
if (err)
return err;
if (wireless & 0x1) { if (wireless & 0x1) {
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
RFKILL_TYPE_WLAN, RFKILL_TYPE_WLAN,
...@@ -882,7 +887,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) ...@@ -882,7 +887,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
wwan_rfkill = NULL; wwan_rfkill = NULL;
rfkill2_count = 0; rfkill2_count = 0;
if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device)) if (hp_wmi_rfkill_setup(device))
hp_wmi_rfkill2_setup(device); hp_wmi_rfkill2_setup(device);
err = device_create_file(&device->dev, &dev_attr_display); err = device_create_file(&device->dev, &dev_attr_display);
......
...@@ -122,8 +122,8 @@ static int intel_hid_input_setup(struct platform_device *device) ...@@ -122,8 +122,8 @@ static int intel_hid_input_setup(struct platform_device *device)
return 0; return 0;
err_free_device: err_free_device:
input_free_device(priv->input_dev); input_free_device(priv->input_dev);
return ret; return ret;
} }
static void intel_hid_input_destroy(struct platform_device *device) static void intel_hid_input_destroy(struct platform_device *device)
...@@ -224,7 +224,6 @@ static int intel_hid_remove(struct platform_device *device) ...@@ -224,7 +224,6 @@ static int intel_hid_remove(struct platform_device *device)
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
intel_hid_input_destroy(device); intel_hid_input_destroy(device);
intel_hid_set_enable(&device->dev, 0); intel_hid_set_enable(&device->dev, 0);
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
/* /*
* Even if we failed to shut off the event stream, we can still * Even if we failed to shut off the event stream, we can still
......
/*
* Intel Virtual Button driver for Windows 8.1+
*
* Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com>
* Copyright (C) 2016 Alex Hung <alex.hung@canonical.com>
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/sparse-keymap.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AceLan Kao");
static const struct acpi_device_id intel_vbtn_ids[] = {
{"INT33D6", 0},
{"", 0},
};
/* In theory, these are HID usages. */
static const struct key_entry intel_vbtn_keymap[] = {
{ KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
{ KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
{ KE_END },
};
struct intel_vbtn_priv {
struct input_dev *input_dev;
};
static int intel_vbtn_input_setup(struct platform_device *device)
{
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
int ret;
priv->input_dev = input_allocate_device();
if (!priv->input_dev)
return -ENOMEM;
ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
if (ret)
goto err_free_device;
priv->input_dev->dev.parent = &device->dev;
priv->input_dev->name = "Intel Virtual Button driver";
priv->input_dev->id.bustype = BUS_HOST;
ret = input_register_device(priv->input_dev);
if (ret)
goto err_free_device;
return 0;
err_free_device:
input_free_device(priv->input_dev);
return ret;
}
static void intel_vbtn_input_destroy(struct platform_device *device)
{
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
input_unregister_device(priv->input_dev);
}
static void notify_handler(acpi_handle handle, u32 event, void *context)
{
struct platform_device *device = context;
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
dev_info(&device->dev, "unknown event index 0x%x\n",
event);
}
static int intel_vbtn_probe(struct platform_device *device)
{
acpi_handle handle = ACPI_HANDLE(&device->dev);
struct intel_vbtn_priv *priv;
acpi_status status;
int err;
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
if (!ACPI_SUCCESS(status)) {
dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
return -ENODEV;
}
priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_set_drvdata(&device->dev, priv);
err = intel_vbtn_input_setup(device);
if (err) {
pr_err("Failed to setup Intel Virtual Button\n");
return err;
}
status = acpi_install_notify_handler(handle,
ACPI_DEVICE_NOTIFY,
notify_handler,
device);
if (ACPI_FAILURE(status)) {
err = -EBUSY;
goto err_remove_input;
}
return 0;
err_remove_input:
intel_vbtn_input_destroy(device);
return err;
}
static int intel_vbtn_remove(struct platform_device *device)
{
acpi_handle handle = ACPI_HANDLE(&device->dev);
intel_vbtn_input_destroy(device);
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
/*
* Even if we failed to shut off the event stream, we can still
* safely detach from the device.
*/
return 0;
}
static struct platform_driver intel_vbtn_pl_driver = {
.driver = {
.name = "intel-vbtn",
.acpi_match_table = intel_vbtn_ids,
},
.probe = intel_vbtn_probe,
.remove = intel_vbtn_remove,
};
MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids);
static acpi_status __init
check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
{
const struct acpi_device_id *ids = context;
struct acpi_device *dev;
if (acpi_bus_get_device(handle, &dev) != 0)
return AE_OK;
if (acpi_match_device_ids(dev, ids) == 0)
if (acpi_create_platform_device(dev))
dev_info(&dev->dev,
"intel-vbtn: created platform device\n");
return AE_OK;
}
static int __init intel_vbtn_init(void)
{
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, check_acpi_dev, NULL,
(void *)intel_vbtn_ids, NULL);
return platform_driver_register(&intel_vbtn_pl_driver);
}
module_init(intel_vbtn_init);
static void __exit intel_vbtn_exit(void)
{
platform_driver_unregister(&intel_vbtn_pl_driver);
}
module_exit(intel_vbtn_exit);
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/seq_file.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
...@@ -78,30 +77,18 @@ int intel_pmc_slp_s0_counter_read(u32 *data) ...@@ -78,30 +77,18 @@ int intel_pmc_slp_s0_counter_read(u32 *data)
} }
EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read); EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
#if IS_ENABLED(CONFIG_DEBUG_FS) static int pmc_core_dev_state_get(void *data, u64 *val)
static int pmc_core_dev_state_show(struct seq_file *s, void *unused)
{ {
struct pmc_dev *pmcdev = s->private; struct pmc_dev *pmcdev = data;
u32 counter_val; u32 value;
counter_val = pmc_core_reg_read(pmcdev, value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
SPT_PMC_SLP_S0_RES_COUNTER_OFFSET); *val = pmc_core_adjust_slp_s0_step(value);
seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val));
return 0; return 0;
} }
static int pmc_core_dev_state_open(struct inode *inode, struct file *file) DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
{
return single_open(file, pmc_core_dev_state_show, inode->i_private);
}
static const struct file_operations pmc_core_dev_state_ops = {
.open = pmc_core_dev_state_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
{ {
...@@ -113,12 +100,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) ...@@ -113,12 +100,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
struct dentry *dir, *file; struct dentry *dir, *file;
dir = debugfs_create_dir("pmc_core", NULL); dir = debugfs_create_dir("pmc_core", NULL);
if (!dir) if (IS_ERR_OR_NULL(dir))
return -ENOMEM; return -ENOMEM;
pmcdev->dbgfs_dir = dir; pmcdev->dbgfs_dir = dir;
file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO, file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
dir, pmcdev, &pmc_core_dev_state_ops); dir, pmcdev, &pmc_core_dev_state);
if (!file) { if (!file) {
pmc_core_dbgfs_unregister(pmcdev); pmc_core_dbgfs_unregister(pmcdev);
...@@ -127,16 +114,6 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) ...@@ -127,16 +114,6 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
return 0; return 0;
} }
#else
static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
{
return 0;
}
static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
{
}
#endif /* CONFIG_DEBUG_FS */
static const struct x86_cpu_id intel_pmc_core_ids[] = { static const struct x86_cpu_id intel_pmc_core_ids[] = {
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT, { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT,
...@@ -183,10 +160,8 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -183,10 +160,8 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
} }
err = pmc_core_dbgfs_register(pmcdev); err = pmc_core_dbgfs_register(pmcdev);
if (err < 0) { if (err < 0)
dev_err(&dev->dev, "PMC Core: debugfs register failed.\n"); dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n");
return err;
}
pmc.has_slp_s0_res = true; pmc.has_slp_s0_res = true;
return 0; return 0;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
/* Sunrise Point Power Management Controller PCI Device ID */ /* Sunrise Point Power Management Controller PCI Device ID */
#define SPT_PMC_PCI_DEVICE_ID 0x9d21 #define SPT_PMC_PCI_DEVICE_ID 0x9d21
#define SPT_PMC_BASE_ADDR_OFFSET 0x48 #define SPT_PMC_BASE_ADDR_OFFSET 0x48
#define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c #define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c
#define SPT_PMC_MMIO_REG_LEN 0x100 #define SPT_PMC_MMIO_REG_LEN 0x100
...@@ -42,9 +43,7 @@ ...@@ -42,9 +43,7 @@
struct pmc_dev { struct pmc_dev {
u32 base_addr; u32 base_addr;
void __iomem *regbase; void __iomem *regbase;
#if IS_ENABLED(CONFIG_DEBUG_FS)
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;
#endif /* CONFIG_DEBUG_FS */
bool has_slp_s0_res; bool has_slp_s0_res;
}; };
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0])) #define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0]))
#define TELEM_DEBUGFS_CPU(model, data) \ #define TELEM_DEBUGFS_CPU(model, data) \
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data} { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data}
#define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \ #define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \
if (evtlog[index].telem_evtid == (EVTID)) { \ if (evtlog[index].telem_evtid == (EVTID)) { \
......
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
#define TELEM_SET_VERBOSITY_BITS(x, y) ((x) |= ((y) << 27)) #define TELEM_SET_VERBOSITY_BITS(x, y) ((x) |= ((y) << 27))
#define TELEM_CPU(model, data) \ #define TELEM_CPU(model, data) \
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data } { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data }
enum telemetry_action { enum telemetry_action {
TELEM_UPDATE = 0, TELEM_UPDATE = 0,
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Copyright (C) 2002-2004 John Belmonte * Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale * Copyright (C) 2008 Philip Langdale
* Copyright (C) 2010 Pierre Ducroquet * Copyright (C) 2010 Pierre Ducroquet
* Copyright (C) 2014-2015 Azael Avalos * Copyright (C) 2014-2016 Azael Avalos
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define TOSHIBA_ACPI_VERSION "0.23" #define TOSHIBA_ACPI_VERSION "0.24"
#define PROC_INTERFACE_VERSION 1 #define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/iio/iio.h>
#include <linux/toshiba.h> #include <linux/toshiba.h>
#include <acpi/video.h> #include <acpi/video.h>
...@@ -134,6 +135,7 @@ MODULE_LICENSE("GPL"); ...@@ -134,6 +135,7 @@ MODULE_LICENSE("GPL");
/* Field definitions */ /* Field definitions */
#define HCI_ACCEL_MASK 0x7fff #define HCI_ACCEL_MASK 0x7fff
#define HCI_ACCEL_DIRECTION_MASK 0x8000
#define HCI_HOTKEY_DISABLE 0x0b #define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09 #define HCI_HOTKEY_ENABLE 0x09
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10 #define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
...@@ -178,6 +180,7 @@ struct toshiba_acpi_dev { ...@@ -178,6 +180,7 @@ struct toshiba_acpi_dev {
struct led_classdev eco_led; struct led_classdev eco_led;
struct miscdevice miscdev; struct miscdevice miscdev;
struct rfkill *wwan_rfk; struct rfkill *wwan_rfk;
struct iio_dev *indio_dev;
int force_fan; int force_fan;
int last_key_event; int last_key_event;
...@@ -1958,28 +1961,6 @@ static ssize_t touchpad_show(struct device *dev, ...@@ -1958,28 +1961,6 @@ static ssize_t touchpad_show(struct device *dev,
} }
static DEVICE_ATTR_RW(touchpad); static DEVICE_ATTR_RW(touchpad);
static ssize_t position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
u32 xyval, zval, tmp;
u16 x, y, z;
int ret;
xyval = zval = 0;
ret = toshiba_accelerometer_get(toshiba, &xyval, &zval);
if (ret < 0)
return ret;
x = xyval & HCI_ACCEL_MASK;
tmp = xyval >> HCI_MISC_SHIFT;
y = tmp & HCI_ACCEL_MASK;
z = zval & HCI_ACCEL_MASK;
return sprintf(buf, "%d %d %d\n", x, y, z);
}
static DEVICE_ATTR_RO(position);
static ssize_t usb_sleep_charge_show(struct device *dev, static ssize_t usb_sleep_charge_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
...@@ -2350,7 +2331,6 @@ static struct attribute *toshiba_attributes[] = { ...@@ -2350,7 +2331,6 @@ static struct attribute *toshiba_attributes[] = {
&dev_attr_available_kbd_modes.attr, &dev_attr_available_kbd_modes.attr,
&dev_attr_kbd_backlight_timeout.attr, &dev_attr_kbd_backlight_timeout.attr,
&dev_attr_touchpad.attr, &dev_attr_touchpad.attr,
&dev_attr_position.attr,
&dev_attr_usb_sleep_charge.attr, &dev_attr_usb_sleep_charge.attr,
&dev_attr_sleep_functions_on_battery.attr, &dev_attr_sleep_functions_on_battery.attr,
&dev_attr_usb_rapid_charge.attr, &dev_attr_usb_rapid_charge.attr,
...@@ -2377,8 +2357,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, ...@@ -2377,8 +2357,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
else if (attr == &dev_attr_touchpad.attr) else if (attr == &dev_attr_touchpad.attr)
exists = (drv->touchpad_supported) ? true : false; exists = (drv->touchpad_supported) ? true : false;
else if (attr == &dev_attr_position.attr)
exists = (drv->accelerometer_supported) ? true : false;
else if (attr == &dev_attr_usb_sleep_charge.attr) else if (attr == &dev_attr_usb_sleep_charge.attr)
exists = (drv->usb_sleep_charge_supported) ? true : false; exists = (drv->usb_sleep_charge_supported) ? true : false;
else if (attr == &dev_attr_sleep_functions_on_battery.attr) else if (attr == &dev_attr_sleep_functions_on_battery.attr)
...@@ -2419,6 +2397,81 @@ static void toshiba_acpi_kbd_bl_work(struct work_struct *work) ...@@ -2419,6 +2397,81 @@ static void toshiba_acpi_kbd_bl_work(struct work_struct *work)
0x92, 0); 0x92, 0);
} }
/*
* IIO device
*/
enum toshiba_iio_accel_chan {
AXIS_X,
AXIS_Y,
AXIS_Z
};
static int toshiba_iio_accel_get_axis(enum toshiba_iio_accel_chan chan)
{
u32 xyval, zval;
int ret;
ret = toshiba_accelerometer_get(toshiba_acpi, &xyval, &zval);
if (ret < 0)
return ret;
switch (chan) {
case AXIS_X:
return xyval & HCI_ACCEL_DIRECTION_MASK ?
-(xyval & HCI_ACCEL_MASK) : xyval & HCI_ACCEL_MASK;
case AXIS_Y:
return (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_DIRECTION_MASK ?
-((xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK) :
(xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK;
case AXIS_Z:
return zval & HCI_ACCEL_DIRECTION_MASK ?
-(zval & HCI_ACCEL_MASK) : zval & HCI_ACCEL_MASK;
}
return ret;
}
static int toshiba_iio_accel_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = toshiba_iio_accel_get_axis(chan->channel);
if (ret == -EIO || ret == -ENODEV)
return ret;
*val = ret;
return IIO_VAL_INT;
}
return -EINVAL;
}
#define TOSHIBA_IIO_ACCEL_CHANNEL(axis, chan) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel = chan, \
.channel2 = IIO_MOD_##axis, \
.output = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
}
static const struct iio_chan_spec toshiba_iio_accel_channels[] = {
TOSHIBA_IIO_ACCEL_CHANNEL(X, AXIS_X),
TOSHIBA_IIO_ACCEL_CHANNEL(Y, AXIS_Y),
TOSHIBA_IIO_ACCEL_CHANNEL(Z, AXIS_Z),
};
static const struct iio_info toshiba_iio_accel_info = {
.driver_module = THIS_MODULE,
.read_raw = &toshiba_iio_accel_read_raw,
};
/* /*
* Misc device * Misc device
*/ */
...@@ -2904,6 +2957,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) ...@@ -2904,6 +2957,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
remove_toshiba_proc_entries(dev); remove_toshiba_proc_entries(dev);
if (dev->accelerometer_supported && dev->indio_dev) {
iio_device_unregister(dev->indio_dev);
iio_device_free(dev->indio_dev);
}
if (dev->sysfs_created) if (dev->sysfs_created)
sysfs_remove_group(&dev->acpi_dev->dev.kobj, sysfs_remove_group(&dev->acpi_dev->dev.kobj,
&toshiba_attr_group); &toshiba_attr_group);
...@@ -3051,6 +3109,30 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) ...@@ -3051,6 +3109,30 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->touchpad_supported = !ret; dev->touchpad_supported = !ret;
toshiba_accelerometer_available(dev); toshiba_accelerometer_available(dev);
if (dev->accelerometer_supported) {
dev->indio_dev = iio_device_alloc(sizeof(*dev));
if (!dev->indio_dev) {
pr_err("Unable to allocate iio device\n");
goto iio_error;
}
pr_info("Registering Toshiba accelerometer iio device\n");
dev->indio_dev->info = &toshiba_iio_accel_info;
dev->indio_dev->name = "Toshiba accelerometer";
dev->indio_dev->dev.parent = &acpi_dev->dev;
dev->indio_dev->modes = INDIO_DIRECT_MODE;
dev->indio_dev->channels = toshiba_iio_accel_channels;
dev->indio_dev->num_channels =
ARRAY_SIZE(toshiba_iio_accel_channels);
ret = iio_device_register(dev->indio_dev);
if (ret < 0) {
pr_err("Unable to register iio device\n");
iio_device_free(dev->indio_dev);
}
}
iio_error:
toshiba_usb_sleep_charge_available(dev); toshiba_usb_sleep_charge_available(dev);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册