Commit 27b79027 authored by Linus Torvalds's avatar 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
  ...
parents f7e68169 332e0812
......@@ -5921,6 +5921,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
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
M: Len Brown <lenb@kernel.org>
L: linux-pm@vger.kernel.org
......
......@@ -603,6 +603,8 @@ config ASUS_WIRELESS
tristate "Asus Wireless Radio Control Driver"
depends on ACPI
depends on INPUT
select NEW_LEDS
select LEDS_CLASS
---help---
The Asus Wireless Radio Control handles the airplane mode hotkey
present on some Asus laptops.
......@@ -668,6 +670,7 @@ config ACPI_TOSHIBA
depends on SERIO_I8042 || SERIO_I8042 = n
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n
depends on IIO
select INPUT_POLLDEV
select INPUT_SPARSEKMAP
---help---
......@@ -770,6 +773,18 @@ config INTEL_HID_EVENT
To compile this driver as a module, choose M here: the module will
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
bool "Intel SCU IPC Support"
depends on X86_INTEL_MID
......
......@@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.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_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
......
......@@ -78,6 +78,15 @@ static struct quirk_entry quirk_asus_x200ca = {
.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)
{
quirks = dmi->driver_data;
......@@ -133,7 +142,7 @@ static const struct dmi_system_id asus_quirks[] = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"),
},
.driver_data = &quirk_asus_wapf4,
.driver_data = &quirk_no_rfkill_wapf4,
},
{
.callback = dmi_matched,
......@@ -142,7 +151,7 @@ static const struct dmi_system_id asus_quirks[] = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"),
},
.driver_data = &quirk_asus_wapf4,
.driver_data = &quirk_no_rfkill_wapf4,
},
{
.callback = dmi_matched,
......@@ -306,6 +315,42 @@ static const struct dmi_system_id asus_quirks[] = {
},
.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[] = {
{ KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ 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, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
......
......@@ -15,11 +15,78 @@
#include <linux/acpi.h>
#include <linux/input.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 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)
{
struct asus_wireless_data *data = acpi_driver_data(adev);
......@@ -37,6 +104,7 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
static int asus_wireless_add(struct acpi_device *adev)
{
struct asus_wireless_data *data;
int err;
data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
......@@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev)
data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
set_bit(EV_KEY, data->idev->evbit);
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)
{
struct asus_wireless_data *data = acpi_driver_data(adev);
if (data->wq)
destroy_workqueue(data->wq);
return 0;
}
......
......@@ -2069,9 +2069,11 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_leds;
err = asus_wmi_rfkill_init(asus);
if (err)
goto fail_rfkill;
if (!asus->driver->quirks->no_rfkill) {
err = asus_wmi_rfkill_init(asus);
if (err)
goto fail_rfkill;
}
/* Some Asus desktop boards export an acpi-video backlight interface,
stop this from showing up */
......
......@@ -38,6 +38,7 @@ struct key_entry;
struct asus_wmi;
struct quirk_entry {
bool no_rfkill;
bool hotplug_wireless;
bool scalar_panel_brightness;
bool store_backlight_power;
......
......@@ -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
* notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again.
*/
static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
{ KE_KEY, 0xe045, { KEY_PROG1 } },
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
/* These also contain the brightness level at offset 6 */
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
/* Key code is followed by brightness level */
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
/* 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 } },
/* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
/* 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 } },
/* BIOS error detected */
{ KE_IGNORE, 0xe00d, { KEY_RESERVED } },
/* Unknown, defined in ACPI DSDT */
/* { KE_IGNORE, 0xe00e, { KEY_RESERVED } }, */
/* Wifi Catcher */
{ KE_KEY, 0xe011, {KEY_PROG2 } },
{ KE_KEY, 0xe011, { KEY_PROG2 } },
/* Ambient light sensor toggle */
{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
{ 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 */
{ KE_KEY, 0xe025, { KEY_PROG4 } },
{ KE_KEY, 0xe029, { KEY_PROG4 } },
{ KE_KEY, 0xe025, { KEY_PROG4 } },
/* Audio panel key */
{ 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, 0xe030, { KEY_VOLUMEUP } },
{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
{ 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 } },
/* Scroll lock and also going to tablet mode on portable devices */
{ 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, 0xe0f8, { KEY_VOLUMEDOWN } },
{ KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
{ KE_END, 0 }
};
static bool dell_new_hk_type;
struct dell_bios_keymap_entry {
u16 scancode;
u16 keycode;
......@@ -153,6 +202,7 @@ struct dell_bios_hotkey_table {
struct dell_dmi_results {
int err;
int keymap_size;
struct key_entry *keymap;
};
......@@ -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
* 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 */
{ KE_IGNORE, 0x151, { KEY_RESERVED } },
......@@ -224,21 +276,39 @@ static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
{ 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 void dell_wmi_process_key(int reported_key)
static void dell_wmi_process_key(int type, int code)
{
const struct key_entry *key;
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
reported_key);
(type << 16) | code);
if (!key) {
pr_info("Unknown key with scancode 0x%x pressed\n",
reported_key);
pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
type, code);
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 */
if ((key->keycode == KEY_BRIGHTNESSUP ||
......@@ -246,7 +316,7 @@ static void dell_wmi_process_key(int reported_key)
acpi_video_handles_brightness_key_presses())
return;
if (reported_key == 0xe025 && !wmi_requires_smbios_request)
if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
return;
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
......@@ -284,18 +354,6 @@ static void dell_wmi_notify(u32 value, void *context)
buffer_entry = (u16 *)obj->buffer.pointer;
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;
/*
......@@ -330,62 +388,18 @@ static void dell_wmi_notify(u32 value, void *context)
pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
switch (buffer_entry[1]) {
case 0x00:
for (i = 2; i < len; ++i) {
switch (buffer_entry[i]) {
case 0xe043:
/* 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;
}
}
case 0x0000: /* One key pressed or event occurred */
if (len > 2)
dell_wmi_process_key(0x0000, buffer_entry[2]);
/* Other entries could contain additional information */
break;
case 0x10:
/* Keys pressed */
case 0x0010: /* Sequence of keys pressed */
case 0x0011: /* Sequence of events occurred */
for (i = 2; i < len; ++i)
dell_wmi_process_key(buffer_entry[i]);
break;
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;
}
}
dell_wmi_process_key(buffer_entry[1],
buffer_entry[i]);
break;
default:
/* Unknown event */
default: /* Unknown event */
pr_info("Unknown WMI event type 0x%x\n",
(int)buffer_entry[1]);
break;
......@@ -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,
void *opaque)
{
......@@ -418,7 +431,6 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
struct dell_bios_hotkey_table *table;
int hotkey_num, i, pos = 0;
struct key_entry *keymap;
int num_bios_keys;
if (results->err || results->keymap)
return; /* We already found the hotkey table. */
......@@ -442,8 +454,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
return;
}
keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
sizeof(struct key_entry), GFP_KERNEL);
keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL);
if (!keymap) {
results->err = -ENOMEM;
return;
......@@ -480,31 +491,15 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
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_size = pos;
}
static int __init dell_wmi_input_setup(void)
{
struct dell_dmi_results dmi_results = {};
int err;
struct key_entry *keymap;
int err, i, pos = 0;
dell_wmi_input_dev = input_allocate_device();
if (!dell_wmi_input_dev)
......@@ -528,21 +523,71 @@ static int __init dell_wmi_input_setup(void)
goto err_free_dev;
}
if (dmi_results.keymap) {
dell_new_hk_type = true;
keymap = kcalloc(dmi_results.keymap_size +
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,
dmi_results.keymap, NULL);
/* Append table with extra events of type 0x0010 which are not in DMI */
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
* don't need the original one that was allocated.
* 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.
*/
kfree(dmi_results.keymap);
} else {
err = sparse_keymap_setup(dell_wmi_input_dev,
dell_wmi_legacy_keymap, NULL);
if (dmi_results.keymap_size &&
have_scancode(entry->code | (0x0010 << 16),
keymap, dmi_results.keymap_size)
)
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)
goto err_free_dev;
......
......@@ -88,9 +88,6 @@
#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
/* FUNC interface - command values */
#define FUNC_RFKILL 0x1000
#define FUNC_LEDS 0x1001
......@@ -108,6 +105,8 @@
#define LOGOLAMP_POWERON 0x2000
#define LOGOLAMP_ALWAYS 0x4000
#define RADIO_LED_ON 0x20
#define ECO_LED 0x10000
#define ECO_LED_ON 0x80000
#endif
/* Hotkey details */
......@@ -121,13 +120,6 @@
#define RINGBUFFERSIZE 40
/* 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_WARN 0x0002
#define FUJLAPTOP_DBG_INFO 0x0004
......@@ -136,7 +128,7 @@
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \
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)
#else
#define vdbg_printk(a_dbg_level, format, arg...) \
......@@ -176,6 +168,7 @@ struct fujitsu_hotkey_t {
int logolamp_registered;
int kblamps_registered;
int radio_led_registered;
int eco_led_registered;
};
static struct fujitsu_hotkey_t *fujitsu_hotkey;
......@@ -212,6 +205,16 @@ static struct led_classdev radio_led = {
.brightness_get = radio_led_get,
.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
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
......@@ -296,6 +299,18 @@ static void radio_led_set(struct led_classdev *cdev,
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)
{
enum led_brightness brightness = LED_OFF;
......@@ -330,6 +345,16 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev)
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
/* Hardware access for LCD brightness control */
......@@ -856,6 +881,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
set_bit(fujitsu->keycode3, input->keybit);
set_bit(fujitsu->keycode4, input->keybit);
set_bit(fujitsu->keycode5, input->keybit);
set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit);
set_bit(KEY_UNKNOWN, input->keybit);
error = input_register_device(input);
......@@ -943,6 +969,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
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
return result;
......@@ -972,6 +1015,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
if (fujitsu_hotkey->radio_led_registered)
led_classdev_unregister(&radio_led);
if (fujitsu_hotkey->eco_led_registered)
led_classdev_unregister(&eco_led);
#endif
input_unregister_device(input);
......@@ -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;
default:
keycode = KEY_UNKNOWN;
......
......@@ -718,6 +718,11 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
if (err)
return err;
err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, &wireless,
sizeof(wireless), 0);
if (err)
return err;
if (wireless & 0x1) {
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
RFKILL_TYPE_WLAN,
......@@ -882,7 +887,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
wwan_rfkill = NULL;
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);
err = device_create_file(&device->dev, &dev_attr_display);
......
......@@ -122,8 +122,8 @@ static int intel_hid_input_setup(struct platform_device *device)
return 0;
err_free_device:
input_free_device(priv->input_dev);
return ret;
input_free_device(priv->input_dev);
return ret;
}
static void intel_hid_input_destroy(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);
intel_hid_input_destroy(device);
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
......
/*
* 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 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
......@@ -78,30 +77,18 @@ int intel_pmc_slp_s0_counter_read(u32 *data)
}
EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
#if IS_ENABLED(CONFIG_DEBUG_FS)
static int pmc_core_dev_state_show(struct seq_file *s, void *unused)
static int pmc_core_dev_state_get(void *data, u64 *val)
{
struct pmc_dev *pmcdev = s->private;
u32 counter_val;
struct pmc_dev *pmcdev = data;
u32 value;
counter_val = pmc_core_reg_read(pmcdev,
SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val));
value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
*val = pmc_core_adjust_slp_s0_step(value);
return 0;
}
static int pmc_core_dev_state_open(struct inode *inode, struct file *file)
{
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,
};
DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
{
......@@ -113,12 +100,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
struct dentry *dir, *file;
dir = debugfs_create_dir("pmc_core", NULL);
if (!dir)
if (IS_ERR_OR_NULL(dir))
return -ENOMEM;
pmcdev->dbgfs_dir = dir;
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) {
pmc_core_dbgfs_unregister(pmcdev);
......@@ -127,16 +114,6 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
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[] = {
{ 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)
}
err = pmc_core_dbgfs_register(pmcdev);
if (err < 0) {
dev_err(&dev->dev, "PMC Core: debugfs register failed.\n");
return err;
}
if (err < 0)
dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n");
pmc.has_slp_s0_res = true;
return 0;
......
......@@ -23,6 +23,7 @@
/* Sunrise Point Power Management Controller PCI Device ID */
#define SPT_PMC_PCI_DEVICE_ID 0x9d21
#define SPT_PMC_BASE_ADDR_OFFSET 0x48
#define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c
#define SPT_PMC_MMIO_REG_LEN 0x100
......@@ -42,9 +43,7 @@
struct pmc_dev {
u32 base_addr;
void __iomem *regbase;
#if IS_ENABLED(CONFIG_DEBUG_FS)
struct dentry *dbgfs_dir;
#endif /* CONFIG_DEBUG_FS */
bool has_slp_s0_res;
};
......
......@@ -79,7 +79,7 @@
#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0]))
#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) { \
if (evtlog[index].telem_evtid == (EVTID)) { \
......
......@@ -83,7 +83,7 @@
#define TELEM_SET_VERBOSITY_BITS(x, y) ((x) |= ((y) << 27))
#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 {
TELEM_UPDATE = 0,
......
......@@ -4,7 +4,7 @@
* Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale
* 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
* it under the terms of the GNU General Public License as published by
......@@ -31,7 +31,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define TOSHIBA_ACPI_VERSION "0.23"
#define TOSHIBA_ACPI_VERSION "0.24"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
......@@ -53,6 +53,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/rfkill.h>
#include <linux/iio/iio.h>
#include <linux/toshiba.h>
#include <acpi/video.h>
......@@ -134,6 +135,7 @@ MODULE_LICENSE("GPL");
/* Field definitions */
#define HCI_ACCEL_MASK 0x7fff
#define HCI_ACCEL_DIRECTION_MASK 0x8000
#define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
......@@ -178,6 +180,7 @@ struct toshiba_acpi_dev {
struct led_classdev eco_led;
struct miscdevice miscdev;
struct rfkill *wwan_rfk;
struct iio_dev *indio_dev;
int force_fan;
int last_key_event;
......@@ -1958,28 +1961,6 @@ static ssize_t touchpad_show(struct device *dev,
}
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,
struct device_attribute *attr, char *buf)
{
......@@ -2350,7 +2331,6 @@ static struct attribute *toshiba_attributes[] = {
&dev_attr_available_kbd_modes.attr,
&dev_attr_kbd_backlight_timeout.attr,
&dev_attr_touchpad.attr,
&dev_attr_position.attr,
&dev_attr_usb_sleep_charge.attr,
&dev_attr_sleep_functions_on_battery.attr,
&dev_attr_usb_rapid_charge.attr,
......@@ -2377,8 +2357,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
else if (attr == &dev_attr_touchpad.attr)
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)
exists = (drv->usb_sleep_charge_supported) ? true : false;
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)
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
*/
......@@ -2904,6 +2957,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_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)
sysfs_remove_group(&dev->acpi_dev->dev.kobj,
&toshiba_attr_group);
......@@ -3051,6 +3109,30 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->touchpad_supported = !ret;
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);
......
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