]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'platform-drivers-x86-v4.11-1' of git://git.infradead.org/linux-platform...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 25 Feb 2017 22:35:37 +0000 (14:35 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 25 Feb 2017 22:35:37 +0000 (14:35 -0800)
Pull x86 platform driver updates from Darren Hart:
 "Big picture:

   - New intel_turbo_max_3 driver, providing max core frequency
     information to the scheduler. Intel PMC APL support, s0ix read API,
     and fixes.

   - New Silead touchscreen platform touchscreen descriptions.
     Additional hotkey support for the intel-hid driver.

   - New model support for dell-laptop and hp_accel.

   - Several cleanups, especially to the fujitsu-laptop and
     intel_mid_powerbtn drivers.

  Detail summary:

  platorm/x86:
   - silead depends on I2C being built-in
   - add support for devices with Silead touchscreens
   - Support Turbo Boost Max 3.0 for non HWP systems

  intel_turbo_max_3:
   - make it explicitly non-modular

  dell-laptop:
   - Add Latitude 7480 and others to the DMI whitelist

  intel-hid:
   - Support 5 button array

  thinkpad_acpi:
   - Call led_classdev_notify_brightness_hw_changed on kbd brightness change
   - Use brightness_set_blocking callback for LEDs
   - Stop setting led_classdev brightness directly

  acer-wmi:
   - add another KEY_WLAN keycode
   - Inform firmware that RF Button Driver is active
   - setup accelerometer when machine has appropriate notify event

  asus-wireless:
   - Fix indentation
   - Use per-HID HSWC parameters

  intel_pmc_ipc:
   - Add APL PMC PCI Id
   - read s0ix residency API
   - Remove unused iTCO_version variable

  alienware-wmi:
   - Remove header duplicate

  intel_pmc_core:
   - fix out-of-bounds accesses on stack

  intel_mid_powerbtn:
   - Use SCU IPC directly
   - Unify IRQ acknowledgment
   - Move comment to where it belongs
   - Unify PBSTATUS access
   - Remove snail address
   - Sort headers alphabetically
   - Join string literals
   - Enable driver for Merrifield
   - Acknowledge interrupts
   - Factor out mfld_ack()
   - Introduce driver data
   - Substitute mfld by mid
   - Convert to use devm_*()

  fujitsu-laptop:
   - make hotkey handling functions more similar
   - break up complex loop condition
   - move keycode processing to separate functions
   - decrease indentation in acpi_fujitsu_hotkey_notify()
   - simplify logolamp_get()
   - rework logolamp_set() to properly handle errors
   - set default trigger for radio LED to rfkill-any

  dell-smbios:
   - Auto-select as needed

  intel_mid_thermal:
   - Fix module autoload
   - Remove duplicated platform device ID

  mlx-platform:
   - mlxcpld-hotplug driver style fixes

  hp_accel:
   - Add support for HP ZBook 17"

* tag 'platform-drivers-x86-v4.11-1' of git://git.infradead.org/linux-platform-drivers-x86: (45 commits)
  platform/x86: intel_turbo_max_3: make it explicitly non-modular
  platform/x86: dell-laptop: Add Latitude 7480 and others to the DMI whitelist
  platform/x86: intel-hid: Support 5 button array
  platform/x86: thinkpad_acpi: Call led_classdev_notify_brightness_hw_changed on kbd brightness change
  platform/x86: thinkpad_acpi: Use brightness_set_blocking callback for LEDs
  platform/x86: thinkpad_acpi: Stop setting led_classdev brightness directly
  leds: class: Add new optional brightness_hw_changed attribute
  platform/x86: acer-wmi: add another KEY_WLAN keycode
  platform/x86: acer-wmi: Inform firmware that RF Button Driver is active
  platform/x86: asus-wireless: Fix indentation
  platform/x86: asus-wireless: Use per-HID HSWC parameters
  platform/x86: intel_pmc_ipc: Add APL PMC PCI Id
  platform/x86: intel_pmc_ipc: read s0ix residency API
  platform/x86: alienware-wmi: Remove header duplicate
  platform/x86: intel_mid_powerbtn: Use SCU IPC directly
  platform/x86: intel_mid_powerbtn: Unify IRQ acknowledgment
  platform/x86: intel_mid_powerbtn: Move comment to where it belongs
  platform/x86: intel_mid_powerbtn: Unify PBSTATUS access
  platform/x86: intel_pmc_core: fix out-of-bounds accesses on stack
  platform/x86: silead depends on I2C being built-in
  ...

19 files changed:
MAINTAINERS
arch/x86/include/asm/intel_pmc_ipc.h
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/alienware-wmi.c
drivers/platform/x86/asus-wireless.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp_accel.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel_mid_powerbtn.c
drivers/platform/x86/intel_mid_thermal.c
drivers/platform/x86/intel_pmc_core.c
drivers/platform/x86/intel_pmc_ipc.c
drivers/platform/x86/intel_turbo_max_3.c [new file with mode: 0644]
drivers/platform/x86/mlx-platform.c
drivers/platform/x86/silead_dmi.c [new file with mode: 0644]
drivers/platform/x86/thinkpad_acpi.c

index dedc66c265c9876a328dd1dea4787453647ac049..b4474a8b7e8ab922f8d5abe4517a400b03a54123 100644 (file)
@@ -11456,6 +11456,14 @@ F:     drivers/media/usb/siano/
 F:     drivers/media/usb/siano/
 F:     drivers/media/mmc/siano/
 
+SILEAD TOUCHSCREEN DRIVER
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     linux-input@vger.kernel.org
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/input/touchscreen/silead.c
+F:     drivers/platform/x86/silead_dmi.c
+
 SIMPLEFB FB DRIVER
 M:     Hans de Goede <hdegoede@redhat.com>
 L:     linux-fbdev@vger.kernel.org
index cd0310e186f4af534b7e21312b81d092ff9ba8cd..4291b6a5ddf795ecee1da4aec4dd091d1b5fce48 100644 (file)
@@ -30,6 +30,7 @@ int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
                u32 *out, u32 outlen, u32 dptr, u32 sptr);
 int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
                u32 *out, u32 outlen);
+int intel_pmc_s0ix_counter_read(u64 *data);
 
 #else
 
@@ -50,6 +51,11 @@ static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
        return -EINVAL;
 }
 
+static inline int intel_pmc_s0ix_counter_read(u64 *data)
+{
+       return -EINVAL;
+}
+
 #endif /*CONFIG_INTEL_PMC_IPC*/
 
 #endif
index a9e9c91cf4d4af6cebd0ac1cb999e8bf5b5ade95..4bc88eb52712c7a8231ac64b94bbfacd5607f1f5 100644 (file)
@@ -92,9 +92,8 @@ config ASUS_LAPTOP
          If you have an ACPI-compatible ASUS laptop, say Y or M here.
 
 config DELL_SMBIOS
-       tristate "Dell SMBIOS Support"
-       depends on DCDBAS
-       default n
+       tristate
+       select DCDBAS
        ---help---
        This module provides common functions for kernel modules using
        Dell SMBIOS.
@@ -103,16 +102,15 @@ config DELL_SMBIOS
 
 config DELL_LAPTOP
        tristate "Dell Laptop Extras"
-       depends on DELL_SMBIOS
        depends on DMI
        depends on BACKLIGHT_CLASS_DEVICE
        depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL || RFKILL = n
        depends on SERIO_I8042
+       select DELL_SMBIOS
        select POWER_SUPPLY
        select LEDS_CLASS
        select NEW_LEDS
-       default n
        ---help---
        This driver adds support for rfkill and backlight control to Dell
        laptops (except for some models covered by the Compal driver).
@@ -123,7 +121,7 @@ config DELL_WMI
        depends on DMI
        depends on INPUT
        depends on ACPI_VIDEO || ACPI_VIDEO = n
-       depends on DELL_SMBIOS
+       select DELL_SMBIOS
        select INPUT_SPARSEKMAP
        ---help---
          Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@@ -1069,6 +1067,27 @@ config MLX_CPLD_PLATFORM
          This driver handles hot-plug events for the power suppliers, power
          cables and fans on the wide range Mellanox IB and Ethernet systems.
 
+config INTEL_TURBO_MAX_3
+       bool "Intel Turbo Boost Max Technology 3.0 enumeration driver"
+       depends on X86_64 && SCHED_MC_PRIO
+       ---help---
+         This driver reads maximum performance ratio of each CPU and set up
+         the scheduler priority metrics. In this way scheduler can prefer
+         CPU with higher performance to schedule tasks.
+         This driver is only required when the system is not using Hardware
+         P-States (HWP). In HWP mode, priority can be read from ACPI tables.
+
+config SILEAD_DMI
+       bool "Tablets with Silead touchscreens"
+       depends on ACPI && DMI && I2C=y && INPUT
+       ---help---
+         Certain ACPI based tablets with Silead touchscreens do not have
+         enough data in ACPI tables for the touchscreen driver to handle
+         the touchscreen properly, as OEMs expected the data to be baked
+         into the tablet model specific version of the driver shipped
+         with the OS-image for the device. This option supplies the missing
+         information. Enable this for x86 tablets with Silead touchscreens.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
index cf9fc3e930c79dce8025526377dad0f504549412..299d0f9e40f7b309feaf534764a5e852f061aa14 100644 (file)
@@ -65,6 +65,7 @@ obj-$(CONFIG_INTEL_SMARTCONNECT)      += intel-smartconnect.o
 obj-$(CONFIG_PVPANIC)           += pvpanic.o
 obj-$(CONFIG_ALIENWARE_WMI)    += alienware-wmi.o
 obj-$(CONFIG_INTEL_PMC_IPC)    += intel_pmc_ipc.o
+obj-$(CONFIG_SILEAD_DMI)       += silead_dmi.o
 obj-$(CONFIG_SURFACE_PRO3_BUTTON)      += surfacepro3_button.o
 obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
 obj-$(CONFIG_INTEL_PUNIT_IPC)  += intel_punit_ipc.o
@@ -76,3 +77,4 @@ obj-$(CONFIG_INTEL_PMC_CORE)    += intel_pmc_core.o
 obj-$(CONFIG_PMC_ATOM)         += pmc_atom.o
 obj-$(CONFIG_MLX_PLATFORM)     += mlx-platform.o
 obj-$(CONFIG_MLX_CPLD_PLATFORM)        += mlxcpld-hotplug.o
+obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
index a66192f692e31afbad13294039121e81f0727f26..dac0fbe87460ebc5b816746830e626ba3876fe83 100644 (file)
@@ -128,6 +128,7 @@ static const struct key_entry acer_wmi_keymap[] __initconst = {
        {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },
        {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} },
        {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} },
+       {KE_KEY, 0x86, {KEY_WLAN} },
        {KE_END, 0}
 };
 
@@ -150,15 +151,30 @@ struct event_return_value {
 #define ACER_WMID3_GDS_BLUETOOTH       (1<<11) /* BT */
 #define ACER_WMID3_GDS_TOUCHPAD                (1<<1)  /* Touchpad */
 
-struct lm_input_params {
+/* Hotkey Customized Setting and Acer Application Status.
+ * Set Device Default Value and Report Acer Application Status.
+ * When Acer Application starts, it will run this method to inform
+ * BIOS/EC that Acer Application is on.
+ * App Status
+ *     Bit[0]: Launch Manager Status
+ *     Bit[1]: ePM Status
+ *     Bit[2]: Device Control Status
+ *     Bit[3]: Acer Power Button Utility Status
+ *     Bit[4]: RF Button Status
+ *     Bit[5]: ODD PM Status
+ *     Bit[6]: Device Default Value Control
+ *     Bit[7]: Hall Sensor Application Status
+ */
+struct func_input_params {
        u8 function_num;        /* Function Number */
        u16 commun_devices;     /* Communication type devices default status */
        u16 devices;            /* Other type devices default status */
-       u8 lm_status;           /* Launch Manager Status */
-       u16 reserved;
+       u8 app_status;          /* Acer Device Status. LM, ePM, RF Button... */
+       u8 app_mask;            /* Bit mask to app_status */
+       u8 reserved;
 } __attribute__((packed));
 
-struct lm_return_value {
+struct func_return_value {
        u8 error_code;          /* Error Code */
        u8 ec_return_value;     /* EC Return Value */
        u16 reserved;
@@ -1769,13 +1785,13 @@ static void acer_wmi_notify(u32 value, void *context)
 }
 
 static acpi_status __init
-wmid3_set_lm_mode(struct lm_input_params *params,
-                 struct lm_return_value *return_value)
+wmid3_set_function_mode(struct func_input_params *params,
+                       struct func_return_value *return_value)
 {
        acpi_status status;
        union acpi_object *obj;
 
-       struct acpi_buffer input = { sizeof(struct lm_input_params), params };
+       struct acpi_buffer input = { sizeof(struct func_input_params), params };
        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 
        status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output);
@@ -1796,7 +1812,7 @@ wmid3_set_lm_mode(struct lm_input_params *params,
                return AE_ERROR;
        }
 
-       *return_value = *((struct lm_return_value *)obj->buffer.pointer);
+       *return_value = *((struct func_return_value *)obj->buffer.pointer);
        kfree(obj);
 
        return status;
@@ -1804,16 +1820,17 @@ wmid3_set_lm_mode(struct lm_input_params *params,
 
 static int __init acer_wmi_enable_ec_raw(void)
 {
-       struct lm_return_value return_value;
+       struct func_return_value return_value;
        acpi_status status;
-       struct lm_input_params params = {
+       struct func_input_params params = {
                .function_num = 0x1,
                .commun_devices = 0xFFFF,
                .devices = 0xFFFF,
-               .lm_status = 0x00,            /* Launch Manager Deactive */
+               .app_status = 0x00,             /* Launch Manager Deactive */
+               .app_mask = 0x01,
        };
 
-       status = wmid3_set_lm_mode(&params, &return_value);
+       status = wmid3_set_function_mode(&params, &return_value);
 
        if (return_value.error_code || return_value.ec_return_value)
                pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n",
@@ -1827,16 +1844,17 @@ static int __init acer_wmi_enable_ec_raw(void)
 
 static int __init acer_wmi_enable_lm(void)
 {
-       struct lm_return_value return_value;
+       struct func_return_value return_value;
        acpi_status status;
-       struct lm_input_params params = {
+       struct func_input_params params = {
                .function_num = 0x1,
                .commun_devices = 0xFFFF,
                .devices = 0xFFFF,
-               .lm_status = 0x01,            /* Launch Manager Active */
+               .app_status = 0x01,            /* Launch Manager Active */
+               .app_mask = 0x01,
        };
 
-       status = wmid3_set_lm_mode(&params, &return_value);
+       status = wmid3_set_function_mode(&params, &return_value);
 
        if (return_value.error_code || return_value.ec_return_value)
                pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n",
@@ -1846,11 +1864,46 @@ static int __init acer_wmi_enable_lm(void)
        return status;
 }
 
+static int __init acer_wmi_enable_rf_button(void)
+{
+       struct func_return_value return_value;
+       acpi_status status;
+       struct func_input_params params = {
+               .function_num = 0x1,
+               .commun_devices = 0xFFFF,
+               .devices = 0xFFFF,
+               .app_status = 0x10,            /* RF Button Active */
+               .app_mask = 0x10,
+       };
+
+       status = wmid3_set_function_mode(&params, &return_value);
+
+       if (return_value.error_code || return_value.ec_return_value)
+               pr_warn("Enabling RF Button failed: 0x%x - 0x%x\n",
+                       return_value.error_code,
+                       return_value.ec_return_value);
+
+       return status;
+}
+
+#define ACER_WMID_ACCEL_HID    "BST0001"
+
 static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
                                                void *ctx, void **retval)
 {
+       struct acpi_device *dev;
+
+       if (!strcmp(ctx, "SENR")) {
+               if (acpi_bus_get_device(ah, &dev))
+                       return AE_OK;
+               if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev)))
+                       return AE_OK;
+       } else
+               return AE_OK;
+
        *(acpi_handle *)retval = ah;
-       return AE_OK;
+
+       return AE_CTRL_TERMINATE;
 }
 
 static int __init acer_wmi_get_handle(const char *name, const char *prop,
@@ -1877,7 +1930,7 @@ static int __init acer_wmi_accel_setup(void)
 {
        int err;
 
-       err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+       err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle);
        if (err)
                return err;
 
@@ -2216,6 +2269,9 @@ static int __init acer_wmi_init(void)
                interface->capability &= ~ACER_CAP_BRIGHTNESS;
 
        if (wmi_has_guid(WMID_GUID3)) {
+               if (ACPI_FAILURE(acer_wmi_enable_rf_button()))
+                       pr_warn("Cannot enable RF Button Driver\n");
+
                if (ec_raw_mode) {
                        if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
                                pr_err("Cannot enable EC raw mode\n");
@@ -2233,10 +2289,11 @@ static int __init acer_wmi_init(void)
                err = acer_wmi_input_setup();
                if (err)
                        return err;
+               err = acer_wmi_accel_setup();
+               if (err)
+                       return err;
        }
 
-       acer_wmi_accel_setup();
-
        err = platform_driver_register(&acer_platform_driver);
        if (err) {
                pr_err("Unable to register platform driver\n");
index 005629447b0ce624b24ed02835e3d9314065a50a..d6b34923fb4edd061ab18a57d2c8d281136f84b7 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/dmi.h>
-#include <linux/acpi.h>
 #include <linux/leds.h>
 
 #define LEGACY_CONTROL_GUID            "A90597CE-A997-11DA-B012-B622A1EF5492"
index 9f31bc1a47d01b9c88072f60444d53917a63af84..f3796164329efb9ea00aed180a8c1116b813c979 100644 (file)
 #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 hswc_params {
+       u8 on;
+       u8 off;
+       u8 status;
+};
 
 struct asus_wireless_data {
        struct input_dev *idev;
        struct acpi_device *adev;
+       const struct hswc_params *hswc_params;
        struct workqueue_struct *wq;
        struct work_struct led_work;
        struct led_classdev led;
        int led_state;
 };
 
+static const struct hswc_params atk4001_id_params = {
+       .on = 0x0,
+       .off = 0x1,
+       .status = 0x2,
+};
+
+static const struct hswc_params atk4002_id_params = {
+       .on = 0x5,
+       .off = 0x4,
+       .status = 0x2,
+};
+
+static const struct acpi_device_id device_ids[] = {
+       {"ATK4001", (kernel_ulong_t)&atk4001_id_params},
+       {"ATK4002", (kernel_ulong_t)&atk4002_id_params},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, device_ids);
+
 static u64 asus_wireless_method(acpi_handle handle, const char *method,
                                int param)
 {
@@ -61,8 +83,8 @@ static enum led_brightness led_state_get(struct led_classdev *led)
 
        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)
+                                data->hswc_params->status);
+       if (s == data->hswc_params->on)
                return LED_FULL;
        return LED_OFF;
 }
@@ -76,14 +98,13 @@ static void led_state_update(struct work_struct *work)
                             data->led_state);
 }
 
-static void led_state_set(struct led_classdev *led,
-                                 enum led_brightness value)
+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;
+       data->led_state = value == LED_OFF ? data->hswc_params->off :
+                                            data->hswc_params->on;
        queue_work(data->wq, &data->led_work);
 }
 
@@ -104,12 +125,14 @@ 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;
+       const struct acpi_device_id *id;
        int err;
 
        data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
        adev->driver_data = data;
+       data->adev = adev;
 
        data->idev = devm_input_allocate_device(&adev->dev);
        if (!data->idev)
@@ -124,7 +147,16 @@ static int asus_wireless_add(struct acpi_device *adev)
        if (err)
                return err;
 
-       data->adev = adev;
+       for (id = device_ids; id->id[0]; id++) {
+               if (!strcmp((char *) id->id, acpi_device_hid(adev))) {
+                       data->hswc_params =
+                               (const struct hswc_params *)id->driver_data;
+                       break;
+               }
+       }
+       if (!data->hswc_params)
+               return 0;
+
        data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
        if (!data->wq)
                return -ENOMEM;
@@ -137,6 +169,7 @@ static int asus_wireless_add(struct acpi_device *adev)
        err = devm_led_classdev_register(&adev->dev, &data->led);
        if (err)
                destroy_workqueue(data->wq);
+
        return err;
 }
 
@@ -149,13 +182,6 @@ static int asus_wireless_remove(struct acpi_device *adev)
        return 0;
 }
 
-static const struct acpi_device_id device_ids[] = {
-       {"ATK4001", 0},
-       {"ATK4002", 0},
-       {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, device_ids);
-
 static struct acpi_driver asus_wireless_driver = {
        .name = "Asus Wireless Radio Control Driver",
        .class = "hotkey",
index 14392a01ab360aceaf80c8424e2b4854ac5ea264..f57dd282a0021a5645eab7fb56b6c7a10307a4d1 100644 (file)
@@ -105,6 +105,12 @@ static const struct dmi_system_id dell_device_table[] __initconst = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
                },
        },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
+               },
+       },
        {
                .ident = "Dell Computer Corporation",
                .matches = {
index 82d67715ce76d9ff0442ecdcdbd2dbb324380203..2b218b1d13e55dc985a2ca27e44b6a6ddf905141 100644 (file)
@@ -202,6 +202,7 @@ static int radio_led_set(struct led_classdev *cdev,
 
 static struct led_classdev radio_led = {
  .name = "fujitsu::radio_led",
+ .default_trigger = "rfkill-any",
  .brightness_get = radio_led_get,
  .brightness_set_blocking = radio_led_set
 };
@@ -270,15 +271,20 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
 static int logolamp_set(struct led_classdev *cdev,
                               enum led_brightness brightness)
 {
-       if (brightness >= LED_FULL) {
-               call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
-               return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
-       } else if (brightness >= LED_HALF) {
-               call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
-               return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
-       } else {
-               return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
-       }
+       int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
+       int ret;
+
+       if (brightness < LED_HALF)
+               poweron = FUNC_LED_OFF;
+
+       if (brightness < LED_FULL)
+               always = FUNC_LED_OFF;
+
+       ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
+       if (ret < 0)
+               return ret;
+
+       return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
 }
 
 static int kblamps_set(struct led_classdev *cdev,
@@ -313,17 +319,17 @@ static int eco_led_set(struct led_classdev *cdev,
 
 static enum led_brightness logolamp_get(struct led_classdev *cdev)
 {
-       enum led_brightness brightness = LED_OFF;
-       int poweron, always;
-
-       poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
-       if (poweron == FUNC_LED_ON) {
-               brightness = LED_HALF;
-               always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
-               if (always == FUNC_LED_ON)
-                       brightness = LED_FULL;
-       }
-       return brightness;
+       int ret;
+
+       ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
+       if (ret == FUNC_LED_ON)
+               return LED_FULL;
+
+       ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
+       if (ret == FUNC_LED_ON)
+               return LED_HALF;
+
+       return LED_OFF;
 }
 
 static enum led_brightness kblamps_get(struct led_classdev *cdev)
@@ -1029,107 +1035,117 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
        return 0;
 }
 
+static void acpi_fujitsu_hotkey_press(int keycode)
+{
+       struct input_dev *input = fujitsu_hotkey->input;
+       int status;
+
+       status = kfifo_in_locked(&fujitsu_hotkey->fifo,
+                                (unsigned char *)&keycode, sizeof(keycode),
+                                &fujitsu_hotkey->fifo_lock);
+       if (status != sizeof(keycode)) {
+               vdbg_printk(FUJLAPTOP_DBG_WARN,
+                           "Could not push keycode [0x%x]\n", keycode);
+               return;
+       }
+       input_report_key(input, keycode, 1);
+       input_sync(input);
+       vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                   "Push keycode into ringbuffer [%d]\n", keycode);
+}
+
+static void acpi_fujitsu_hotkey_release(void)
+{
+       struct input_dev *input = fujitsu_hotkey->input;
+       int keycode, status;
+
+       while (true) {
+               status = kfifo_out_locked(&fujitsu_hotkey->fifo,
+                                         (unsigned char *)&keycode,
+                                         sizeof(keycode),
+                                         &fujitsu_hotkey->fifo_lock);
+               if (status != sizeof(keycode))
+                       return;
+               input_report_key(input, keycode, 0);
+               input_sync(input);
+               vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                           "Pop keycode from ringbuffer [%d]\n", keycode);
+       }
+}
+
 static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
 {
        struct input_dev *input;
-       int keycode, keycode_r;
+       int keycode;
        unsigned int irb = 1;
-       int i, status;
+       int i;
 
        input = fujitsu_hotkey->input;
 
+       if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
+               keycode = KEY_UNKNOWN;
+               vdbg_printk(FUJLAPTOP_DBG_WARN,
+                           "Unsupported event [0x%x]\n", event);
+               input_report_key(input, keycode, 1);
+               input_sync(input);
+               input_report_key(input, keycode, 0);
+               input_sync(input);
+               return;
+       }
+
        if (fujitsu_hotkey->rfkill_supported)
                fujitsu_hotkey->rfkill_state =
                        call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
 
-       switch (event) {
-       case ACPI_FUJITSU_NOTIFY_CODE1:
-               i = 0;
-               while ((irb =
-                       call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
-                               && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
-                       switch (irb & 0x4ff) {
-                       case KEY1_CODE:
-                               keycode = fujitsu->keycode1;
-                               break;
-                       case KEY2_CODE:
-                               keycode = fujitsu->keycode2;
-                               break;
-                       case KEY3_CODE:
-                               keycode = fujitsu->keycode3;
-                               break;
-                       case KEY4_CODE:
-                               keycode = fujitsu->keycode4;
-                               break;
-                       case KEY5_CODE:
-                               keycode = fujitsu->keycode5;
-                               break;
-                       case 0:
-                               keycode = 0;
-                               break;
-                       default:
-                               vdbg_printk(FUJLAPTOP_DBG_WARN,
-                                           "Unknown GIRB result [%x]\n", irb);
-                               keycode = -1;
-                               break;
-                       }
-                       if (keycode > 0) {
-                               vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                                       "Push keycode into ringbuffer [%d]\n",
-                                       keycode);
-                               status = kfifo_in_locked(&fujitsu_hotkey->fifo,
-                                                  (unsigned char *)&keycode,
-                                                  sizeof(keycode),
-                                                  &fujitsu_hotkey->fifo_lock);
-                               if (status != sizeof(keycode)) {
-                                       vdbg_printk(FUJLAPTOP_DBG_WARN,
-                                           "Could not push keycode [0x%x]\n",
-                                           keycode);
-                               } else {
-                                       input_report_key(input, keycode, 1);
-                                       input_sync(input);
-                               }
-                       } else if (keycode == 0) {
-                               while ((status =
-                                       kfifo_out_locked(
-                                        &fujitsu_hotkey->fifo,
-                                        (unsigned char *) &keycode_r,
-                                        sizeof(keycode_r),
-                                        &fujitsu_hotkey->fifo_lock))
-                                        == sizeof(keycode_r)) {
-                                       input_report_key(input, keycode_r, 0);
-                                       input_sync(input);
-                                       vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                                         "Pop keycode from ringbuffer [%d]\n",
-                                         keycode_r);
-                               }
-                       }
+       i = 0;
+       while ((irb =
+               call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
+                       && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
+               switch (irb & 0x4ff) {
+               case KEY1_CODE:
+                       keycode = fujitsu->keycode1;
+                       break;
+               case KEY2_CODE:
+                       keycode = fujitsu->keycode2;
+                       break;
+               case KEY3_CODE:
+                       keycode = fujitsu->keycode3;
+                       break;
+               case KEY4_CODE:
+                       keycode = fujitsu->keycode4;
+                       break;
+               case KEY5_CODE:
+                       keycode = fujitsu->keycode5;
+                       break;
+               case 0:
+                       keycode = 0;
+                       break;
+               default:
+                       vdbg_printk(FUJLAPTOP_DBG_WARN,
+                                   "Unknown GIRB result [%x]\n", irb);
+                       keycode = -1;
+                       break;
                }
 
-               /* 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);
-               }
+               if (keycode > 0)
+                       acpi_fujitsu_hotkey_press(keycode);
+               else if (keycode == 0)
+                       acpi_fujitsu_hotkey_release();
+       }
 
-               break;
-       default:
-               keycode = KEY_UNKNOWN;
-               vdbg_printk(FUJLAPTOP_DBG_WARN,
-                           "Unsupported event [0x%x]\n", 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;
        }
+
 }
 
 /* Initialization */
index 09356684c32f6a78f03dbc5ef1dd7addc828bf08..493d8910a74e2eca10812c3202a74cdb21ed37cd 100644 (file)
@@ -251,6 +251,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
        AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
        AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
        AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted),
+       AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted),
        { NULL, }
 /* Laptop models without axis info (yet):
  * "NC6910" "HP Compaq 6910"
index cb3ab2b212b1768b8355aced7d7956f9e1ed2185..bcf438f38781019a2db04847e77aa4bfce023eee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Intel HID event driver for Windows 8
+ *  Intel HID event & 5 button array driver
  *
  *  Copyright (C) 2015 Alex Hung <alex.hung@canonical.com>
  *  Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org>
@@ -57,8 +57,24 @@ static const struct key_entry intel_hid_keymap[] = {
        { KE_END },
 };
 
+/* 5 button array notification value. */
+static const struct key_entry intel_array_keymap[] = {
+       { KE_KEY,    0xC2, { KEY_LEFTMETA } },                /* Press */
+       { KE_IGNORE, 0xC3, { KEY_LEFTMETA } },                /* Release */
+       { KE_KEY,    0xC4, { KEY_VOLUMEUP } },                /* Press */
+       { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } },                /* Release */
+       { KE_KEY,    0xC6, { KEY_VOLUMEDOWN } },              /* Press */
+       { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },              /* Release */
+       { KE_SW,     0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */
+       { KE_SW,     0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */
+       { KE_KEY,    0xCE, { KEY_POWER } },                   /* Press */
+       { KE_IGNORE, 0xCF, { KEY_POWER } },                   /* Release */
+       { KE_END },
+};
+
 struct intel_hid_priv {
        struct input_dev *input_dev;
+       struct input_dev *array;
 };
 
 static int intel_hid_set_enable(struct device *device, int enable)
@@ -78,15 +94,43 @@ static int intel_hid_set_enable(struct device *device, int enable)
        return 0;
 }
 
+static void intel_button_array_enable(struct device *device, bool enable)
+{
+       struct intel_hid_priv *priv = dev_get_drvdata(device);
+       acpi_handle handle = ACPI_HANDLE(device);
+       unsigned long long button_cap;
+       acpi_status status;
+
+       if (!priv->array)
+               return;
+
+       /* Query supported platform features */
+       status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap);
+       if (ACPI_FAILURE(status)) {
+               dev_warn(device, "failed to get button capability\n");
+               return;
+       }
+
+       /* Enable|disable features - power button is always enabled */
+       status = acpi_execute_simple_method(handle, "BTNE",
+                                           enable ? button_cap : 1);
+       if (ACPI_FAILURE(status))
+               dev_warn(device, "failed to set button capability\n");
+}
+
 static int intel_hid_pl_suspend_handler(struct device *device)
 {
        intel_hid_set_enable(device, 0);
+       intel_button_array_enable(device, false);
+
        return 0;
 }
 
 static int intel_hid_pl_resume_handler(struct device *device)
 {
        intel_hid_set_enable(device, 1);
+       intel_button_array_enable(device, true);
+
        return 0;
 }
 
@@ -126,6 +170,27 @@ err_free_device:
        return ret;
 }
 
+static int intel_button_array_input_setup(struct platform_device *device)
+{
+       struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
+       int ret;
+
+       /* Setup input device for 5 button array */
+       priv->array = devm_input_allocate_device(&device->dev);
+       if (!priv->array)
+               return -ENOMEM;
+
+       ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL);
+       if (ret)
+               return ret;
+
+       priv->array->dev.parent = &device->dev;
+       priv->array->name = "Intel HID 5 button array";
+       priv->array->id.bustype = BUS_HOST;
+
+       return input_register_device(priv->array);
+}
+
 static void intel_hid_input_destroy(struct platform_device *device)
 {
        struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
@@ -140,10 +205,11 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
        unsigned long long ev_index;
        acpi_status status;
 
-       /* The platform spec only defines one event code: 0xC0. */
+       /* 0xC0 is for HID events, other values are for 5 button array */
        if (event != 0xc0) {
-               dev_warn(&device->dev, "received unknown event (0x%x)\n",
-                        event);
+               if (!priv->array ||
+                   !sparse_keymap_report_event(priv->array, event, 1, true))
+                       dev_info(&device->dev, "unknown event 0x%x\n", event);
                return;
        }
 
@@ -161,8 +227,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
 static int intel_hid_probe(struct platform_device *device)
 {
        acpi_handle handle = ACPI_HANDLE(&device->dev);
+       unsigned long long event_cap, mode;
        struct intel_hid_priv *priv;
-       unsigned long long mode;
        acpi_status status;
        int err;
 
@@ -193,6 +259,15 @@ static int intel_hid_probe(struct platform_device *device)
                return err;
        }
 
+       /* Setup 5 button array */
+       status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap);
+       if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) {
+               dev_info(&device->dev, "platform supports 5 button array\n");
+               err = intel_button_array_input_setup(device);
+               if (err)
+                       pr_err("Failed to setup Intel 5 button array hotkeys\n");
+       }
+
        status = acpi_install_notify_handler(handle,
                                             ACPI_DEVICE_NOTIFY,
                                             notify_handler,
@@ -206,6 +281,16 @@ static int intel_hid_probe(struct platform_device *device)
        if (err)
                goto err_remove_notify;
 
+       if (priv->array) {
+               intel_button_array_enable(&device->dev, true);
+
+               /* Call button load method to enable HID power button */
+               status = acpi_evaluate_object(handle, "BTNL", NULL, NULL);
+               if (ACPI_FAILURE(status))
+                       dev_warn(&device->dev,
+                                "failed to enable HID power button\n");
+       }
+
        return 0;
 
 err_remove_notify:
@@ -224,6 +309,7 @@ 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);
+       intel_button_array_enable(&device->dev, false);
 
        /*
         * Even if we failed to shut off the event stream, we can still
index 361770568ad03a6e7a3bc7e6d579ccacae1725b7..871cfa682519c2256126edae36dffefc9537ed8b 100644 (file)
@@ -1,7 +1,10 @@
 /*
- * Power button driver for Medfield.
+ * Power button driver for Intel MID platforms.
  *
- * Copyright (C) 2010 Intel Corp
+ * Copyright (C) 2010,2017 Intel Corp
+ *
+ * Author: Hong Liu <hong.liu@intel.com>
+ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.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
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
 #include <linux/input.h>
+#include <linux/interrupt.h>
 #include <linux/mfd/intel_msic.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include <asm/intel_scu_ipc.h>
 
 #define DRIVER_NAME "msic_power_btn"
 
  */
 #define MSIC_PWRBTNM    (1 << 0)
 
-static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
+/* Intel Tangier */
+#define BCOVE_PB_LEVEL         (1 << 4)        /* 1 - release, 0 - press */
+
+/* Basin Cove PMIC */
+#define BCOVE_PBIRQ            0x02
+#define BCOVE_IRQLVL1MSK       0x0c
+#define BCOVE_PBIRQMASK                0x0d
+#define BCOVE_PBSTATUS         0x27
+
+struct mid_pb_ddata {
+       struct device *dev;
+       int irq;
+       struct input_dev *input;
+       unsigned short mirqlvl1_addr;
+       unsigned short pbstat_addr;
+       u8 pbstat_mask;
+       int (*setup)(struct mid_pb_ddata *ddata);
+};
+
+static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
 {
-       struct input_dev *input = dev_id;
+       struct input_dev *input = ddata->input;
        int ret;
        u8 pbstat;
 
-       ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat);
+       ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
+       if (ret)
+               return ret;
+
        dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat);
 
+       *value = !(pbstat & ddata->pbstat_mask);
+       return 0;
+}
+
+static int mid_irq_ack(struct mid_pb_ddata *ddata)
+{
+       return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
+}
+
+static int mrfld_setup(struct mid_pb_ddata *ddata)
+{
+       /* Unmask the PBIRQ and MPBIRQ on Tangier */
+       intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
+       intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
+
+       return 0;
+}
+
+static irqreturn_t mid_pb_isr(int irq, void *dev_id)
+{
+       struct mid_pb_ddata *ddata = dev_id;
+       struct input_dev *input = ddata->input;
+       int value = 0;
+       int ret;
+
+       ret = mid_pbstat(ddata, &value);
        if (ret < 0) {
-               dev_err(input->dev.parent, "Read error %d while reading"
-                              " MSIC_PB_STATUS\n", ret);
+               dev_err(input->dev.parent,
+                       "Read error %d while reading MSIC_PB_STATUS\n", ret);
        } else {
-               input_event(input, EV_KEY, KEY_POWER,
-                              !(pbstat & MSIC_PB_LEVEL));
+               input_event(input, EV_KEY, KEY_POWER, value);
                input_sync(input);
        }
 
+       mid_irq_ack(ddata);
        return IRQ_HANDLED;
 }
 
-static int mfld_pb_probe(struct platform_device *pdev)
+static struct mid_pb_ddata mfld_ddata = {
+       .mirqlvl1_addr  = INTEL_MSIC_IRQLVL1MSK,
+       .pbstat_addr    = INTEL_MSIC_PBSTATUS,
+       .pbstat_mask    = MSIC_PB_LEVEL,
+};
+
+static struct mid_pb_ddata mrfld_ddata = {
+       .mirqlvl1_addr  = BCOVE_IRQLVL1MSK,
+       .pbstat_addr    = BCOVE_PBSTATUS,
+       .pbstat_mask    = BCOVE_PB_LEVEL,
+       .setup  = mrfld_setup,
+};
+
+#define ICPU(model, ddata)     \
+       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata }
+
+static const struct x86_cpu_id mid_pb_cpu_ids[] = {
+       ICPU(INTEL_FAM6_ATOM_PENWELL,           mfld_ddata),
+       ICPU(INTEL_FAM6_ATOM_MERRIFIELD,        mrfld_ddata),
+       {}
+};
+
+static int mid_pb_probe(struct platform_device *pdev)
 {
+       const struct x86_cpu_id *id;
+       struct mid_pb_ddata *ddata;
        struct input_dev *input;
        int irq = platform_get_irq(pdev, 0);
        int error;
 
+       id = x86_match_cpu(mid_pb_cpu_ids);
+       if (!id)
+               return -ENODEV;
+
        if (irq < 0)
                return -EINVAL;
 
-       input = input_allocate_device();
+       input = devm_input_allocate_device(&pdev->dev);
        if (!input)
                return -ENOMEM;
 
@@ -77,25 +156,36 @@ static int mfld_pb_probe(struct platform_device *pdev)
 
        input_set_capability(input, EV_KEY, KEY_POWER);
 
-       error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT,
-                                    DRIVER_NAME, input);
-       if (error) {
-               dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
-                               "button\n", irq);
-               goto err_free_input;
+       ddata = (struct mid_pb_ddata *)id->driver_data;
+       if (!ddata)
+               return -ENODATA;
+
+       ddata->dev = &pdev->dev;
+       ddata->irq = irq;
+       ddata->input = input;
+
+       if (ddata->setup) {
+               error = ddata->setup(ddata);
+               if (error)
+                       return error;
        }
 
-       device_init_wakeup(&pdev->dev, true);
-       dev_pm_set_wake_irq(&pdev->dev, irq);
+       error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
+                                         IRQF_ONESHOT, DRIVER_NAME, ddata);
+       if (error) {
+               dev_err(&pdev->dev,
+                       "Unable to request irq %d for MID power button\n", irq);
+               return error;
+       }
 
        error = input_register_device(input);
        if (error) {
-               dev_err(&pdev->dev, "Unable to register input dev, error "
-                               "%d\n", error);
-               goto err_free_irq;
+               dev_err(&pdev->dev,
+                       "Unable to register input dev, error %d\n", error);
+               return error;
        }
 
-       platform_set_drvdata(pdev, input);
+       platform_set_drvdata(pdev, ddata);
 
        /*
         * SCU firmware might send power button interrupts to IA core before
@@ -107,46 +197,39 @@ static int mfld_pb_probe(struct platform_device *pdev)
         * initialization. The race happens rarely. So we needn't worry
         * about it.
         */
-       error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM);
+       error = mid_irq_ack(ddata);
        if (error) {
-               dev_err(&pdev->dev, "Unable to clear power button interrupt, "
-                               "error: %d\n", error);
-               goto err_free_irq;
+               dev_err(&pdev->dev,
+                       "Unable to clear power button interrupt, error: %d\n",
+                       error);
+               return error;
        }
 
-       return 0;
+       device_init_wakeup(&pdev->dev, true);
+       dev_pm_set_wake_irq(&pdev->dev, irq);
 
-err_free_irq:
-       free_irq(irq, input);
-err_free_input:
-       input_free_device(input);
-       return error;
+       return 0;
 }
 
-static int mfld_pb_remove(struct platform_device *pdev)
+static int mid_pb_remove(struct platform_device *pdev)
 {
-       struct input_dev *input = platform_get_drvdata(pdev);
-       int irq = platform_get_irq(pdev, 0);
-
        dev_pm_clear_wake_irq(&pdev->dev);
        device_init_wakeup(&pdev->dev, false);
-       free_irq(irq, input);
-       input_unregister_device(input);
 
        return 0;
 }
 
-static struct platform_driver mfld_pb_driver = {
+static struct platform_driver mid_pb_driver = {
        .driver = {
                .name = DRIVER_NAME,
        },
-       .probe  = mfld_pb_probe,
-       .remove = mfld_pb_remove,
+       .probe  = mid_pb_probe,
+       .remove = mid_pb_remove,
 };
 
-module_platform_driver(mfld_pb_driver);
+module_platform_driver(mid_pb_driver);
 
 MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
-MODULE_DESCRIPTION("Intel Medfield Power Button Driver");
+MODULE_DESCRIPTION("Intel MID Power Button Driver");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:" DRIVER_NAME);
index 0df3c9d375096e77234aba0048fdd59366b12696..008a76903cbfb62221ac4fd4bb8aaa7b46c56fb1 100644 (file)
@@ -549,9 +549,9 @@ static int mid_thermal_remove(struct platform_device *pdev)
 
 static const struct platform_device_id therm_id_table[] = {
        { DRIVER_NAME, 1 },
-       { "msic_thermal", 1 },
        { }
 };
+MODULE_DEVICE_TABLE(platform, therm_id_table);
 
 static struct platform_driver mid_thermal_driver = {
        .driver = {
index b130b8c9b9d7b429e9dbb0b17926285337ab1d78..914bcd2edbde6b172ab47dfbec1ddbb2f93880e1 100644 (file)
@@ -188,8 +188,7 @@ static int pmc_core_check_read_lock_bit(void)
        u32 value;
 
        value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET);
-       return test_bit(SPT_PMC_READ_DISABLE_BIT,
-                       (unsigned long *)&value);
+       return value & BIT(SPT_PMC_READ_DISABLE_BIT);
 }
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -238,8 +237,7 @@ static int pmc_core_mtpmc_link_status(void)
        u32 value;
 
        value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET);
-       return test_bit(SPT_PMC_MSG_FULL_STS_BIT,
-                       (unsigned long *)&value);
+       return value & BIT(SPT_PMC_MSG_FULL_STS_BIT);
 }
 
 static int pmc_core_send_msg(u32 *addr_xram)
index 0bf51d574fa9a47ada5e6e0b3c98d6cb4d710581..0651d47b8eeb7e59aa371ff61388a1df79b25ce0 100644 (file)
 #include <linux/notifier.h>
 #include <linux/suspend.h>
 #include <linux/acpi.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
 #include <asm/intel_pmc_ipc.h>
+
 #include <linux/platform_data/itco_wdt.h>
 
 /*
 #define IPC_WRITE_BUFFER       0x80
 #define IPC_READ_BUFFER                0x90
 
+/* PMC Global Control Registers */
+#define GCR_TELEM_DEEP_S0IX_OFFSET     0x1078
+#define GCR_TELEM_SHLW_S0IX_OFFSET     0x1080
+
+/* Residency with clock rate at 19.2MHz to usecs */
+#define S0IX_RESIDENCY_IN_USECS(d, s)          \
+({                                             \
+       u64 result = 10ull * ((d) + (s));       \
+       do_div(result, 192);                    \
+       result;                                 \
+})
+
 /*
  * 16-byte buffer for sending data associated with IPC command.
  */
@@ -68,7 +83,7 @@
 #define PLAT_RESOURCE_IPC_INDEX                0
 #define PLAT_RESOURCE_IPC_SIZE         0x1000
 #define PLAT_RESOURCE_GCR_OFFSET       0x1008
-#define PLAT_RESOURCE_GCR_SIZE         0x4
+#define PLAT_RESOURCE_GCR_SIZE         0x1000
 #define PLAT_RESOURCE_BIOS_DATA_INDEX  1
 #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
 #define PLAT_RESOURCE_TELEM_SSRAM_INDEX        3
 #define TCO_PMC_OFFSET                 0x8
 #define TCO_PMC_SIZE                   0x4
 
-static const int iTCO_version = 3;
-
 static struct intel_pmc_ipc_dev {
        struct device *dev;
        void __iomem *ipc_base;
@@ -115,6 +128,7 @@ static struct intel_pmc_ipc_dev {
        /* gcr */
        resource_size_t gcr_base;
        int gcr_size;
+       bool has_gcr_regs;
 
        /* punit */
        struct platform_device *punit_dev;
@@ -180,6 +194,11 @@ static inline u32 ipc_data_readl(u32 offset)
        return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
 }
 
+static inline u64 gcr_data_readq(u32 offset)
+{
+       return readq(ipcdev.ipc_base + offset);
+}
+
 static int intel_pmc_ipc_check_status(void)
 {
        int status;
@@ -389,6 +408,7 @@ static void ipc_pci_remove(struct pci_dev *pdev)
 static const struct pci_device_id ipc_pci_ids[] = {
        {PCI_VDEVICE(INTEL, 0x0a94), 0},
        {PCI_VDEVICE(INTEL, 0x1a94), 0},
+       {PCI_VDEVICE(INTEL, 0x5a94), 0},
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
@@ -712,7 +732,8 @@ static int ipc_plat_get_res(struct platform_device *pdev)
                dev_err(&pdev->dev, "Failed to get ipc resource\n");
                return -ENXIO;
        }
-       size = PLAT_RESOURCE_IPC_SIZE;
+       size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
+
        if (!request_mem_region(res->start, size, pdev->name)) {
                dev_err(&pdev->dev, "Failed to request ipc resource\n");
                return -EBUSY;
@@ -748,6 +769,28 @@ static int ipc_plat_get_res(struct platform_device *pdev)
        return 0;
 }
 
+/**
+ * intel_pmc_s0ix_counter_read() - Read S0ix residency.
+ * @data: Out param that contains current S0ix residency count.
+ *
+ * Return: an error code or 0 on success.
+ */
+int intel_pmc_s0ix_counter_read(u64 *data)
+{
+       u64 deep, shlw;
+
+       if (!ipcdev.has_gcr_regs)
+               return -EACCES;
+
+       deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET);
+       shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET);
+
+       *data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
+
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id ipc_acpi_ids[] = {
        { "INT34D2", 0},
@@ -797,6 +840,8 @@ static int ipc_plat_probe(struct platform_device *pdev)
                goto err_sys;
        }
 
+       ipcdev.has_gcr_regs = true;
+
        return 0;
 err_sys:
        free_irq(ipcdev.irq, &ipcdev);
@@ -808,8 +853,11 @@ err_device:
        iounmap(ipcdev.ipc_base);
        res = platform_get_resource(pdev, IORESOURCE_MEM,
                                    PLAT_RESOURCE_IPC_INDEX);
-       if (res)
-               release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE);
+       if (res) {
+               release_mem_region(res->start,
+                                  PLAT_RESOURCE_IPC_SIZE +
+                                  PLAT_RESOURCE_GCR_SIZE);
+       }
        return ret;
 }
 
@@ -825,8 +873,11 @@ static int ipc_plat_remove(struct platform_device *pdev)
        iounmap(ipcdev.ipc_base);
        res = platform_get_resource(pdev, IORESOURCE_MEM,
                                    PLAT_RESOURCE_IPC_INDEX);
-       if (res)
-               release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE);
+       if (res) {
+               release_mem_region(res->start,
+                                  PLAT_RESOURCE_IPC_SIZE +
+                                  PLAT_RESOURCE_GCR_SIZE);
+       }
        ipcdev.dev = NULL;
        return 0;
 }
diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c
new file mode 100644 (file)
index 0000000..4f60d8e
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver
+ * Copyright (c) 2017, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/topology.h>
+#include <linux/workqueue.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpufeature.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+#define MSR_OC_MAILBOX                 0x150
+#define MSR_OC_MAILBOX_CMD_OFFSET      32
+#define MSR_OC_MAILBOX_RSP_OFFSET      32
+#define MSR_OC_MAILBOX_BUSY_BIT                63
+#define OC_MAILBOX_FC_CONTROL_CMD      0x1C
+
+/*
+ * Typical latency to get mail box response is ~3us, It takes +3 us to
+ * process reading mailbox after issuing mailbox write on a Broadwell 3.4 GHz
+ * system. So for most of the time, the first mailbox read should have the
+ * response, but to avoid some boundary cases retry twice.
+ */
+#define OC_MAILBOX_RETRY_COUNT         2
+
+static int get_oc_core_priority(unsigned int cpu)
+{
+       u64 value, cmd = OC_MAILBOX_FC_CONTROL_CMD;
+       int ret, i;
+
+       /* Issue favored core read command */
+       value = cmd << MSR_OC_MAILBOX_CMD_OFFSET;
+       /* Set the busy bit to indicate OS is trying to issue command */
+       value |=  BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT);
+       ret = wrmsrl_safe(MSR_OC_MAILBOX, value);
+       if (ret) {
+               pr_debug("cpu %d OC mailbox write failed\n", cpu);
+               return ret;
+       }
+
+       for (i = 0; i < OC_MAILBOX_RETRY_COUNT; ++i) {
+               ret = rdmsrl_safe(MSR_OC_MAILBOX, &value);
+               if (ret) {
+                       pr_debug("cpu %d OC mailbox read failed\n", cpu);
+                       break;
+               }
+
+               if (value & BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT)) {
+                       pr_debug("cpu %d OC mailbox still processing\n", cpu);
+                       ret = -EBUSY;
+                       continue;
+               }
+
+               if ((value >> MSR_OC_MAILBOX_RSP_OFFSET) & 0xff) {
+                       pr_debug("cpu %d OC mailbox cmd failed\n", cpu);
+                       ret = -ENXIO;
+                       break;
+               }
+
+               ret = value & 0xff;
+               pr_debug("cpu %d max_ratio %d\n", cpu, ret);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * The work item is needed to avoid CPU hotplug locking issues. The function
+ * itmt_legacy_set_priority() is called from CPU online callback, so can't
+ * call sched_set_itmt_support() from there as this function will aquire
+ * hotplug locks in its path.
+ */
+static void itmt_legacy_work_fn(struct work_struct *work)
+{
+       sched_set_itmt_support();
+}
+
+static DECLARE_WORK(sched_itmt_work, itmt_legacy_work_fn);
+
+static int itmt_legacy_cpu_online(unsigned int cpu)
+{
+       static u32 max_highest_perf = 0, min_highest_perf = U32_MAX;
+       int priority;
+
+       priority = get_oc_core_priority(cpu);
+       if (priority < 0)
+               return 0;
+
+       sched_set_itmt_core_prio(priority, cpu);
+
+       /* Enable ITMT feature when a core with different priority is found */
+       if (max_highest_perf <= min_highest_perf) {
+               if (priority > max_highest_perf)
+                       max_highest_perf = priority;
+
+               if (priority < min_highest_perf)
+                       min_highest_perf = priority;
+
+               if (max_highest_perf > min_highest_perf)
+                       schedule_work(&sched_itmt_work);
+       }
+
+       return 0;
+}
+
+#define ICPU(model)     { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+
+static const struct x86_cpu_id itmt_legacy_cpu_ids[] = {
+       ICPU(INTEL_FAM6_BROADWELL_X),
+       {}
+};
+
+static int __init itmt_legacy_init(void)
+{
+       const struct x86_cpu_id *id;
+       int ret;
+
+       id = x86_match_cpu(itmt_legacy_cpu_ids);
+       if (!id)
+               return -ENODEV;
+
+       if (boot_cpu_has(X86_FEATURE_HWP))
+               return -ENODEV;
+
+       ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+                               "platform/x86/turbo_max_3:online",
+                               itmt_legacy_cpu_online, NULL);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+late_initcall(itmt_legacy_init)
index 25f15df5c2d7b3c37b82e099f301831c403caa7d..8f98c211b440c0a7658d54980daa5fc46a195da3 100644 (file)
 /* LPC bus IO offsets */
 #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR         0x2000
 #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR         0x2500
+#define MLXPLAT_CPLD_LPC_REG_AGGR_ADRR         0x253a
+#define MLXPLAT_CPLD_LPC_REG_PSU_ADRR          0x2558
+#define MLXPLAT_CPLD_LPC_REG_PWR_ADRR          0x2564
+#define MLXPLAT_CPLD_LPC_REG_FAN_ADRR          0x2588
 #define MLXPLAT_CPLD_LPC_IO_RANGE              0x100
 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF           0xdb
 #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF           0xda
                                  MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
                                  MLXPLAT_CPLD_LPC_PIO_OFFSET)
 
+/* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */
+#define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08
+#define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08
+#define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40
+#define MLXPLAT_CPLD_AGGR_MASK_DEF     (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \
+                                        MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
+#define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04
+#define MLXPLAT_CPLD_PSU_MASK          GENMASK(1, 0)
+#define MLXPLAT_CPLD_PWR_MASK          GENMASK(1, 0)
+#define MLXPLAT_CPLD_FAN_MASK          GENMASK(3, 0)
+
 /* Start channel numbers */
 #define MLXPLAT_CPLD_CH1                       2
 #define MLXPLAT_CPLD_CH2                       10
@@ -123,7 +138,7 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
 };
 
 /* Platform hotplug devices */
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = {
+static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = {
        {
                .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) },
                .bus = 10,
@@ -134,7 +149,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = {
        },
 };
 
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = {
+static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = {
        {
                .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) },
                .bus = 10,
@@ -145,7 +160,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = {
        },
 };
 
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = {
+static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = {
        {
                .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
                .bus = 11,
@@ -166,38 +181,38 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = {
 
 /* Platform hotplug default data */
 static
-struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = {
-       .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a),
-       .top_aggr_mask = 0x48,
-       .top_aggr_psu_mask = 0x08,
-       .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58),
-       .psu_mask = 0x03,
-       .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu),
-       .psu = mlxplat_mlxcpld_hotplug_psu,
-       .top_aggr_pwr_mask = 0x08,
-       .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64),
-       .pwr_mask = 0x03,
-       .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr),
-       .pwr = mlxplat_mlxcpld_hotplug_pwr,
-       .top_aggr_fan_mask = 0x40,
-       .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88),
-       .fan_mask = 0x0f,
-       .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan),
-       .fan = mlxplat_mlxcpld_hotplug_fan,
+struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_default_data = {
+       .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR,
+       .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
+       .top_aggr_psu_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
+       .psu_reg_offset = MLXPLAT_CPLD_LPC_REG_PSU_ADRR,
+       .psu_mask = MLXPLAT_CPLD_PSU_MASK,
+       .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
+       .psu = mlxplat_mlxcpld_psu,
+       .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
+       .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR,
+       .pwr_mask = MLXPLAT_CPLD_PWR_MASK,
+       .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
+       .pwr = mlxplat_mlxcpld_pwr,
+       .top_aggr_fan_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
+       .fan_reg_offset = MLXPLAT_CPLD_LPC_REG_FAN_ADRR,
+       .fan_mask = MLXPLAT_CPLD_FAN_MASK,
+       .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
+       .fan = mlxplat_mlxcpld_fan,
 };
 
 /* Platform hotplug MSN21xx system family data */
 static
-struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = {
-       .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a),
-       .top_aggr_mask = 0x04,
-       .top_aggr_pwr_mask = 0x04,
-       .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64),
-       .pwr_mask = 0x03,
-       .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr),
+struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
+       .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR,
+       .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX,
+       .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX,
+       .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR,
+       .pwr_mask = MLXPLAT_CPLD_PWR_MASK,
+       .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
 };
 
-static struct resource mlxplat_mlxcpld_hotplug_resources[] = {
+static struct resource mlxplat_mlxcpld_resources[] = {
        [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
 };
 
@@ -213,7 +228,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
                mlxplat_mux_data[i].n_values =
                                ARRAY_SIZE(mlxplat_default_channels[i]);
        }
-       mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data;
+       mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
 
        return 1;
 };
@@ -227,7 +242,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
                mlxplat_mux_data[i].n_values =
                                ARRAY_SIZE(mlxplat_msn21xx_channels);
        }
-       mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data;
+       mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
 
        return 1;
 };
@@ -314,9 +329,10 @@ static int __init mlxplat_init(void)
        }
 
        priv->pdev_hotplug = platform_device_register_resndata(
-                               &mlxplat_dev->dev, "mlxcpld-hotplug", -1,
-                               mlxplat_mlxcpld_hotplug_resources,
-                               ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources),
+                               &mlxplat_dev->dev, "mlxcpld-hotplug",
+                               PLATFORM_DEVID_NONE,
+                               mlxplat_mlxcpld_resources,
+                               ARRAY_SIZE(mlxplat_mlxcpld_resources),
                                mlxplat_hotplug, sizeof(*mlxplat_hotplug));
        if (IS_ERR(priv->pdev_hotplug)) {
                err = PTR_ERR(priv->pdev_hotplug);
diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c
new file mode 100644 (file)
index 0000000..02e11fd
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Silead touchscreen driver DMI based configuration code
+ *
+ * Copyright (c) 2017 Red Hat Inc.
+ *
+ * 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.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/string.h>
+
+struct silead_ts_dmi_data {
+       const char *acpi_name;
+       struct property_entry *properties;
+};
+
+static struct property_entry cube_iwork8_air_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1660),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 900),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       { }
+};
+
+static const struct silead_ts_dmi_data cube_iwork8_air_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = cube_iwork8_air_props,
+};
+
+static struct property_entry jumper_ezpad_mini3_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1700),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       { }
+};
+
+static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = jumper_ezpad_mini3_props,
+};
+
+static const struct dmi_system_id silead_ts_dmi_table[] = {
+       {
+               /* CUBE iwork8 Air */
+               .driver_data = (void *)&cube_iwork8_air_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+               },
+       },
+       {
+               /* Jumper EZpad mini3 */
+               .driver_data = (void *)&jumper_ezpad_mini3_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       /* jumperx.T87.KFBNEEA02 with the version-nr dropped */
+                       DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"),
+               },
+       },
+       { },
+};
+
+static void silead_ts_dmi_add_props(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       const struct dmi_system_id *dmi_id;
+       const struct silead_ts_dmi_data *ts_data;
+       int error;
+
+       dmi_id = dmi_first_match(silead_ts_dmi_table);
+       if (!dmi_id)
+               return;
+
+       ts_data = dmi_id->driver_data;
+       if (has_acpi_companion(dev) &&
+           !strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) {
+               error = device_add_properties(dev, ts_data->properties);
+               if (error)
+                       dev_err(dev, "failed to add properties: %d\n", error);
+       }
+}
+
+static int silead_ts_dmi_notifier_call(struct notifier_block *nb,
+                                      unsigned long action, void *data)
+{
+       struct device *dev = data;
+
+       switch (action) {
+       case BUS_NOTIFY_ADD_DEVICE:
+               silead_ts_dmi_add_props(dev);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static struct notifier_block silead_ts_dmi_notifier = {
+       .notifier_call = silead_ts_dmi_notifier_call,
+};
+
+static int __init silead_ts_dmi_init(void)
+{
+       int error;
+
+       error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier);
+       if (error)
+               pr_err("%s: failed to register i2c bus notifier: %d\n",
+                       __func__, error);
+
+       return error;
+}
+
+/*
+ * We are registering out notifier after i2c core is initialized and i2c bus
+ * itself is ready (which happens at postcore initcall level), but before
+ * ACPI starts enumerating devices (at subsys initcall level).
+ */
+arch_initcall(silead_ts_dmi_init);
index cacb43fb1df7fcbd5f643788fa960a4cdb6e4e36..1d18b32628ecbf501aff7193fc857b0126a56f58 100644 (file)
@@ -163,6 +163,7 @@ enum tpacpi_hkey_event_t {
        TP_HKEY_EV_HOTKEY_BASE          = 0x1001, /* first hotkey (FN+F1) */
        TP_HKEY_EV_BRGHT_UP             = 0x1010, /* Brightness up */
        TP_HKEY_EV_BRGHT_DOWN           = 0x1011, /* Brightness down */
+       TP_HKEY_EV_KBD_LIGHT            = 0x1012, /* Thinklight/kbd backlight */
        TP_HKEY_EV_VOL_UP               = 0x1015, /* Volume up or unmute */
        TP_HKEY_EV_VOL_DOWN             = 0x1016, /* Volume down or unmute */
        TP_HKEY_EV_VOL_MUTE             = 0x1017, /* Mixer output mute */
@@ -372,11 +373,9 @@ enum led_status_t {
        TPACPI_LED_BLINK,
 };
 
-/* Special LED class that can defer work */
+/* tpacpi LED class */
 struct tpacpi_led_classdev {
        struct led_classdev led_classdev;
-       struct work_struct work;
-       enum led_status_t new_state;
        int led;
 };
 
@@ -1959,7 +1958,7 @@ enum {    /* Positions of some of the keys in hotkey masks */
        TP_ACPI_HKEY_HIBERNATE_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF12,
        TP_ACPI_HKEY_BRGHTUP_MASK       = 1 << TP_ACPI_HOTKEYSCAN_FNHOME,
        TP_ACPI_HKEY_BRGHTDWN_MASK      = 1 << TP_ACPI_HOTKEYSCAN_FNEND,
-       TP_ACPI_HKEY_THNKLGHT_MASK      = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
+       TP_ACPI_HKEY_KBD_LIGHT_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
        TP_ACPI_HKEY_ZOOM_MASK          = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE,
        TP_ACPI_HKEY_VOLUP_MASK         = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP,
        TP_ACPI_HKEY_VOLDWN_MASK        = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
@@ -2344,7 +2343,7 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
                n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
                n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
        }
-       if (m & TP_ACPI_HKEY_THNKLGHT_MASK) {
+       if (m & TP_ACPI_HKEY_KBD_LIGHT_MASK) {
                d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
                n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
        }
@@ -5084,18 +5083,27 @@ static struct ibm_struct video_driver_data = {
  * Keyboard backlight subdriver
  */
 
+static enum led_brightness kbdlight_brightness;
+static DEFINE_MUTEX(kbdlight_mutex);
+
 static int kbdlight_set_level(int level)
 {
+       int ret = 0;
+
        if (!hkey_handle)
                return -ENXIO;
 
+       mutex_lock(&kbdlight_mutex);
+
        if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level))
-               return -EIO;
+               ret = -EIO;
+       else
+               kbdlight_brightness = level;
 
-       return 0;
-}
+       mutex_unlock(&kbdlight_mutex);
 
-static int kbdlight_set_level_and_update(int level);
+       return ret;
+}
 
 static int kbdlight_get_level(void)
 {
@@ -5158,24 +5166,10 @@ static bool kbdlight_is_supported(void)
        return status & BIT(9);
 }
 
-static void kbdlight_set_worker(struct work_struct *work)
-{
-       struct tpacpi_led_classdev *data =
-                       container_of(work, struct tpacpi_led_classdev, work);
-
-       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
-               kbdlight_set_level_and_update(data->new_state);
-}
-
-static void kbdlight_sysfs_set(struct led_classdev *led_cdev,
+static int kbdlight_sysfs_set(struct led_classdev *led_cdev,
                        enum led_brightness brightness)
 {
-       struct tpacpi_led_classdev *data =
-                       container_of(led_cdev,
-                                    struct tpacpi_led_classdev,
-                                    led_classdev);
-       data->new_state = brightness;
-       queue_work(tpacpi_wq, &data->work);
+       return kbdlight_set_level(brightness);
 }
 
 static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev)
@@ -5193,7 +5187,8 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = {
        .led_classdev = {
                .name           = "tpacpi::kbd_backlight",
                .max_brightness = 2,
-               .brightness_set = &kbdlight_sysfs_set,
+               .flags          = LED_BRIGHT_HW_CHANGED,
+               .brightness_set_blocking = &kbdlight_sysfs_set,
                .brightness_get = &kbdlight_sysfs_get,
        }
 };
@@ -5205,7 +5200,6 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
        vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n");
 
        TPACPI_ACPIHANDLE_INIT(hkey);
-       INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker);
 
        if (!kbdlight_is_supported()) {
                tp_features.kbdlight = 0;
@@ -5213,6 +5207,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
                return 1;
        }
 
+       kbdlight_brightness = kbdlight_sysfs_get(NULL);
        tp_features.kbdlight = 1;
 
        rc = led_classdev_register(&tpacpi_pdev->dev,
@@ -5222,6 +5217,8 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
                return rc;
        }
 
+       tpacpi_hotkey_driver_mask_set(hotkey_driver_mask |
+                                     TP_ACPI_HKEY_KBD_LIGHT_MASK);
        return 0;
 }
 
@@ -5229,7 +5226,6 @@ static void kbdlight_exit(void)
 {
        if (tp_features.kbdlight)
                led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
-       flush_workqueue(tpacpi_wq);
 }
 
 static int kbdlight_set_level_and_update(int level)
@@ -5358,25 +5354,11 @@ static int light_set_status(int status)
        return -ENXIO;
 }
 
-static void light_set_status_worker(struct work_struct *work)
-{
-       struct tpacpi_led_classdev *data =
-                       container_of(work, struct tpacpi_led_classdev, work);
-
-       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
-               light_set_status((data->new_state != TPACPI_LED_OFF));
-}
-
-static void light_sysfs_set(struct led_classdev *led_cdev,
+static int light_sysfs_set(struct led_classdev *led_cdev,
                        enum led_brightness brightness)
 {
-       struct tpacpi_led_classdev *data =
-               container_of(led_cdev,
-                            struct tpacpi_led_classdev,
-                            led_classdev);
-       data->new_state = (brightness != LED_OFF) ?
-                               TPACPI_LED_ON : TPACPI_LED_OFF;
-       queue_work(tpacpi_wq, &data->work);
+       return light_set_status((brightness != LED_OFF) ?
+                               TPACPI_LED_ON : TPACPI_LED_OFF);
 }
 
 static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
@@ -5387,7 +5369,7 @@ static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
 static struct tpacpi_led_classdev tpacpi_led_thinklight = {
        .led_classdev = {
                .name           = "tpacpi::thinklight",
-               .brightness_set = &light_sysfs_set,
+               .brightness_set_blocking = &light_sysfs_set,
                .brightness_get = &light_sysfs_get,
        }
 };
@@ -5403,7 +5385,6 @@ static int __init light_init(struct ibm_init_struct *iibm)
                TPACPI_ACPIHANDLE_INIT(lght);
        }
        TPACPI_ACPIHANDLE_INIT(cmos);
-       INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
 
        /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
        tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
@@ -5437,7 +5418,6 @@ static int __init light_init(struct ibm_init_struct *iibm)
 static void light_exit(void)
 {
        led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
-       flush_workqueue(tpacpi_wq);
 }
 
 static int light_read(struct seq_file *m)
@@ -5704,29 +5684,21 @@ static int led_set_status(const unsigned int led,
        return rc;
 }
 
-static void led_set_status_worker(struct work_struct *work)
-{
-       struct tpacpi_led_classdev *data =
-               container_of(work, struct tpacpi_led_classdev, work);
-
-       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
-               led_set_status(data->led, data->new_state);
-}
-
-static void led_sysfs_set(struct led_classdev *led_cdev,
+static int led_sysfs_set(struct led_classdev *led_cdev,
                        enum led_brightness brightness)
 {
        struct tpacpi_led_classdev *data = container_of(led_cdev,
                             struct tpacpi_led_classdev, led_classdev);
+       enum led_status_t new_state;
 
        if (brightness == LED_OFF)
-               data->new_state = TPACPI_LED_OFF;
+               new_state = TPACPI_LED_OFF;
        else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK)
-               data->new_state = TPACPI_LED_ON;
+               new_state = TPACPI_LED_ON;
        else
-               data->new_state = TPACPI_LED_BLINK;
+               new_state = TPACPI_LED_BLINK;
 
-       queue_work(tpacpi_wq, &data->work);
+       return led_set_status(data->led, new_state);
 }
 
 static int led_sysfs_blink_set(struct led_classdev *led_cdev,
@@ -5743,10 +5715,7 @@ static int led_sysfs_blink_set(struct led_classdev *led_cdev,
        } else if ((*delay_on != 500) || (*delay_off != 500))
                return -EINVAL;
 
-       data->new_state = TPACPI_LED_BLINK;
-       queue_work(tpacpi_wq, &data->work);
-
-       return 0;
+       return led_set_status(data->led, TPACPI_LED_BLINK);
 }
 
 static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
@@ -5775,7 +5744,6 @@ static void led_exit(void)
                        led_classdev_unregister(&tpacpi_leds[i].led_classdev);
        }
 
-       flush_workqueue(tpacpi_wq);
        kfree(tpacpi_leds);
 }
 
@@ -5789,7 +5757,7 @@ static int __init tpacpi_init_led(unsigned int led)
        if (!tpacpi_led_names[led])
                return 0;
 
-       tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
+       tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
        tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
        if (led_supported == TPACPI_LED_570)
                tpacpi_leds[led].led_classdev.brightness_get =
@@ -5797,8 +5765,6 @@ static int __init tpacpi_init_led(unsigned int led)
 
        tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
 
-       INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker);
-
        rc = led_classdev_register(&tpacpi_pdev->dev,
                                &tpacpi_leds[led].led_classdev);
        if (rc < 0)
@@ -9169,6 +9135,24 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
                        volume_alsa_notify_change();
                }
        }
+       if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) {
+               enum led_brightness brightness;
+
+               mutex_lock(&kbdlight_mutex);
+
+               /*
+                * Check the brightness actually changed, setting the brightness
+                * through kbdlight_set_level() also triggers this event.
+                */
+               brightness = kbdlight_sysfs_get(NULL);
+               if (kbdlight_brightness != brightness) {
+                       kbdlight_brightness = brightness;
+                       led_classdev_notify_brightness_hw_changed(
+                               &tpacpi_led_kbdlight.led_classdev, brightness);
+               }
+
+               mutex_unlock(&kbdlight_mutex);
+       }
 }
 
 static void hotkey_driver_event(const unsigned int scancode)