Merge tag 'platform-drivers-x86-v4.7-1' of git://git.infradead.org/users/dvhart/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 May 2016 20:56:02 +0000 (13:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 May 2016 20:56:02 +0000 (13:56 -0700)
Pull x86 platform driver updates from Darren Hart:
 "Mostly minor updates and cleanups.  One new power management
  controller driver for Intel Core SoCs.

  platform/x86:
   - Add PMC Driver for Intel Core SoC

  dell-rbtn:
   - Ignore ACPI notifications if device is suspended

  thinkpad_acpi:
   - save kbdlight state on suspend and restore it on resume

  intel_menlow:
   - reduce code duplication

  asus-wmi:
   - provide access to ALS control

  ideapad-laptop:
   - add a new WMI string for ESC key

  surfacepro3_button:
   - Add a warning when switching to tablet mode

  sony-laptop:
   - Avoid oops on module unload for older laptops

  intel_telemetry:
   - Constify telemetry_core_ops structures

  fujitsu-laptop:
   - Use IS_ENABLED() instead of checking for built-in or module

  asus-laptop:
   - correct error handling in sysfs_acpi_set
   - remove redundant initializers
   - correct error handling in asus_read_brightness()

  fujitsu-laptop:
   - Support radio LED"

* tag 'platform-drivers-x86-v4.7-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86:
  platform/x86: Add PMC Driver for Intel Core SoC
  dell-rbtn: Ignore ACPI notifications if device is suspended
  thinkpad_acpi: save kbdlight state on suspend and restore it on resume
  intel_menlow: reduce code duplication
  asus-wmi: provide access to ALS control
  ideapad-laptop: add a new WMI string for ESC key
  surfacepro3_button: Add a warning when switching to tablet mode
  sony-laptop: Avoid oops on module unload for older laptops
  intel_telemetry: Constify telemetry_core_ops structures
  fujitsu-laptop: Use IS_ENABLED() instead of checking for built-in or module
  asus-laptop: correct error handling in sysfs_acpi_set
  asus-laptop: remove redundant initializers
  asus-laptop: correct error handling in asus_read_brightness()
  fujitsu-laptop: Support radio LED

18 files changed:
MAINTAINERS
arch/x86/include/asm/intel_telemetry.h
arch/x86/include/asm/pmc_core.h [new file with mode: 0644]
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/dell-rbtn.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_menlow.c
drivers/platform/x86/intel_pmc_core.c [new file with mode: 0644]
drivers/platform/x86/intel_pmc_core.h [new file with mode: 0644]
drivers/platform/x86/intel_telemetry_core.c
drivers/platform/x86/intel_telemetry_pltdrv.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/surfacepro3_button.c
drivers/platform/x86/thinkpad_acpi.c

index 312cd77..c68eaeb 100644 (file)
@@ -6096,6 +6096,14 @@ S:       Maintained
 F:     arch/x86/include/asm/intel_telemetry.h
 F:     drivers/platform/x86/intel_telemetry*
 
+INTEL PMC CORE DRIVER
+M:     Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+M:     Vishwanath Somayaji <vishwanath.somayaji@intel.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     arch/x86/include/asm/pmc_core.h
+F:     drivers/platform/x86/intel_pmc_core*
+
 IOC3 ETHERNET DRIVER
 M:     Ralf Baechle <ralf@linux-mips.org>
 L:     linux-mips@linux-mips.org
index ed65fe7..85029b5 100644 (file)
@@ -99,7 +99,7 @@ struct telemetry_core_ops {
        int (*reset_events)(void);
 };
 
-int telemetry_set_pltdata(struct telemetry_core_ops *ops,
+int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
                          struct telemetry_plt_config *pltconfig);
 
 int telemetry_clear_pltdata(void);
diff --git a/arch/x86/include/asm/pmc_core.h b/arch/x86/include/asm/pmc_core.h
new file mode 100644 (file)
index 0000000..d4855f1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Intel Core SoC Power Management Controller Header File
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+ *          Vishwanath Somayaji <vishwanath.somayaji@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.
+ *
+ */
+
+#ifndef _ASM_PMC_CORE_H
+#define _ASM_PMC_CORE_H
+
+/* API to read SLP_S0_RESIDENCY counter */
+int intel_pmc_slp_s0_counter_read(u32 *data);
+
+#endif /* _ASM_PMC_CORE_H */
index ed2004b..c06bb85 100644 (file)
@@ -846,6 +846,18 @@ config INTEL_IMR
 
          If you are running on a Galileo/Quark say Y here.
 
+config INTEL_PMC_CORE
+       bool "Intel PMC Core driver"
+       depends on X86 && PCI
+       ---help---
+         The Intel Platform Controller Hub for Intel Core SoCs provides access
+         to Power Management Controller registers via a PCI interface. This
+         driver can utilize debugging capabilities and supported features as
+         exposed by the Power Management Controller.
+
+         Supported features:
+               - SLP_S0_RESIDENCY counter.
+
 config IBM_RTL
        tristate "Device driver to enable PRTL support"
        depends on X86 && PCI
index 448443c..9b11b40 100644 (file)
@@ -69,3 +69,4 @@ obj-$(CONFIG_INTEL_PUNIT_IPC)  += intel_punit_ipc.o
 obj-$(CONFIG_INTEL_TELEMETRY)  += intel_telemetry_core.o \
                                   intel_telemetry_pltdrv.o \
                                   intel_telemetry_debugfs.o
+obj-$(CONFIG_INTEL_PMC_CORE)    += intel_pmc_core.o
index f2b5d0a..15f1311 100644 (file)
@@ -771,12 +771,14 @@ static int asus_read_brightness(struct backlight_device *bd)
 {
        struct asus_laptop *asus = bl_get_data(bd);
        unsigned long long value;
-       acpi_status rv = AE_OK;
+       acpi_status rv;
 
        rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET,
                                   NULL, &value);
-       if (ACPI_FAILURE(rv))
+       if (ACPI_FAILURE(rv)) {
                pr_warn("Error reading brightness\n");
+               return 0;
+       }
 
        return value;
 }
@@ -865,7 +867,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
        int len = 0;
        unsigned long long temp;
        char buf[16];           /* enough for all info */
-       acpi_status rv = AE_OK;
+       acpi_status rv;
 
        /*
         * We use the easy way, we don't care of off and count,
@@ -946,11 +948,10 @@ static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
                              const char *method)
 {
        int rv, value;
-       int out = 0;
 
        rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               out = value ? 1 : 0;
+       if (rv <= 0)
+               return rv;
 
        if (write_acpi_int(asus->handle, method, value))
                return -ENODEV;
@@ -1265,7 +1266,7 @@ static DEVICE_ATTR_RO(ls_value);
 static int asus_gps_status(struct asus_laptop *asus)
 {
        unsigned long long status;
-       acpi_status rv = AE_OK;
+       acpi_status rv;
 
        rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS,
                                   NULL, &status);
index a96630d..a26dca3 100644 (file)
@@ -114,6 +114,7 @@ MODULE_LICENSE("GPL");
 #define ASUS_WMI_DEVID_LED6            0x00020016
 
 /* Backlight and Brightness */
+#define ASUS_WMI_DEVID_ALS_ENABLE      0x00050001 /* Ambient Light Sensor */
 #define ASUS_WMI_DEVID_BACKLIGHT       0x00050011
 #define ASUS_WMI_DEVID_BRIGHTNESS      0x00050012
 #define ASUS_WMI_DEVID_KBD_BACKLIGHT   0x00050021
@@ -1730,6 +1731,7 @@ ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
 ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
 ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
 ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
+ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);
 
 static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t count)
@@ -1756,6 +1758,7 @@ static struct attribute *platform_attributes[] = {
        &dev_attr_cardr.attr,
        &dev_attr_touchpad.attr,
        &dev_attr_lid_resume.attr,
+       &dev_attr_als_enable.attr,
        NULL
 };
 
@@ -1776,6 +1779,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
                devid = ASUS_WMI_DEVID_TOUCHPAD;
        else if (attr == &dev_attr_lid_resume.attr)
                devid = ASUS_WMI_DEVID_LID_RESUME;
+       else if (attr == &dev_attr_als_enable.attr)
+               devid = ASUS_WMI_DEVID_ALS_ENABLE;
 
        if (devid != -1)
                ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
index b51a200..dcd9f40 100644 (file)
@@ -28,6 +28,7 @@ struct rbtn_data {
        enum rbtn_type type;
        struct rfkill *rfkill;
        struct input_dev *input_dev;
+       bool suspended;
 };
 
 
@@ -235,9 +236,55 @@ static const struct acpi_device_id rbtn_ids[] = {
        { "", 0 },
 };
 
+#ifdef CONFIG_PM_SLEEP
+static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
+{
+       struct rbtn_data *rbtn_data = context;
+
+       rbtn_data->suspended = false;
+}
+
+static int rbtn_suspend(struct device *dev)
+{
+       struct acpi_device *device = to_acpi_device(dev);
+       struct rbtn_data *rbtn_data = acpi_driver_data(device);
+
+       rbtn_data->suspended = true;
+
+       return 0;
+}
+
+static int rbtn_resume(struct device *dev)
+{
+       struct acpi_device *device = to_acpi_device(dev);
+       struct rbtn_data *rbtn_data = acpi_driver_data(device);
+       acpi_status status;
+
+       /*
+        * Upon resume, some BIOSes send an ACPI notification thet triggers
+        * an unwanted input event. In order to ignore it, we use a flag
+        * that we set at suspend and clear once we have received the extra
+        * ACPI notification. Since ACPI notifications are delivered
+        * asynchronously to drivers, we clear the flag from the workqueue
+        * used to deliver the notifications. This should be enough
+        * to have the flag cleared only after we received the extra
+        * notification, if any.
+        */
+       status = acpi_os_execute(OSL_NOTIFY_HANDLER,
+                        rbtn_clear_suspended_flag, rbtn_data);
+       if (ACPI_FAILURE(status))
+               rbtn_clear_suspended_flag(rbtn_data);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
+
 static struct acpi_driver rbtn_driver = {
        .name = "dell-rbtn",
        .ids = rbtn_ids,
+       .drv.pm = &rbtn_pm_ops,
        .ops = {
                .add = rbtn_add,
                .remove = rbtn_remove,
@@ -399,6 +446,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event)
 {
        struct rbtn_data *rbtn_data = device->driver_data;
 
+       /*
+        * Some BIOSes send a notification at resume.
+        * Ignore it to prevent unwanted input events.
+        */
+       if (rbtn_data->suspended) {
+               dev_dbg(&device->dev, "ACPI notification ignored\n");
+               return;
+       }
+
        if (event != 0x80) {
                dev_info(&device->dev, "Received unknown event (0x%x)\n",
                         event);
index ffc84cc..ce41bc3 100644 (file)
@@ -69,7 +69,7 @@
 #include <linux/kfifo.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
 #include <linux/leds.h>
 #endif
 #include <acpi/video.h>
 /* FUNC interface - responses */
 #define UNSUPPORTED_CMD 0x80000000
 
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
 /* FUNC interface - LED control */
 #define FUNC_LED_OFF   0x1
 #define FUNC_LED_ON    0x30001
 #define KEYBOARD_LAMPS 0x100
 #define LOGOLAMP_POWERON 0x2000
 #define LOGOLAMP_ALWAYS  0x4000
+#define RADIO_LED_ON   0x20
 #endif
 
 /* Hotkey details */
@@ -174,13 +175,14 @@ struct fujitsu_hotkey_t {
        int rfkill_state;
        int logolamp_registered;
        int kblamps_registered;
+       int radio_led_registered;
 };
 
 static struct fujitsu_hotkey_t *fujitsu_hotkey;
 
 static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event);
 
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
 static enum led_brightness logolamp_get(struct led_classdev *cdev);
 static void logolamp_set(struct led_classdev *cdev,
                               enum led_brightness brightness);
@@ -200,6 +202,16 @@ static struct led_classdev kblamps_led = {
  .brightness_get = kblamps_get,
  .brightness_set = kblamps_set
 };
+
+static enum led_brightness radio_led_get(struct led_classdev *cdev);
+static void radio_led_set(struct led_classdev *cdev,
+                              enum led_brightness brightness);
+
+static struct led_classdev radio_led = {
+ .name = "fujitsu::radio_led",
+ .brightness_get = radio_led_get,
+ .brightness_set = radio_led_set
+};
 #endif
 
 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
@@ -249,7 +261,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
        return value;
 }
 
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
 /* LED class callbacks */
 
 static void logolamp_set(struct led_classdev *cdev,
@@ -275,6 +287,15 @@ static void kblamps_set(struct led_classdev *cdev,
                call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
 }
 
+static void radio_led_set(struct led_classdev *cdev,
+                               enum led_brightness brightness)
+{
+       if (brightness >= LED_FULL)
+               call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, RADIO_LED_ON);
+       else
+               call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
+}
+
 static enum led_brightness logolamp_get(struct led_classdev *cdev)
 {
        enum led_brightness brightness = LED_OFF;
@@ -299,6 +320,16 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev)
 
        return brightness;
 }
+
+static enum led_brightness radio_led_get(struct led_classdev *cdev)
+{
+       enum led_brightness brightness = LED_OFF;
+
+       if (call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0) & RADIO_LED_ON)
+               brightness = LED_FULL;
+
+       return brightness;
+}
 #endif
 
 /* Hardware access for LCD brightness control */
@@ -872,7 +903,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
        /* Suspect this is a keymap of the application panel, print it */
        pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
 
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
        if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
                result = led_classdev_register(&fujitsu->pf_device->dev,
                                                &logolamp_led);
@@ -895,6 +926,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
                               result);
                }
        }
+
+       /*
+        * BTNI bit 24 seems to indicate the presence of a radio toggle
+        * button in place of a slide switch, and all such machines appear
+        * to also have an RF LED.  Therefore use bit 24 as an indicator
+        * that an RF LED is present.
+        */
+       if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
+               result = led_classdev_register(&fujitsu->pf_device->dev,
+                                               &radio_led);
+               if (result == 0) {
+                       fujitsu_hotkey->radio_led_registered = 1;
+               } else {
+                       pr_err("Could not register LED handler for radio LED, error %i\n",
+                              result);
+               }
+       }
 #endif
 
        return result;
@@ -915,12 +963,15 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
        struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device);
        struct input_dev *input = fujitsu_hotkey->input;
 
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
        if (fujitsu_hotkey->logolamp_registered)
                led_classdev_unregister(&logolamp_led);
 
        if (fujitsu_hotkey->kblamps_registered)
                led_classdev_unregister(&kblamps_led);
+
+       if (fujitsu_hotkey->radio_led_registered)
+               led_classdev_unregister(&radio_led);
 #endif
 
        input_unregister_device(input);
index be3bc2f..4a23fbc 100644 (file)
 #define CFG_CAMERA_BIT (19)
 
 #if IS_ENABLED(CONFIG_ACPI_WMI)
-static const char ideapad_wmi_fnesc_event[] = "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6";
+static const char *const ideapad_wmi_fnesc_events[] = {
+       "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
+       "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
+};
 #endif
 
 enum {
@@ -93,6 +96,7 @@ struct ideapad_private {
        struct dentry *debug;
        unsigned long cfg;
        bool has_hw_rfkill_switch;
+       const char *fnesc_guid;
 };
 
 static bool no_bt_rfkill;
@@ -989,8 +993,16 @@ static int ideapad_acpi_add(struct platform_device *pdev)
                ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
        if (ret)
                goto notification_failed;
+
 #if IS_ENABLED(CONFIG_ACPI_WMI)
-       ret = wmi_install_notify_handler(ideapad_wmi_fnesc_event, ideapad_wmi_notify, priv);
+       for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
+               ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
+                                                ideapad_wmi_notify, priv);
+               if (ret == AE_OK) {
+                       priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
+                       break;
+               }
+       }
        if (ret != AE_OK && ret != AE_NOT_EXIST)
                goto notification_failed_wmi;
 #endif
@@ -1020,7 +1032,8 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
        int i;
 
 #if IS_ENABLED(CONFIG_ACPI_WMI)
-       wmi_remove_notify_handler(ideapad_wmi_fnesc_event);
+       if (priv->fnesc_guid)
+               wmi_remove_notify_handler(priv->fnesc_guid);
 #endif
        acpi_remove_notify_handler(priv->adev->handle,
                ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
index 0a919d8..cbe0102 100644 (file)
@@ -306,33 +306,32 @@ static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
 #define to_intel_menlow_attr(_attr)    \
        container_of(_attr, struct intel_menlow_attribute, attr)
 
-static ssize_t aux0_show(struct device *dev,
-                        struct device_attribute *dev_attr, char *buf)
+static ssize_t aux_show(struct device *dev, struct device_attribute *dev_attr,
+                       char *buf, int idx)
 {
        struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
        unsigned long long value;
        int result;
 
-       result = sensor_get_auxtrip(attr->handle, 0, &value);
+       result = sensor_get_auxtrip(attr->handle, idx, &value);
 
        return result ? result : sprintf(buf, "%lu", DECI_KELVIN_TO_CELSIUS(value));
 }
 
-static ssize_t aux1_show(struct device *dev,
+static ssize_t aux0_show(struct device *dev,
                         struct device_attribute *dev_attr, char *buf)
 {
-       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
-       unsigned long long value;
-       int result;
-
-       result = sensor_get_auxtrip(attr->handle, 1, &value);
+       return aux_show(dev, dev_attr, buf, 0);
+}
 
-       return result ? result : sprintf(buf, "%lu", DECI_KELVIN_TO_CELSIUS(value));
+static ssize_t aux1_show(struct device *dev,
+                        struct device_attribute *dev_attr, char *buf)
+{
+       return aux_show(dev, dev_attr, buf, 1);
 }
 
-static ssize_t aux0_store(struct device *dev,
-                         struct device_attribute *dev_attr,
-                         const char *buf, size_t count)
+static ssize_t aux_store(struct device *dev, struct device_attribute *dev_attr,
+                        const char *buf, size_t count, int idx)
 {
        struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
        int value;
@@ -345,27 +344,23 @@ static ssize_t aux0_store(struct device *dev,
        if (value < 0)
                return -EINVAL;
 
-       result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_DECI_KELVIN(value));
+       result = sensor_set_auxtrip(attr->handle, idx, 
+                                   CELSIUS_TO_DECI_KELVIN(value));
        return result ? result : count;
 }
 
-static ssize_t aux1_store(struct device *dev,
+static ssize_t aux0_store(struct device *dev,
                          struct device_attribute *dev_attr,
                          const char *buf, size_t count)
 {
-       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
-       int value;
-       int result;
-
-       /*Sanity check; should be a positive integer */
-       if (!sscanf(buf, "%d", &value))
-               return -EINVAL;
-
-       if (value < 0)
-               return -EINVAL;
+       return aux_store(dev, dev_attr, buf, count, 0);
+}
 
-       result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_DECI_KELVIN(value));
-       return result ? result : count;
+static ssize_t aux1_store(struct device *dev,
+                         struct device_attribute *dev_attr,
+                         const char *buf, size_t count)
+{
+       return aux_store(dev, dev_attr, buf, count, 1);
 }
 
 /* BIOS can enable/disable the thermal user application in dabney platform */
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
new file mode 100644 (file)
index 0000000..2776bec
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Intel Core SoC Power Management Controller Driver
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+ *          Vishwanath Somayaji <vishwanath.somayaji@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.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#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/pmc_core.h>
+
+#include "intel_pmc_core.h"
+
+static struct pmc_dev pmc;
+
+static const struct pci_device_id pmc_pci_ids[] = {
+       { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), (kernel_ulong_t)NULL },
+       { 0, },
+};
+
+static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
+{
+       return readl(pmcdev->regbase + reg_offset);
+}
+
+static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
+{
+       return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
+}
+
+/**
+ * intel_pmc_slp_s0_counter_read() - Read SLP_S0 residency.
+ * @data: Out param that contains current SLP_S0 count.
+ *
+ * This API currently supports Intel Skylake SoC and Sunrise
+ * Point Platform Controller Hub. Future platform support
+ * should be added for platforms that support low power modes
+ * beyond Package C10 state.
+ *
+ * SLP_S0_RESIDENCY counter counts in 100 us granularity per
+ * step hence function populates the multiplied value in out
+ * parameter @data.
+ *
+ * Return: an error code or 0 on success.
+ */
+int intel_pmc_slp_s0_counter_read(u32 *data)
+{
+       struct pmc_dev *pmcdev = &pmc;
+       u32 value;
+
+       if (!pmcdev->has_slp_s0_res)
+               return -EACCES;
+
+       value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
+       *data = pmc_core_adjust_slp_s0_step(value);
+
+       return 0;
+}
+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)
+{
+       struct pmc_dev *pmcdev = s->private;
+       u32 counter_val;
+
+       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));
+
+       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,
+};
+
+static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
+{
+       debugfs_remove_recursive(pmcdev->dbgfs_dir);
+}
+
+static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
+{
+       struct dentry *dir, *file;
+
+       dir = debugfs_create_dir("pmc_core", NULL);
+       if (!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);
+
+       if (!file) {
+               pmc_core_dbgfs_unregister(pmcdev);
+               return -ENODEV;
+       }
+
+       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, 0x4e, X86_FEATURE_MWAIT,
+               (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */
+       { X86_VENDOR_INTEL, 6, 0x5e, X86_FEATURE_MWAIT,
+               (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */
+       {}
+};
+
+static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       struct device *ptr_dev = &dev->dev;
+       struct pmc_dev *pmcdev = &pmc;
+       const struct x86_cpu_id *cpu_id;
+       int err;
+
+       cpu_id = x86_match_cpu(intel_pmc_core_ids);
+       if (!cpu_id) {
+               dev_dbg(&dev->dev, "PMC Core: cpuid mismatch.\n");
+               return -EINVAL;
+       }
+
+       err = pcim_enable_device(dev);
+       if (err < 0) {
+               dev_dbg(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n");
+               return err;
+       }
+
+       err = pci_read_config_dword(dev,
+                                   SPT_PMC_BASE_ADDR_OFFSET,
+                                   &pmcdev->base_addr);
+       if (err < 0) {
+               dev_dbg(&dev->dev, "PMC Core: failed to read PCI config space.\n");
+               return err;
+       }
+       dev_dbg(&dev->dev, "PMC Core: PWRMBASE is %#x\n", pmcdev->base_addr);
+
+       pmcdev->regbase = devm_ioremap_nocache(ptr_dev,
+                                             pmcdev->base_addr,
+                                             SPT_PMC_MMIO_REG_LEN);
+       if (!pmcdev->regbase) {
+               dev_dbg(&dev->dev, "PMC Core: ioremap failed.\n");
+               return -ENOMEM;
+       }
+
+       err = pmc_core_dbgfs_register(pmcdev);
+       if (err < 0) {
+               dev_err(&dev->dev, "PMC Core: debugfs register failed.\n");
+               return err;
+       }
+
+       pmc.has_slp_s0_res = true;
+       return 0;
+}
+
+static struct pci_driver intel_pmc_core_driver = {
+       .name = "intel_pmc_core",
+       .id_table = pmc_pci_ids,
+       .probe = pmc_core_probe,
+};
+
+builtin_pci_driver(intel_pmc_core_driver);
diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h
new file mode 100644 (file)
index 0000000..a9dadaf
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Intel Core SoC Power Management Controller Header File
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+ *          Vishwanath Somayaji <vishwanath.somayaji@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.
+ *
+ */
+
+#ifndef PMC_CORE_H
+#define PMC_CORE_H
+
+/* 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
+#define SPT_PMC_SLP_S0_RES_COUNTER_STEP                0x64
+
+/**
+ * struct pmc_dev - pmc device structure
+ * @base_addr:         comtains pmc base address
+ * @regbase:           pointer to io-remapped memory location
+ * @dbgfs_dir:         path to debug fs interface
+ * @feature_available: flag to indicate whether
+ *                     the feature is available
+ *                     on a particular platform or not.
+ *
+ * pmc_dev contains info about power management controller device.
+ */
+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;
+};
+
+#endif /* PMC_CORE_H */
index a695a43..0d4c380 100644 (file)
@@ -25,7 +25,7 @@
 
 struct telemetry_core_config {
        struct telemetry_plt_config *plt_config;
-       struct telemetry_core_ops *telem_ops;
+       const struct telemetry_core_ops *telem_ops;
 };
 
 static struct telemetry_core_config telm_core_conf;
@@ -95,7 +95,7 @@ static int telemetry_def_reset_events(void)
        return 0;
 }
 
-static struct telemetry_core_ops telm_defpltops = {
+static const struct telemetry_core_ops telm_defpltops = {
        .set_sampling_period = telemetry_def_set_sampling_period,
        .get_sampling_period = telemetry_def_get_sampling_period,
        .get_trace_verbosity = telemetry_def_get_trace_verbosity,
@@ -332,7 +332,7 @@ EXPORT_SYMBOL_GPL(telemetry_set_trace_verbosity);
  *
  * Return: 0 success, < 0 for failure
  */
-int telemetry_set_pltdata(struct telemetry_core_ops *ops,
+int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
                          struct telemetry_plt_config *pltconfig)
 {
        if (ops)
index 781bd10..09c84a2 100644 (file)
@@ -1081,7 +1081,7 @@ out:
        return ret;
 }
 
-static struct telemetry_core_ops telm_pltops = {
+static const struct telemetry_core_ops telm_pltops = {
        .get_trace_verbosity = telemetry_plt_get_trace_verbosity,
        .set_trace_verbosity = telemetry_plt_set_trace_verbosity,
        .set_sampling_period = telemetry_plt_set_sampling_period,
index e9caa34..1dba359 100644 (file)
@@ -1446,6 +1446,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
 {
        unsigned int i, result, bitmask, handle;
 
+       if (!handles)
+               return;
+
        /* get enabled events and disable them */
        sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask);
        sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result);
index 700e0fa..6505c97 100644 (file)
@@ -24,6 +24,8 @@
 #define SURFACE_BUTTON_OBJ_NAME                "VGBI"
 #define SURFACE_BUTTON_DEVICE_NAME     "Surface Pro 3/4 Buttons"
 
+#define SURFACE_BUTTON_NOTIFY_TABLET_MODE      0xc8
+
 #define SURFACE_BUTTON_NOTIFY_PRESS_POWER      0xc6
 #define SURFACE_BUTTON_NOTIFY_RELEASE_POWER    0xc7
 
@@ -33,7 +35,7 @@
 #define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP  0xc0
 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP        0xc1
 
-#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN        0xc2
+#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN                0xc2
 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN      0xc3
 
 ACPI_MODULE_NAME("surface pro 3 button");
@@ -105,9 +107,12 @@ static void surface_button_notify(struct acpi_device *device, u32 event)
        case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN:
                key_code = KEY_VOLUMEDOWN;
                break;
+       case SURFACE_BUTTON_NOTIFY_TABLET_MODE:
+               dev_warn_once(&device->dev, "Tablet mode is not supported\n");
+               break;
        default:
                dev_info_ratelimited(&device->dev,
-                                 "Unsupported event [0x%x]\n", event);
+                                    "Unsupported event [0x%x]\n", event);
                break;
        }
        input = button->input;
index 9255ff3..c3bfa1f 100644 (file)
@@ -5001,6 +5001,8 @@ static int kbdlight_set_level(int level)
        return 0;
 }
 
+static int kbdlight_set_level_and_update(int level);
+
 static int kbdlight_get_level(void)
 {
        int status = 0;
@@ -5068,7 +5070,7 @@ static void kbdlight_set_worker(struct work_struct *work)
                        container_of(work, struct tpacpi_led_classdev, work);
 
        if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
-               kbdlight_set_level(data->new_state);
+               kbdlight_set_level_and_update(data->new_state);
 }
 
 static void kbdlight_sysfs_set(struct led_classdev *led_cdev,
@@ -5099,7 +5101,6 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = {
                .max_brightness = 2,
                .brightness_set = &kbdlight_sysfs_set,
                .brightness_get = &kbdlight_sysfs_get,
-               .flags          = LED_CORE_SUSPENDRESUME,
        }
 };
 
@@ -5137,6 +5138,20 @@ static void kbdlight_exit(void)
        flush_workqueue(tpacpi_wq);
 }
 
+static int kbdlight_set_level_and_update(int level)
+{
+       int ret;
+       struct led_classdev *led_cdev;
+
+       ret = kbdlight_set_level(level);
+       led_cdev = &tpacpi_led_kbdlight.led_classdev;
+
+       if (ret == 0 && !(led_cdev->flags & LED_SUSPENDED))
+               led_cdev->brightness = level;
+
+       return ret;
+}
+
 static int kbdlight_read(struct seq_file *m)
 {
        int level;
@@ -5177,13 +5192,35 @@ static int kbdlight_write(char *buf)
        if (level == -1)
                return -EINVAL;
 
-       return kbdlight_set_level(level);
+       return kbdlight_set_level_and_update(level);
+}
+
+static void kbdlight_suspend(void)
+{
+       struct led_classdev *led_cdev;
+
+       if (!tp_features.kbdlight)
+               return;
+
+       led_cdev = &tpacpi_led_kbdlight.led_classdev;
+       led_update_brightness(led_cdev);
+       led_classdev_suspend(led_cdev);
+}
+
+static void kbdlight_resume(void)
+{
+       if (!tp_features.kbdlight)
+               return;
+
+       led_classdev_resume(&tpacpi_led_kbdlight.led_classdev);
 }
 
 static struct ibm_struct kbdlight_driver_data = {
        .name = "kbdlight",
        .read = kbdlight_read,
        .write = kbdlight_write,
+       .suspend = kbdlight_suspend,
+       .resume = kbdlight_resume,
        .exit = kbdlight_exit,
 };