Merge tag 'platform-drivers-x86-v4.16-1' of git://git.infradead.org/linux-platform...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 6 Feb 2018 23:30:52 +0000 (15:30 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 6 Feb 2018 23:30:52 +0000 (15:30 -0800)
Pull x86 platform-driver updates from Darren Hart:
 "New model support added for Dell, Ideapad, Acer, Asus, Thinkpad, and
  GPD laptops. Improvements to the common intel-vbtn driver, including
  tablet mode, rotate, and front button support. Intel CPU support added
  for Cannonlake and platform support for Dollar Cove power button.

  Overhaul of the mellanox platform driver, creating a new
  platform/mellanox directory for the newly multi-architecture regmap
  interface.

  Significant Intel PMC update with CannonLake support, Coffeelake
  update, CPUID enumeration, module support, new read64 API, refactoring
  and cleanups.

  Revert the apple-gmux iGP IO lock, addressing reported issues with
  non-binary drivers, leaving Nvidia binary driver users to comment out
  conflicting code.

  Miscellaneous fixes and cleanups"

* tag 'platform-drivers-x86-v4.16-1' of git://git.infradead.org/linux-platform-drivers-x86: (81 commits)
  platform/x86: mlx-platform: Fix an ERR_PTR vs NULL issue
  platform/x86: intel_pmc_core: Special case for Coffeelake
  platform/x86: intel_pmc_core: Add CannonLake PCH support
  x86/cpu: Add Cannonlake to Intel family
  platform/x86: intel_pmc_core: Read base address from LPIT
  ACPI / LPIT: Export lpit_read_residency_count_address()
  platform/x86: intel-vbtn: Replace License by SDPX identifier
  platform/x86: intel-vbtn: Remove redundant inclusions
  platform/x86: intel-vbtn: Support tablet mode switch
  platform/x86: dell-laptop: Allocate buffer on heap rather than globally
  platform/x86: intel_pmc_core: Remove unused header file
  platform/x86: mlx-platform: Add hotplug device unregister to error path
  platform/x86: mlx-platform: fix module aliases
  platform/mellanox: mlxreg-hotplug: Add check for negative adapter number
  platform/x86: mlx-platform: Add IO access verification callbacks
  platform/x86: mlx-platform: Document pdev_hotplug field
  platform/x86: mlx-platform: Allow compilation for 32 bit arch
  platform/mellanox: mlxreg-hotplug: Enable building for ARM
  platform/mellanox: mlxreg-hotplug: Modify to use a regmap interface
  platform/mellanox: Group create/destroy with attribute functions
  ...

38 files changed:
Documentation/ABI/testing/sysfs-driver-samsung-laptop
MAINTAINERS
arch/x86/include/asm/intel-family.h
arch/x86/include/asm/intel_pmc_ipc.h
arch/x86/include/asm/pmc_core.h [deleted file]
drivers/acpi/acpi_lpit.c
drivers/platform/Kconfig
drivers/platform/Makefile
drivers/platform/mellanox/Kconfig [new file with mode: 0644]
drivers/platform/mellanox/Makefile [new file with mode: 0644]
drivers/platform/mellanox/mlxreg-hotplug.c [new file with mode: 0644]
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acer-wireless.c [new file with mode: 0644]
drivers/platform/x86/alienware-wmi.c
drivers/platform/x86/apple-gmux.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-smbios.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/gpd-pocket-fan.c [new file with mode: 0644]
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel-vbtn.c
drivers/platform/x86/intel_chtdc_ti_pwrbtn.c [new file with mode: 0644]
drivers/platform/x86/intel_int0002_vgpio.c
drivers/platform/x86/intel_pmc_core.c
drivers/platform/x86/intel_pmc_core.h
drivers/platform/x86/intel_pmc_ipc.c
drivers/platform/x86/intel_telemetry_debugfs.c
drivers/platform/x86/mlx-platform.c
drivers/platform/x86/mlxcpld-hotplug.c [deleted file]
drivers/platform/x86/pmc_atom.c
drivers/platform/x86/silead_dmi.c
drivers/platform/x86/thinkpad_acpi.c
include/linux/platform_data/mlxcpld-hotplug.h [deleted file]
include/linux/platform_data/mlxreg.h [new file with mode: 0644]
include/uapi/linux/input-event-codes.h

index 63c1ad0212fc8624432f1691240443b9015b5d3f..34d3a3359cf4587fb182eaf146d6b1648d6a7188 100644 (file)
@@ -3,7 +3,7 @@ Date:           January 1, 2010
 KernelVersion: 2.6.33
 Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:   Some Samsung laptops have different "performance levels"
-               that are can be modified by a function key, and by this
+               that can be modified by a function key, and by this
                sysfs file.  These values don't always make a whole lot
                of sense, but some users like to modify them to keep
                their fans quiet at all costs.  Reading from this file
index 836f6c7bbf2d50c67bb56e5a1a882f01d9d98a41..a2d0aeabf3cc0a832ec7c6599a7995d579abd173 100644 (file)
@@ -6001,6 +6001,12 @@ L:       linux-input@vger.kernel.org
 S:     Maintained
 F:     drivers/input/touchscreen/goodix.c
 
+GPD POCKET FAN DRIVER
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/gpd-pocket-fan.c
+
 GPIO ACPI SUPPORT
 M:     Mika Westerberg <mika.westerberg@linux.intel.com>
 M:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
@@ -8939,12 +8945,13 @@ W:      http://www.mellanox.com
 Q:     http://patchwork.ozlabs.org/project/netdev/list/
 F:     drivers/net/ethernet/mellanox/mlxfw/
 
-MELLANOX MLX CPLD HOTPLUG DRIVER
+MELLANOX HARDWARE PLATFORM SUPPORT
+M:     Andy Shevchenko <andy@infradead.org>
+M:     Darren Hart <dvhart@infradead.org>
 M:     Vadim Pasternak <vadimp@mellanox.com>
 L:     platform-driver-x86@vger.kernel.org
 S:     Supported
-F:     drivers/platform/x86/mlxcpld-hotplug.c
-F:     include/linux/platform_data/mlxcpld-hotplug.h
+F:     drivers/platform/mellanox/
 
 MELLANOX MLX4 core VPI driver
 M:     Tariq Toukan <tariqt@mellanox.com>
@@ -15134,7 +15141,7 @@ X86 PLATFORM DRIVERS
 M:     Darren Hart <dvhart@infradead.org>
 M:     Andy Shevchenko <andy@infradead.org>
 L:     platform-driver-x86@vger.kernel.org
-T:     git git://git.infradead.org/users/dvhart/linux-platform-drivers-x86.git
+T:     git git://git.infradead.org/linux-platform-drivers-x86.git
 S:     Maintained
 F:     drivers/platform/x86/
 F:     drivers/platform/olpc/
index 35a6bc4da8adfc760d75b407875ba22d045c16c3..cf090e584202259e5480af9e3248aa03f6f79873 100644 (file)
  *
  * Things ending in "2" are usually because we have no better
  * name for them.  There's no processor called "SILVERMONT2".
+ *
+ * While adding a new CPUID for a new microarchitecture, add a new
+ * group to keep logically sorted out in chronological order. Within
+ * that group keep the CPUID for the variants sorted by model number.
  */
 
 #define INTEL_FAM6_CORE_YONAH          0x0E
@@ -49,6 +53,8 @@
 #define INTEL_FAM6_KABYLAKE_MOBILE     0x8E
 #define INTEL_FAM6_KABYLAKE_DESKTOP    0x9E
 
+#define INTEL_FAM6_CANNONLAKE_MOBILE   0x66
+
 /* "Small Core" Processors (Atom) */
 
 #define INTEL_FAM6_ATOM_PINEVIEW       0x1C
index 528ed4be43939b6afce28e7ea3429315ca033a73..9e7adcdbe031dfc6a30f2e4f47cd125d54418e74 100644 (file)
@@ -38,6 +38,7 @@ 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);
 int intel_pmc_gcr_read(u32 offset, u32 *data);
+int intel_pmc_gcr_read64(u32 offset, u64 *data);
 int intel_pmc_gcr_write(u32 offset, u32 data);
 int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
 
@@ -70,6 +71,11 @@ static inline int intel_pmc_gcr_read(u32 offset, u32 *data)
        return -EINVAL;
 }
 
+static inline int intel_pmc_gcr_read64(u32 offset, u64 *data)
+{
+       return -EINVAL;
+}
+
 static inline int intel_pmc_gcr_write(u32 offset, u32 data)
 {
        return -EINVAL;
diff --git a/arch/x86/include/asm/pmc_core.h b/arch/x86/include/asm/pmc_core.h
deleted file mode 100644 (file)
index d4855f1..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 e94e478dd18bb801f2a119779e08d0fd6fbda7db..cf4fc0161164158e3f83e65e173a6389ab0eaec2 100644 (file)
@@ -100,6 +100,7 @@ int lpit_read_residency_count_address(u64 *address)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(lpit_read_residency_count_address);
 
 static void lpit_update_residency(struct lpit_residency_info *info,
                                 struct acpi_lpit_native *lpit_native)
index c11db8bceea13e49cfd70704def1fb4e62b9313f..d4c2e424a700cad439acfbf82ab2228a6755c9cd 100644 (file)
@@ -8,3 +8,5 @@ endif
 source "drivers/platform/goldfish/Kconfig"
 
 source "drivers/platform/chrome/Kconfig"
+
+source "drivers/platform/mellanox/Kconfig"
index d3a6630266a07b10629953d8d06cbf7ba920923b..4b2ce58bcd9c62d29810222391073579d51de74b 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_X86)              += x86/
+obj-$(CONFIG_MELLANOX_PLATFORM)        += mellanox/
 obj-$(CONFIG_MIPS)             += mips/
 obj-$(CONFIG_OLPC)             += olpc/
 obj-$(CONFIG_GOLDFISH)         += goldfish/
diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
new file mode 100644 (file)
index 0000000..591bccd
--- /dev/null
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Platform support for Mellanox hardware
+#
+
+menuconfig MELLANOX_PLATFORM
+       bool "Platform support for Mellanox hardware"
+       depends on X86 || ARM || COMPILE_TEST
+       ---help---
+         Say Y here to get to see options for platform support for
+         Mellanox systems. This option alone does not add any kernel code.
+
+         If you say N, all options in this submenu will be skipped and disabled.
+
+if MELLANOX_PLATFORM
+
+config MLXREG_HOTPLUG
+       tristate "Mellanox platform hotplug driver support"
+       depends on REGMAP
+       depends on HWMON
+       depends on I2C
+       ---help---
+         This driver handles hot-plug events for the power suppliers, power
+         cables and fans on the wide range Mellanox IB and Ethernet systems.
+
+endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
new file mode 100644 (file)
index 0000000..7c8385e
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for linux/drivers/platform/mellanox
+# Mellanox Platform-Specific Drivers
+#
+obj-$(CONFIG_MLXREG_HOTPLUG)   += mlxreg-hotplug.o
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
new file mode 100644 (file)
index 0000000..0dfa1ca
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016-2018 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+
+/* Offset of event and mask registers from status register. */
+#define MLXREG_HOTPLUG_EVENT_OFF       1
+#define MLXREG_HOTPLUG_MASK_OFF                2
+#define MLXREG_HOTPLUG_AGGR_MASK_OFF   1
+
+/* ASIC health parameters. */
+#define MLXREG_HOTPLUG_HEALTH_MASK     0x02
+#define MLXREG_HOTPLUG_RST_CNTR                3
+
+#define MLXREG_HOTPLUG_ATTRS_MAX       24
+
+/**
+ * struct mlxreg_hotplug_priv_data - platform private data:
+ * @irq: platform device interrupt number;
+ * @pdev: platform device;
+ * @plat: platform data;
+ * @dwork: delayed work template;
+ * @lock: spin lock;
+ * @hwmon: hwmon device;
+ * @mlxreg_hotplug_attr: sysfs attributes array;
+ * @mlxreg_hotplug_dev_attr: sysfs sensor device attribute array;
+ * @group: sysfs attribute group;
+ * @groups: list of sysfs attribute group for hwmon registration;
+ * @cell: location of top aggregation interrupt register;
+ * @mask: top aggregation interrupt common mask;
+ * @aggr_cache: last value of aggregation register status;
+ */
+struct mlxreg_hotplug_priv_data {
+       int irq;
+       struct device *dev;
+       struct platform_device *pdev;
+       struct mlxreg_hotplug_platform_data *plat;
+       struct regmap *regmap;
+       struct delayed_work dwork_irq;
+       struct delayed_work dwork;
+       spinlock_t lock; /* sync with interrupt */
+       struct device *hwmon;
+       struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_MAX + 1];
+       struct sensor_device_attribute_2
+                       mlxreg_hotplug_dev_attr[MLXREG_HOTPLUG_ATTRS_MAX];
+       struct attribute_group group;
+       const struct attribute_group *groups[2];
+       u32 cell;
+       u32 mask;
+       u32 aggr_cache;
+       bool after_probe;
+};
+
+static int mlxreg_hotplug_device_create(struct device *dev,
+                                       struct mlxreg_core_data *data)
+{
+       /*
+        * Return if adapter number is negative. It could be in case hotplug
+        * event is not associated with hotplug device.
+        */
+       if (data->hpdev.nr < 0)
+               return 0;
+
+       data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr);
+       if (!data->hpdev.adapter) {
+               dev_err(dev, "Failed to get adapter for bus %d\n",
+                       data->hpdev.nr);
+               return -EFAULT;
+       }
+
+       data->hpdev.client = i2c_new_device(data->hpdev.adapter,
+                                           data->hpdev.brdinfo);
+       if (!data->hpdev.client) {
+               dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
+                       data->hpdev.brdinfo->type, data->hpdev.nr,
+                       data->hpdev.brdinfo->addr);
+
+               i2c_put_adapter(data->hpdev.adapter);
+               data->hpdev.adapter = NULL;
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static void mlxreg_hotplug_device_destroy(struct mlxreg_core_data *data)
+{
+       if (data->hpdev.client) {
+               i2c_unregister_device(data->hpdev.client);
+               data->hpdev.client = NULL;
+       }
+
+       if (data->hpdev.adapter) {
+               i2c_put_adapter(data->hpdev.adapter);
+               data->hpdev.adapter = NULL;
+       }
+}
+
+static ssize_t mlxreg_hotplug_attr_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(dev);
+       struct mlxreg_core_hotplug_platform_data *pdata;
+       int index = to_sensor_dev_attr_2(attr)->index;
+       int nr = to_sensor_dev_attr_2(attr)->nr;
+       struct mlxreg_core_item *item;
+       struct mlxreg_core_data *data;
+       u32 regval;
+       int ret;
+
+       pdata = dev_get_platdata(&priv->pdev->dev);
+       item = pdata->items + nr;
+       data = item->data + index;
+
+       ret = regmap_read(priv->regmap, data->reg, &regval);
+       if (ret)
+               return ret;
+
+       if (item->health) {
+               regval &= data->mask;
+       } else {
+               /* Bit = 0 : functional if item->inversed is true. */
+               if (item->inversed)
+                       regval = !(regval & data->mask);
+               else
+                       regval = !!(regval & data->mask);
+       }
+
+       return sprintf(buf, "%u\n", regval);
+}
+
+#define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
+#define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i]
+
+static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
+{
+       struct mlxreg_core_hotplug_platform_data *pdata;
+       struct mlxreg_core_item *item;
+       struct mlxreg_core_data *data;
+       int num_attrs = 0, id = 0, i, j;
+
+       pdata = dev_get_platdata(&priv->pdev->dev);
+       item = pdata->items;
+
+       /* Go over all kinds of items - psu, pwr, fan. */
+       for (i = 0; i < pdata->counter; i++, item++) {
+               num_attrs += item->count;
+               data = item->data;
+               /* Go over all units within the item. */
+               for (j = 0; j < item->count; j++, data++, id++) {
+                       PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr;
+                       PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev,
+                                                            GFP_KERNEL,
+                                                            data->label);
+
+                       if (!PRIV_ATTR(id)->name) {
+                               dev_err(priv->dev, "Memory allocation failed for attr %d.\n",
+                                       id);
+                               return -ENOMEM;
+                       }
+
+                       PRIV_DEV_ATTR(id).dev_attr.attr.name =
+                                                       PRIV_ATTR(id)->name;
+                       PRIV_DEV_ATTR(id).dev_attr.attr.mode = 0444;
+                       PRIV_DEV_ATTR(id).dev_attr.show =
+                                               mlxreg_hotplug_attr_show;
+                       PRIV_DEV_ATTR(id).nr = i;
+                       PRIV_DEV_ATTR(id).index = j;
+                       sysfs_attr_init(&PRIV_DEV_ATTR(id).dev_attr.attr);
+               }
+       }
+
+       priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs *
+                                        sizeof(struct attribute *),
+                                        GFP_KERNEL);
+       if (!priv->group.attrs)
+               return -ENOMEM;
+
+       priv->group.attrs = priv->mlxreg_hotplug_attr;
+       priv->groups[0] = &priv->group;
+       priv->groups[1] = NULL;
+
+       return 0;
+}
+
+static void
+mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
+                          struct mlxreg_core_item *item)
+{
+       struct mlxreg_core_data *data;
+       u32 asserted, regval, bit;
+       int ret;
+
+       /*
+        * Validate if item related to received signal type is valid.
+        * It should never happen, excepted the situation when some
+        * piece of hardware is broken. In such situation just produce
+        * error message and return. Caller must continue to handle the
+        * signals from other devices if any.
+        */
+       if (unlikely(!item)) {
+               dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n",
+                       item->reg, item->mask);
+
+               return;
+       }
+
+       /* Mask event. */
+       ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
+                          0);
+       if (ret)
+               goto out;
+
+       /* Read status. */
+       ret = regmap_read(priv->regmap, item->reg, &regval);
+       if (ret)
+               goto out;
+
+       /* Set asserted bits and save last status. */
+       regval &= item->mask;
+       asserted = item->cache ^ regval;
+       item->cache = regval;
+
+       for_each_set_bit(bit, (unsigned long *)&asserted, 8) {
+               data = item->data + bit;
+               if (regval & BIT(bit)) {
+                       if (item->inversed)
+                               mlxreg_hotplug_device_destroy(data);
+                       else
+                               mlxreg_hotplug_device_create(priv->dev, data);
+               } else {
+                       if (item->inversed)
+                               mlxreg_hotplug_device_create(priv->dev, data);
+                       else
+                               mlxreg_hotplug_device_destroy(data);
+               }
+       }
+
+       /* Acknowledge event. */
+       ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_EVENT_OFF,
+                          0);
+       if (ret)
+               goto out;
+
+       /* Unmask event. */
+       ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
+                          item->mask);
+
+ out:
+       if (ret)
+               dev_err(priv->dev, "Failed to complete workqueue.\n");
+}
+
+static void
+mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
+                                 struct mlxreg_core_item *item)
+{
+       struct mlxreg_core_data *data = item->data;
+       u32 regval;
+       int i, ret;
+
+       for (i = 0; i < item->count; i++, data++) {
+               /* Mask event. */
+               ret = regmap_write(priv->regmap, data->reg +
+                                  MLXREG_HOTPLUG_MASK_OFF, 0);
+               if (ret)
+                       goto out;
+
+               /* Read status. */
+               ret = regmap_read(priv->regmap, data->reg, &regval);
+               if (ret)
+                       goto out;
+
+               regval &= data->mask;
+               item->cache = regval;
+               if (regval == MLXREG_HOTPLUG_HEALTH_MASK) {
+                       if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) ||
+                           !priv->after_probe) {
+                               mlxreg_hotplug_device_create(priv->dev, data);
+                               data->attached = true;
+                       }
+               } else {
+                       if (data->attached) {
+                               mlxreg_hotplug_device_destroy(data);
+                               data->attached = false;
+                               data->health_cntr = 0;
+                       }
+               }
+
+               /* Acknowledge event. */
+               ret = regmap_write(priv->regmap, data->reg +
+                                  MLXREG_HOTPLUG_EVENT_OFF, 0);
+               if (ret)
+                       goto out;
+
+               /* Unmask event. */
+               ret = regmap_write(priv->regmap, data->reg +
+                                  MLXREG_HOTPLUG_MASK_OFF, data->mask);
+               if (ret)
+                       goto out;
+       }
+
+ out:
+       if (ret)
+               dev_err(priv->dev, "Failed to complete workqueue.\n");
+}
+
+/*
+ * mlxreg_hotplug_work_handler - performs traversing of device interrupt
+ * registers according to the below hierarchy schema:
+ *
+ *                             Aggregation registers (status/mask)
+ * PSU registers:              *---*
+ * *-----------------*         |   |
+ * |status/event/mask|----->    | * |
+ * *-----------------*         |   |
+ * Power registers:            |   |
+ * *-----------------*         |   |
+ * |status/event/mask|----->    | * |
+ * *-----------------*         |   |
+ * FAN registers:              |   |--> CPU
+ * *-----------------*         |   |
+ * |status/event/mask|----->    | * |
+ * *-----------------*         |   |
+ * ASIC registers:             |   |
+ * *-----------------*         |   |
+ * |status/event/mask|----->    | * |
+ * *-----------------*         |   |
+ *                             *---*
+ *
+ * In case some system changed are detected: FAN in/out, PSU in/out, power
+ * cable attached/detached, ASIC health good/bad, relevant device is created
+ * or destroyed.
+ */
+static void mlxreg_hotplug_work_handler(struct work_struct *work)
+{
+       struct mlxreg_core_hotplug_platform_data *pdata;
+       struct mlxreg_hotplug_priv_data *priv;
+       struct mlxreg_core_item *item;
+       u32 regval, aggr_asserted;
+       unsigned long flags;
+       int i, ret;
+
+       priv = container_of(work, struct mlxreg_hotplug_priv_data,
+                           dwork_irq.work);
+       pdata = dev_get_platdata(&priv->pdev->dev);
+       item = pdata->items;
+
+       /* Mask aggregation event. */
+       ret = regmap_write(priv->regmap, pdata->cell +
+                          MLXREG_HOTPLUG_AGGR_MASK_OFF, 0);
+       if (ret < 0)
+               goto out;
+
+       /* Read aggregation status. */
+       ret = regmap_read(priv->regmap, pdata->cell, &regval);
+       if (ret)
+               goto out;
+
+       regval &= pdata->mask;
+       aggr_asserted = priv->aggr_cache ^ regval;
+       priv->aggr_cache = regval;
+
+       /* Handle topology and health configuration changes. */
+       for (i = 0; i < pdata->counter; i++, item++) {
+               if (aggr_asserted & item->aggr_mask) {
+                       if (item->health)
+                               mlxreg_hotplug_health_work_helper(priv, item);
+                       else
+                               mlxreg_hotplug_work_helper(priv, item);
+               }
+       }
+
+       if (aggr_asserted) {
+               spin_lock_irqsave(&priv->lock, flags);
+
+               /*
+                * It is possible, that some signals have been inserted, while
+                * interrupt has been masked by mlxreg_hotplug_work_handler.
+                * In this case such signals will be missed. In order to handle
+                * these signals delayed work is canceled and work task
+                * re-scheduled for immediate execution. It allows to handle
+                * missed signals, if any. In other case work handler just
+                * validates that no new signals have been received during
+                * masking.
+                */
+               cancel_delayed_work(&priv->dwork_irq);
+               schedule_delayed_work(&priv->dwork_irq, 0);
+
+               spin_unlock_irqrestore(&priv->lock, flags);
+
+               return;
+       }
+
+       /* Unmask aggregation event (no need acknowledge). */
+       ret = regmap_write(priv->regmap, pdata->cell +
+                          MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
+
+ out:
+       if (ret)
+               dev_err(priv->dev, "Failed to complete workqueue.\n");
+}
+
+static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv)
+{
+       struct mlxreg_core_hotplug_platform_data *pdata;
+       struct mlxreg_core_item *item;
+       int i, ret;
+
+       pdata = dev_get_platdata(&priv->pdev->dev);
+       item = pdata->items;
+
+       for (i = 0; i < pdata->counter; i++, item++) {
+               /* Clear group presense event. */
+               ret = regmap_write(priv->regmap, item->reg +
+                                  MLXREG_HOTPLUG_EVENT_OFF, 0);
+               if (ret)
+                       goto out;
+
+               /* Set group initial status as mask and unmask group event. */
+               if (item->inversed) {
+                       item->cache = item->mask;
+                       ret = regmap_write(priv->regmap, item->reg +
+                                          MLXREG_HOTPLUG_MASK_OFF,
+                                          item->mask);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+       /* Keep aggregation initial status as zero and unmask events. */
+       ret = regmap_write(priv->regmap, pdata->cell +
+                          MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
+       if (ret)
+               goto out;
+
+       /* Keep low aggregation initial status as zero and unmask events. */
+       if (pdata->cell_low) {
+               ret = regmap_write(priv->regmap, pdata->cell_low +
+                                  MLXREG_HOTPLUG_AGGR_MASK_OFF,
+                                  pdata->mask_low);
+               if (ret)
+                       goto out;
+       }
+
+       /* Invoke work handler for initializing hot plug devices setting. */
+       mlxreg_hotplug_work_handler(&priv->dwork_irq.work);
+
+ out:
+       if (ret)
+               dev_err(priv->dev, "Failed to set interrupts.\n");
+       enable_irq(priv->irq);
+       return ret;
+}
+
+static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv)
+{
+       struct mlxreg_core_hotplug_platform_data *pdata;
+       struct mlxreg_core_item *item;
+       struct mlxreg_core_data *data;
+       int count, i, j;
+
+       pdata = dev_get_platdata(&priv->pdev->dev);
+       item = pdata->items;
+       disable_irq(priv->irq);
+       cancel_delayed_work_sync(&priv->dwork_irq);
+
+       /* Mask low aggregation event, if defined. */
+       if (pdata->cell_low)
+               regmap_write(priv->regmap, pdata->cell_low +
+                            MLXREG_HOTPLUG_AGGR_MASK_OFF, 0);
+
+       /* Mask aggregation event. */
+       regmap_write(priv->regmap, pdata->cell + MLXREG_HOTPLUG_AGGR_MASK_OFF,
+                    0);
+
+       /* Clear topology configurations. */
+       for (i = 0; i < pdata->counter; i++, item++) {
+               data = item->data;
+               /* Mask group presense event. */
+               regmap_write(priv->regmap, data->reg + MLXREG_HOTPLUG_MASK_OFF,
+                            0);
+               /* Clear group presense event. */
+               regmap_write(priv->regmap, data->reg +
+                            MLXREG_HOTPLUG_EVENT_OFF, 0);
+
+               /* Remove all the attached devices in group. */
+               count = item->count;
+               for (j = 0; j < count; j++, data++)
+                       mlxreg_hotplug_device_destroy(data);
+       }
+}
+
+static irqreturn_t mlxreg_hotplug_irq_handler(int irq, void *dev)
+{
+       struct mlxreg_hotplug_priv_data *priv;
+
+       priv = (struct mlxreg_hotplug_priv_data *)dev;
+
+       /* Schedule work task for immediate execution.*/
+       schedule_delayed_work(&priv->dwork_irq, 0);
+
+       return IRQ_HANDLED;
+}
+
+static int mlxreg_hotplug_probe(struct platform_device *pdev)
+{
+       struct mlxreg_core_hotplug_platform_data *pdata;
+       struct mlxreg_hotplug_priv_data *priv;
+       int err;
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (!pdata) {
+               dev_err(&pdev->dev, "Failed to get platform data.\n");
+               return -EINVAL;
+       }
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       if (pdata->irq) {
+               priv->irq = pdata->irq;
+       } else {
+               priv->irq = platform_get_irq(pdev, 0);
+               if (priv->irq < 0) {
+                       dev_err(&pdev->dev, "Failed to get platform irq: %d\n",
+                               priv->irq);
+                       return priv->irq;
+               }
+       }
+
+       priv->regmap = pdata->regmap;
+       priv->dev = pdev->dev.parent;
+       priv->pdev = pdev;
+
+       err = devm_request_irq(&pdev->dev, priv->irq,
+                              mlxreg_hotplug_irq_handler, IRQF_TRIGGER_FALLING
+                              | IRQF_SHARED, "mlxreg-hotplug", priv);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to request irq: %d\n", err);
+               return err;
+       }
+
+       disable_irq(priv->irq);
+       spin_lock_init(&priv->lock);
+       INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler);
+       /* Perform initial interrupts setup. */
+       mlxreg_hotplug_set_irq(priv);
+
+       priv->after_probe = true;
+       dev_set_drvdata(&pdev->dev, priv);
+
+       err = mlxreg_hotplug_attr_init(priv);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to allocate attributes: %d\n",
+                       err);
+               return err;
+       }
+
+       priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
+                                       "mlxreg_hotplug", priv, priv->groups);
+       if (IS_ERR(priv->hwmon)) {
+               dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
+                       PTR_ERR(priv->hwmon));
+               return PTR_ERR(priv->hwmon);
+       }
+
+       return 0;
+}
+
+static int mlxreg_hotplug_remove(struct platform_device *pdev)
+{
+       struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(&pdev->dev);
+
+       /* Clean interrupts setup. */
+       mlxreg_hotplug_unset_irq(priv);
+
+       return 0;
+}
+
+static struct platform_driver mlxreg_hotplug_driver = {
+       .driver = {
+               .name = "mlxreg-hotplug",
+       },
+       .probe = mlxreg_hotplug_probe,
+       .remove = mlxreg_hotplug_remove,
+};
+
+module_platform_driver(mlxreg_hotplug_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox regmap hotplug platform driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:mlxreg-hotplug");
index 2c745e8ccad6c190882725d8864d4f96ae2e68be..9a8f96465cdc3c04783f6296dcc778128c231df5 100644 (file)
@@ -36,6 +36,20 @@ config ACER_WMI
          If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
          here.
 
+config ACER_WIRELESS
+        tristate "Acer Wireless Radio Control Driver"
+        depends on ACPI
+        depends on INPUT
+        ---help---
+          The Acer Wireless Radio Control handles the airplane mode hotkey
+          present on new Acer laptops.
+
+          Say Y or M here if you have an Acer notebook with an airplane mode
+          hotkey.
+
+          If you choose to compile this driver as a module the module will be
+          called acer-wireless.
+
 config ACERHDF
        tristate "Acer Aspire One temperature and fan driver"
        depends on ACPI && THERMAL
@@ -244,6 +258,18 @@ config AMILO_RFKILL
          This is a driver for enabling wifi on some Fujitsu-Siemens Amilo
          laptops.
 
+config GPD_POCKET_FAN
+       tristate "GPD Pocket Fan Controller support"
+       depends on ACPI
+       depends on THERMAL
+       ---help---
+         Driver for the GPD Pocket vendor specific FAN02501 ACPI device
+         which controls the fan speed on the GPD Pocket.
+
+         Without this driver the fan on the Pocket will stay off independent
+         of the CPU temperature. Say Y or M if the kernel may be used on a
+         GPD pocket.
+
 config TC1100_WMI
        tristate "HP Compaq TC1100 Tablet WMI Extras"
        depends on !X86_64
@@ -812,9 +838,8 @@ config TOSHIBA_WMI
 
 config ACPI_CMPC
        tristate "CMPC Laptop Extras"
-       depends on ACPI
+       depends on ACPI && INPUT
        depends on RFKILL || RFKILL=n
-       select INPUT
        select BACKLIGHT_CLASS_DEVICE
        help
          Support for Intel Classmate PC ACPI devices, including some
@@ -949,7 +974,7 @@ config INTEL_IMR
          If you are running on a Galileo/Quark say Y here.
 
 config INTEL_PMC_CORE
-       bool "Intel PMC Core driver"
+       tristate "Intel PMC Core driver"
        depends on PCI
        ---help---
          The Intel Platform Controller Hub for Intel Core SoCs provides access
@@ -958,7 +983,10 @@ config INTEL_PMC_CORE
          exposed by the Power Management Controller.
 
          Supported features:
-               - SLP_S0_RESIDENCY counter.
+               - SLP_S0_RESIDENCY counter
+               - PCH IP Power Gating status
+               - LTR Ignore
+               - MPHY/PLL gating status (Sunrisepoint PCH only)
 
 config IBM_RTL
        tristate "Device driver to enable PRTL support"
@@ -1131,7 +1159,6 @@ config INTEL_TELEMETRY
 
 config MLX_PLATFORM
        tristate "Mellanox Technologies platform support"
-       depends on X86_64
        ---help---
          This option enables system support for the Mellanox Technologies
          platform. The Mellanox systems provide data center networking
@@ -1141,14 +1168,6 @@ config MLX_PLATFORM
 
          If you have a Mellanox system, say Y or M here.
 
-config MLX_CPLD_PLATFORM
-       tristate "Mellanox platform hotplug driver support"
-       select HWMON
-       select I2C
-       ---help---
-         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
@@ -1170,6 +1189,17 @@ config SILEAD_DMI
          with the OS-image for the device. This option supplies the missing
          information. Enable this for x86 tablets with Silead touchscreens.
 
+config INTEL_CHTDC_TI_PWRBTN
+       tristate "Intel Cherry Trail Dollar Cove TI power button driver"
+       depends on INTEL_SOC_PMIC_CHTDC_TI
+       depends on INPUT
+       ---help---
+         This option adds a power button driver driver for Dollar Cove TI
+         PMIC on Intel Cherry Trail devices.
+
+         To compile this driver as a module, choose M here: the module
+         will be called intel_chtdc_ti_pwrbtn.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
index c32b34a724679435d134615c42a9c3f3855cf463..c388608ad2a3942ef2c949c87b18f76a33f0c66f 100644 (file)
@@ -23,11 +23,13 @@ obj-$(CONFIG_DELL_WMI_LED)  += dell-wmi-led.o
 obj-$(CONFIG_DELL_SMO8800)     += dell-smo8800.o
 obj-$(CONFIG_DELL_RBTN)                += dell-rbtn.o
 obj-$(CONFIG_ACER_WMI)         += acer-wmi.o
+obj-$(CONFIG_ACER_WIRELESS)    += acer-wireless.o
 obj-$(CONFIG_ACERHDF)          += acerhdf.o
 obj-$(CONFIG_HP_ACCEL)         += hp_accel.o
 obj-$(CONFIG_HP_WIRELESS)      += hp-wireless.o
 obj-$(CONFIG_HP_WMI)           += hp-wmi.o
 obj-$(CONFIG_AMILO_RFKILL)     += amilo-rfkill.o
+obj-$(CONFIG_GPD_POCKET_FAN)   += gpd-pocket-fan.o
 obj-$(CONFIG_TC1100_WMI)       += tc1100-wmi.o
 obj-$(CONFIG_SONY_LAPTOP)      += sony-laptop.o
 obj-$(CONFIG_IDEAPAD_LAPTOP)   += ideapad-laptop.o
@@ -86,5 +88,5 @@ obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
 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
+obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN)    += intel_chtdc_ti_pwrbtn.o
diff --git a/drivers/platform/x86/acer-wireless.c b/drivers/platform/x86/acer-wireless.c
new file mode 100644 (file)
index 0000000..8580379
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Acer Wireless Radio Control Driver
+ *
+ * Copyright (C) 2017 Endless Mobile, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci_ids.h>
+#include <linux/types.h>
+
+static const struct acpi_device_id acer_wireless_acpi_ids[] = {
+       {"10251229", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, acer_wireless_acpi_ids);
+
+static void acer_wireless_notify(struct acpi_device *adev, u32 event)
+{
+       struct input_dev *idev = acpi_driver_data(adev);
+
+       dev_dbg(&adev->dev, "event=%#x\n", event);
+       if (event != 0x80) {
+               dev_notice(&adev->dev, "Unknown SMKB event: %#x\n", event);
+               return;
+       }
+       input_report_key(idev, KEY_RFKILL, 1);
+       input_report_key(idev, KEY_RFKILL, 0);
+       input_sync(idev);
+}
+
+static int acer_wireless_add(struct acpi_device *adev)
+{
+       struct input_dev *idev;
+
+       idev = devm_input_allocate_device(&adev->dev);
+       if (!idev)
+               return -ENOMEM;
+
+       adev->driver_data = idev;
+       idev->name = "Acer Wireless Radio Control";
+       idev->phys = "acer-wireless/input0";
+       idev->id.bustype = BUS_HOST;
+       idev->id.vendor = PCI_VENDOR_ID_AI;
+       idev->id.product = 0x1229;
+       set_bit(EV_KEY, idev->evbit);
+       set_bit(KEY_RFKILL, idev->keybit);
+
+       return input_register_device(idev);
+}
+
+static struct acpi_driver acer_wireless_driver = {
+       .name = "Acer Wireless Radio Control Driver",
+       .class = "hotkey",
+       .ids = acer_wireless_acpi_ids,
+       .ops = {
+               .add = acer_wireless_add,
+               .notify = acer_wireless_notify,
+       },
+};
+module_acpi_driver(acer_wireless_driver);
+
+MODULE_DESCRIPTION("Acer Wireless Radio Control Driver");
+MODULE_AUTHOR("Chris Chiu <chiu@gmail.com>");
+MODULE_LICENSE("GPL v2");
index 4eb8e1a472b23821280fae17f91df35a8ce8737e..9d7dbd925065c2f9e5321431d93e97d71b7b20ce 100644 (file)
@@ -68,6 +68,14 @@ struct quirk_entry {
 
 static struct quirk_entry *quirks;
 
+
+static struct quirk_entry quirk_inspiron5675 = {
+       .num_zones = 2,
+       .hdmi_mux = 0,
+       .amplifier = 0,
+       .deepslp = 0,
+};
+
 static struct quirk_entry quirk_unknown = {
        .num_zones = 2,
        .hdmi_mux = 0,
@@ -171,6 +179,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
                     },
         .driver_data = &quirk_asm201,
         },
+        {
+        .callback = dmi_matched,
+        .ident = "Dell Inc. Inspiron 5675",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+                    },
+        .driver_data = &quirk_inspiron5675,
+        },
        {}
 };
 
index 623d322447a2192fcfba9e0041ddbe76080b30bb..7c4eb86c851ed9568cb882d32a9a5fdd3a0d13a0 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/delay.h>
 #include <linux/pci.h>
 #include <linux/vga_switcheroo.h>
-#include <linux/vgaarb.h>
 #include <acpi/video.h>
 #include <asm/io.h>
 
@@ -54,7 +53,6 @@ struct apple_gmux_data {
        bool indexed;
        struct mutex index_lock;
 
-       struct pci_dev *pdev;
        struct backlight_device *bdev;
 
        /* switcheroo data */
@@ -599,23 +597,6 @@ static int gmux_resume(struct device *dev)
        return 0;
 }
 
-static struct pci_dev *gmux_get_io_pdev(void)
-{
-       struct pci_dev *pdev = NULL;
-
-       while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) {
-               u16 cmd;
-
-               pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-               if (!(cmd & PCI_COMMAND_IO))
-                       continue;
-
-               return pdev;
-       }
-
-       return NULL;
-}
-
 static int is_thunderbolt(struct device *dev, void *data)
 {
        return to_pci_dev(dev)->is_thunderbolt;
@@ -631,7 +612,6 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
        int ret = -ENXIO;
        acpi_status status;
        unsigned long long gpe;
-       struct pci_dev *pdev = NULL;
 
        if (apple_gmux_data)
                return -EBUSY;
@@ -682,7 +662,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
                        ver_minor = (version >> 16) & 0xff;
                        ver_release = (version >> 8) & 0xff;
                } else {
-                       pr_info("gmux device not present or IO disabled\n");
+                       pr_info("gmux device not present\n");
                        ret = -ENODEV;
                        goto err_release;
                }
@@ -690,23 +670,6 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
        pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
                ver_release, (gmux_data->indexed ? "indexed" : "classic"));
 
-       /*
-        * Apple systems with gmux are EFI based and normally don't use
-        * VGA. In addition changing IO+MEM ownership between IGP and dGPU
-        * disables IO/MEM used for backlight control on some systems.
-        * Lock IO+MEM to GPU with active IO to prevent switch.
-        */
-       pdev = gmux_get_io_pdev();
-       if (pdev && vga_tryget(pdev,
-                              VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM)) {
-               pr_err("IO+MEM vgaarb-locking for PCI:%s failed\n",
-                       pci_name(pdev));
-               ret = -EBUSY;
-               goto err_release;
-       } else if (pdev)
-               pr_info("locked IO for PCI:%s\n", pci_name(pdev));
-       gmux_data->pdev = pdev;
-
        memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_PLATFORM;
        props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
@@ -822,10 +785,6 @@ err_enable_gpe:
 err_notify:
        backlight_device_unregister(bdev);
 err_release:
-       if (gmux_data->pdev)
-               vga_put(gmux_data->pdev,
-                       VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM);
-       pci_dev_put(pdev);
        release_region(gmux_data->iostart, gmux_data->iolen);
 err_free:
        kfree(gmux_data);
@@ -845,11 +804,6 @@ static void gmux_remove(struct pnp_dev *pnp)
                                           &gmux_notify_handler);
        }
 
-       if (gmux_data->pdev) {
-               vga_put(gmux_data->pdev,
-                       VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM);
-               pci_dev_put(gmux_data->pdev);
-       }
        backlight_device_unregister(gmux_data->bdev);
 
        release_region(gmux_data->iostart, gmux_data->iolen);
index 5269a01d9bdd9fa069541dfec40200be3601f714..136ff2b4cce5e200cc04ce049d98f7199cf6e8a7 100644 (file)
@@ -111,7 +111,7 @@ static struct quirk_entry quirk_asus_x550lb = {
        .xusb2pr = 0x01D9,
 };
 
-static struct quirk_entry quirk_asus_ux330uak = {
+static struct quirk_entry quirk_asus_forceals = {
        .wmi_force_als_set = true,
 };
 
@@ -387,7 +387,7 @@ static const struct dmi_system_id asus_quirks[] = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "UX330UAK"),
                },
-               .driver_data = &quirk_asus_ux330uak,
+               .driver_data = &quirk_asus_forceals,
        },
        {
                .callback = dmi_matched,
@@ -398,6 +398,15 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_x550lb,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. UX430UQ",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "UX430UQ"),
+               },
+               .driver_data = &quirk_asus_forceals,
+       },
        {},
 };
 
index cd4725e7e0b56d8981bc770ca80b562c874370f8..a7b141992cb3f8f92e2d215bd2f610901d62df22 100644 (file)
 #include "dell-smbios.h"
 
 struct quirk_entry {
-       u8 touchpad_led;
-       u8 kbd_led_levels_off_1;
+       bool touchpad_led;
+       bool kbd_led_levels_off_1;
 
-       int needs_kbd_timeouts;
+       bool needs_kbd_timeouts;
        /*
         * Ordered list of timeouts expressed in seconds.
         * The list must end with -1
@@ -50,7 +50,7 @@ struct quirk_entry {
 static struct quirk_entry *quirks;
 
 static struct quirk_entry quirk_dell_vostro_v130 = {
-       .touchpad_led = 1,
+       .touchpad_led = true,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -64,12 +64,12 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
  * is used then BIOS silently set timeout to 0 without any error message.
  */
 static struct quirk_entry quirk_dell_xps13_9333 = {
-       .needs_kbd_timeouts = 1,
+       .needs_kbd_timeouts = true,
        .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
 };
 
 static struct quirk_entry quirk_dell_latitude_e6410 = {
-       .kbd_led_levels_off_1 = 1,
+       .kbd_led_levels_off_1 = true,
 };
 
 static struct platform_driver platform_driver = {
@@ -78,7 +78,6 @@ static struct platform_driver platform_driver = {
        }
 };
 
-static struct calling_interface_buffer *buffer;
 static struct platform_device *platform_device;
 static struct backlight_device *dell_backlight_device;
 static struct rfkill *wifi_rfkill;
@@ -109,6 +108,42 @@ static const struct dmi_system_id dell_device_table[] __initconst = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
                },
        },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "30"), /*Tablet*/
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /*Convertible*/
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "32"), /*Detachable*/
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "30"), /*Tablet*/
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /*Convertible*/
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "32"), /*Detachable*/
+               },
+       },
        {
                .ident = "Dell Computer Corporation",
                .matches = {
@@ -286,7 +321,8 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
        { }
 };
 
-void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3)
+static void dell_fill_request(struct calling_interface_buffer *buffer,
+                              u32 arg0, u32 arg1, u32 arg2, u32 arg3)
 {
        memset(buffer, 0, sizeof(struct calling_interface_buffer));
        buffer->input[0] = arg0;
@@ -295,7 +331,8 @@ void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3)
        buffer->input[3] = arg3;
 }
 
-int dell_send_request(u16 class, u16 select)
+static int dell_send_request(struct calling_interface_buffer *buffer,
+                            u16 class, u16 select)
 {
        int ret;
 
@@ -432,21 +469,22 @@ static int dell_rfkill_set(void *data, bool blocked)
        int disable = blocked ? 1 : 0;
        unsigned long radio = (unsigned long)data;
        int hwswitch_bit = (unsigned long)data - 1;
+       struct calling_interface_buffer buffer;
        int hwswitch;
        int status;
        int ret;
 
-       dell_set_arguments(0, 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
        if (ret)
                return ret;
-       status = buffer->output[1];
+       status = buffer.output[1];
 
-       dell_set_arguments(0x2, 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       dell_fill_request(&buffer, 0x2, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
        if (ret)
                return ret;
-       hwswitch = buffer->output[1];
+       hwswitch = buffer.output[1];
 
        /* If the hardware switch controls this radio, and the hardware
           switch is disabled, always disable the radio */
@@ -454,8 +492,8 @@ static int dell_rfkill_set(void *data, bool blocked)
            (status & BIT(0)) && !(status & BIT(16)))
                disable = 1;
 
-       dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       dell_fill_request(&buffer, 1 | (radio<<8) | (disable << 16), 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
        return ret;
 }
 
@@ -464,9 +502,11 @@ static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
 {
        if (status & BIT(0)) {
                /* Has hw-switch, sync sw_state to BIOS */
+               struct calling_interface_buffer buffer;
                int block = rfkill_blocked(rfkill);
-               dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0);
-               dell_send_request(CLASS_INFO, SELECT_RFKILL);
+               dell_fill_request(&buffer,
+                                  1 | (radio << 8) | (block << 16), 0, 0, 0);
+               dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
        } else {
                /* No hw-switch, sync BIOS state to sw_state */
                rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
@@ -483,21 +523,22 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
 static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 {
        int radio = ((unsigned long)data & 0xF);
+       struct calling_interface_buffer buffer;
        int hwswitch;
        int status;
        int ret;
 
-       dell_set_arguments(0, 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
-       status = buffer->output[1];
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
+       status = buffer.output[1];
 
        if (ret != 0 || !(status & BIT(0))) {
                return;
        }
 
-       dell_set_arguments(0, 0x2, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
-       hwswitch = buffer->output[1];
+       dell_fill_request(&buffer, 0, 0x2, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
+       hwswitch = buffer.output[1];
 
        if (ret != 0)
                return;
@@ -514,22 +555,23 @@ static struct dentry *dell_laptop_dir;
 
 static int dell_debugfs_show(struct seq_file *s, void *data)
 {
+       struct calling_interface_buffer buffer;
        int hwswitch_state;
        int hwswitch_ret;
        int status;
        int ret;
 
-       dell_set_arguments(0, 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
        if (ret)
                return ret;
-       status = buffer->output[1];
+       status = buffer.output[1];
 
-       dell_set_arguments(0, 0x2, 0, 0);
-       hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       dell_fill_request(&buffer, 0, 0x2, 0, 0);
+       hwswitch_ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
        if (hwswitch_ret)
                return hwswitch_ret;
-       hwswitch_state = buffer->output[1];
+       hwswitch_state = buffer.output[1];
 
        seq_printf(s, "return:\t%d\n", ret);
        seq_printf(s, "status:\t0x%X\n", status);
@@ -610,22 +652,23 @@ static const struct file_operations dell_debugfs_fops = {
 
 static void dell_update_rfkill(struct work_struct *ignored)
 {
+       struct calling_interface_buffer buffer;
        int hwswitch = 0;
        int status;
        int ret;
 
-       dell_set_arguments(0, 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
-       status = buffer->output[1];
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
+       status = buffer.output[1];
 
        if (ret != 0)
                return;
 
-       dell_set_arguments(0, 0x2, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       dell_fill_request(&buffer, 0, 0x2, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
 
        if (ret == 0 && (status & BIT(0)))
-               hwswitch = buffer->output[1];
+               hwswitch = buffer.output[1];
 
        if (wifi_rfkill) {
                dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
@@ -683,6 +726,7 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
 
 static int __init dell_setup_rfkill(void)
 {
+       struct calling_interface_buffer buffer;
        int status, ret, whitelisted;
        const char *product;
 
@@ -698,9 +742,9 @@ static int __init dell_setup_rfkill(void)
        if (!force_rfkill && !whitelisted)
                return 0;
 
-       dell_set_arguments(0, 0, 0, 0);
-       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
-       status = buffer->output[1];
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
+       status = buffer.output[1];
 
        /* dell wireless info smbios call is not supported */
        if (ret != 0)
@@ -853,6 +897,7 @@ static void dell_cleanup_rfkill(void)
 
 static int dell_send_intensity(struct backlight_device *bd)
 {
+       struct calling_interface_buffer buffer;
        struct calling_interface_token *token;
        int ret;
 
@@ -860,17 +905,21 @@ static int dell_send_intensity(struct backlight_device *bd)
        if (!token)
                return -ENODEV;
 
-       dell_set_arguments(token->location, bd->props.brightness, 0, 0);
+       dell_fill_request(&buffer,
+                          token->location, bd->props.brightness, 0, 0);
        if (power_supply_is_system_supplied() > 0)
-               ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
+               ret = dell_send_request(&buffer,
+                                       CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
        else
-               ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);
+               ret = dell_send_request(&buffer,
+                                       CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);
 
        return ret;
 }
 
 static int dell_get_intensity(struct backlight_device *bd)
 {
+       struct calling_interface_buffer buffer;
        struct calling_interface_token *token;
        int ret;
 
@@ -878,14 +927,17 @@ static int dell_get_intensity(struct backlight_device *bd)
        if (!token)
                return -ENODEV;
 
-       dell_set_arguments(token->location, 0, 0, 0);
+       dell_fill_request(&buffer, token->location, 0, 0, 0);
        if (power_supply_is_system_supplied() > 0)
-               ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
+               ret = dell_send_request(&buffer,
+                                       CLASS_TOKEN_READ, SELECT_TOKEN_AC);
        else
-               ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT);
+               ret = dell_send_request(&buffer,
+                                       CLASS_TOKEN_READ, SELECT_TOKEN_BAT);
 
        if (ret == 0)
-               ret = buffer->output[1];
+               ret = buffer.output[1];
+
        return ret;
 }
 
@@ -1133,6 +1185,7 @@ static u8 kbd_previous_mode_bit;
 
 static bool kbd_led_present;
 static DEFINE_MUTEX(kbd_led_mutex);
+static enum led_brightness kbd_led_level;
 
 /*
  * NOTE: there are three ways to set the keyboard backlight level.
@@ -1149,31 +1202,33 @@ static DEFINE_MUTEX(kbd_led_mutex);
 
 static int kbd_get_info(struct kbd_info *info)
 {
+       struct calling_interface_buffer buffer;
        u8 units;
        int ret;
 
-       dell_set_arguments(0, 0, 0, 0);
-       ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer,
+                               CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
        if (ret)
                return ret;
 
-       info->modes = buffer->output[1] & 0xFFFF;
-       info->type = (buffer->output[1] >> 24) & 0xFF;
-       info->triggers = buffer->output[2] & 0xFF;
-       units = (buffer->output[2] >> 8) & 0xFF;
-       info->levels = (buffer->output[2] >> 16) & 0xFF;
+       info->modes = buffer.output[1] & 0xFFFF;
+       info->type = (buffer.output[1] >> 24) & 0xFF;
+       info->triggers = buffer.output[2] & 0xFF;
+       units = (buffer.output[2] >> 8) & 0xFF;
+       info->levels = (buffer.output[2] >> 16) & 0xFF;
 
        if (quirks && quirks->kbd_led_levels_off_1 && info->levels)
                info->levels--;
 
        if (units & BIT(0))
-               info->seconds = (buffer->output[3] >> 0) & 0xFF;
+               info->seconds = (buffer.output[3] >> 0) & 0xFF;
        if (units & BIT(1))
-               info->minutes = (buffer->output[3] >> 8) & 0xFF;
+               info->minutes = (buffer.output[3] >> 8) & 0xFF;
        if (units & BIT(2))
-               info->hours = (buffer->output[3] >> 16) & 0xFF;
+               info->hours = (buffer.output[3] >> 16) & 0xFF;
        if (units & BIT(3))
-               info->days = (buffer->output[3] >> 24) & 0xFF;
+               info->days = (buffer.output[3] >> 24) & 0xFF;
 
        return ret;
 }
@@ -1233,31 +1288,34 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
 
 static int kbd_get_state(struct kbd_state *state)
 {
+       struct calling_interface_buffer buffer;
        int ret;
 
-       dell_set_arguments(0x1, 0, 0, 0);
-       ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
+       dell_fill_request(&buffer, 0, 0, 0, 0);
+       ret = dell_send_request(&buffer,
+                               CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
        if (ret)
                return ret;
 
-       state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
+       state->mode_bit = ffs(buffer.output[1] & 0xFFFF);
        if (state->mode_bit != 0)
                state->mode_bit--;
 
-       state->triggers = (buffer->output[1] >> 16) & 0xFF;
-       state->timeout_value = (buffer->output[1] >> 24) & 0x3F;
-       state->timeout_unit = (buffer->output[1] >> 30) & 0x3;
-       state->als_setting = buffer->output[2] & 0xFF;
-       state->als_value = (buffer->output[2] >> 8) & 0xFF;
-       state->level = (buffer->output[2] >> 16) & 0xFF;
-       state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F;
-       state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3;
+       state->triggers = (buffer.output[1] >> 16) & 0xFF;
+       state->timeout_value = (buffer.output[1] >> 24) & 0x3F;
+       state->timeout_unit = (buffer.output[1] >> 30) & 0x3;
+       state->als_setting = buffer.output[2] & 0xFF;
+       state->als_value = (buffer.output[2] >> 8) & 0xFF;
+       state->level = (buffer.output[2] >> 16) & 0xFF;
+       state->timeout_value_ac = (buffer.output[2] >> 24) & 0x3F;
+       state->timeout_unit_ac = (buffer.output[2] >> 30) & 0x3;
 
        return ret;
 }
 
 static int kbd_set_state(struct kbd_state *state)
 {
+       struct calling_interface_buffer buffer;
        int ret;
        u32 input1;
        u32 input2;
@@ -1270,8 +1328,9 @@ static int kbd_set_state(struct kbd_state *state)
        input2 |= (state->level & 0xFF) << 16;
        input2 |= (state->timeout_value_ac & 0x3F) << 24;
        input2 |= (state->timeout_unit_ac & 0x3) << 30;
-       dell_set_arguments(0x2, input1, input2, 0);
-       ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
+       dell_fill_request(&buffer, 0x2, input1, input2, 0);
+       ret = dell_send_request(&buffer,
+                               CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
 
        return ret;
 }
@@ -1298,6 +1357,7 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
 
 static int kbd_set_token_bit(u8 bit)
 {
+       struct calling_interface_buffer buffer;
        struct calling_interface_token *token;
        int ret;
 
@@ -1308,14 +1368,15 @@ static int kbd_set_token_bit(u8 bit)
        if (!token)
                return -EINVAL;
 
-       dell_set_arguments(token->location, token->value, 0, 0);
-       ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
+       dell_fill_request(&buffer, token->location, token->value, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
 
        return ret;
 }
 
 static int kbd_get_token_bit(u8 bit)
 {
+       struct calling_interface_buffer buffer;
        struct calling_interface_token *token;
        int ret;
        int val;
@@ -1327,9 +1388,9 @@ static int kbd_get_token_bit(u8 bit)
        if (!token)
                return -EINVAL;
 
-       dell_set_arguments(token->location, 0, 0, 0);
-       ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD);
-       val = buffer->output[1];
+       dell_fill_request(&buffer, token->location, 0, 0, 0);
+       ret = dell_send_request(&buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD);
+       val = buffer.output[1];
 
        if (ret)
                return ret;
@@ -1947,6 +2008,7 @@ static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev)
 static int kbd_led_level_set(struct led_classdev *led_cdev,
                             enum led_brightness value)
 {
+       enum led_brightness new_value = value;
        struct kbd_state state;
        struct kbd_state new_state;
        u16 num;
@@ -1976,6 +2038,9 @@ static int kbd_led_level_set(struct led_classdev *led_cdev,
        }
 
 out:
+       if (ret == 0)
+               kbd_led_level = new_value;
+
        mutex_unlock(&kbd_led_mutex);
        return ret;
 }
@@ -2003,6 +2068,9 @@ static int __init kbd_led_init(struct device *dev)
                if (kbd_led.max_brightness)
                        kbd_led.max_brightness--;
        }
+
+       kbd_led_level = kbd_led_level_get(NULL);
+
        ret = led_classdev_register(dev, &kbd_led);
        if (ret)
                kbd_led_present = false;
@@ -2027,13 +2095,25 @@ static void kbd_led_exit(void)
 static int dell_laptop_notifier_call(struct notifier_block *nb,
                                     unsigned long action, void *data)
 {
+       bool changed = false;
+       enum led_brightness new_kbd_led_level;
+
        switch (action) {
        case DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED:
                if (!kbd_led_present)
                        break;
 
-               led_classdev_notify_brightness_hw_changed(&kbd_led,
-                                               kbd_led_level_get(&kbd_led));
+               mutex_lock(&kbd_led_mutex);
+               new_kbd_led_level = kbd_led_level_get(&kbd_led);
+               if (kbd_led_level != new_kbd_led_level) {
+                       kbd_led_level = new_kbd_led_level;
+                       changed = true;
+               }
+               mutex_unlock(&kbd_led_mutex);
+
+               if (changed)
+                       led_classdev_notify_brightness_hw_changed(&kbd_led,
+                                                               kbd_led_level);
                break;
        }
 
@@ -2046,6 +2126,7 @@ static struct notifier_block dell_laptop_notifier = {
 
 int dell_micmute_led_set(int state)
 {
+       struct calling_interface_buffer buffer;
        struct calling_interface_token *token;
 
        if (state == 0)
@@ -2058,8 +2139,8 @@ int dell_micmute_led_set(int state)
        if (!token)
                return -ENODEV;
 
-       dell_set_arguments(token->location, token->value, 0, 0);
-       dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
+       dell_fill_request(&buffer, token->location, token->value, 0, 0);
+       dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
 
        return state;
 }
@@ -2090,13 +2171,6 @@ static int __init dell_init(void)
        if (ret)
                goto fail_platform_device2;
 
-       buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
-       if (!buffer) {
-               ret = -ENOMEM;
-               goto fail_buffer;
-       }
-
-
        ret = dell_setup_rfkill();
 
        if (ret) {
@@ -2121,10 +2195,13 @@ static int __init dell_init(void)
 
        token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
        if (token) {
-               dell_set_arguments(token->location, 0, 0, 0);
-               ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
+               struct calling_interface_buffer buffer;
+
+               dell_fill_request(&buffer, token->location, 0, 0, 0);
+               ret = dell_send_request(&buffer,
+                                       CLASS_TOKEN_READ, SELECT_TOKEN_AC);
                if (ret)
-                       max_intensity = buffer->output[3];
+                       max_intensity = buffer.output[3];
        }
 
        if (max_intensity) {
@@ -2158,8 +2235,6 @@ static int __init dell_init(void)
 fail_get_brightness:
        backlight_device_unregister(dell_backlight_device);
 fail_backlight:
-       kfree(buffer);
-fail_buffer:
        dell_cleanup_rfkill();
 fail_rfkill:
        platform_device_del(platform_device);
@@ -2179,7 +2254,6 @@ static void __exit dell_exit(void)
                touchpad_led_exit();
        kbd_led_exit();
        backlight_device_unregister(dell_backlight_device);
-       kfree(buffer);
        dell_cleanup_rfkill();
        if (platform_device) {
                platform_device_unregister(platform_device);
index 6a60db515bdabcb490410b35ea85bb7a3fd621f1..8541cde4cb7d3c4febde205b639428b89215bdc0 100644 (file)
@@ -65,10 +65,10 @@ static struct smbios_call call_whitelist[] = {
 
 /* calls that are explicitly blacklisted */
 static struct smbios_call call_blacklist[] = {
-       {0x0000, 01, 07}, /* manufacturing use */
-       {0x0000, 06, 05}, /* manufacturing use */
-       {0x0000, 11, 03}, /* write once */
-       {0x0000, 11, 07}, /* write once */
+       {0x0000,  1,  7}, /* manufacturing use */
+       {0x0000,  6,  5}, /* manufacturing use */
+       {0x0000, 11,  3}, /* write once */
+       {0x0000, 11,  7}, /* write once */
        {0x0000, 11, 11}, /* write once */
        {0x0000, 19, -1}, /* diagnostics */
        /* handled by kernel: dell-laptop */
index fb25b20df316f39140a3af0f974e9f1fa30aabff..2c9927430d8525bc7df26c50de85e5a2fd8863d1 100644 (file)
@@ -261,6 +261,9 @@ static const u16 bios_to_linux_keycode[256] = {
  * override them.
  */
 static const struct key_entry dell_wmi_keymap_type_0010[] = {
+       /* Mic mute */
+       { KE_KEY, 0x150, { KEY_MICMUTE } },
+
        /* Fn-lock */
        { KE_IGNORE, 0x151, { KEY_RESERVED } },
 
diff --git a/drivers/platform/x86/gpd-pocket-fan.c b/drivers/platform/x86/gpd-pocket-fan.c
new file mode 100644 (file)
index 0000000..2d645c5
--- /dev/null
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GPD Pocket fan controller driver
+ *
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/thermal.h>
+#include <linux/workqueue.h>
+
+#define MAX_SPEED 3
+
+static int temp_limits[3] = { 55000, 60000, 65000 };
+module_param_array(temp_limits, int, NULL, 0444);
+MODULE_PARM_DESC(temp_limits,
+                "Milli-celcius values above which the fan speed increases");
+
+static int hysteresis = 3000;
+module_param(hysteresis, int, 0444);
+MODULE_PARM_DESC(hysteresis,
+                "Hysteresis in milli-celcius before lowering the fan speed");
+
+static int speed_on_ac = 2;
+module_param(speed_on_ac, int, 0444);
+MODULE_PARM_DESC(speed_on_ac,
+                "minimum fan speed to allow when system is powered by AC");
+
+struct gpd_pocket_fan_data {
+       struct device *dev;
+       struct thermal_zone_device *dts0;
+       struct thermal_zone_device *dts1;
+       struct gpio_desc *gpio0;
+       struct gpio_desc *gpio1;
+       struct delayed_work work;
+       int last_speed;
+};
+
+static void gpd_pocket_fan_set_speed(struct gpd_pocket_fan_data *fan, int speed)
+{
+       if (speed == fan->last_speed)
+               return;
+
+       gpiod_direction_output(fan->gpio0, !!(speed & 1));
+       gpiod_direction_output(fan->gpio1, !!(speed & 2));
+
+       fan->last_speed = speed;
+}
+
+static int gpd_pocket_fan_min_speed(void)
+{
+       if (power_supply_is_system_supplied())
+               return speed_on_ac;
+       else
+               return 0;
+}
+
+static void gpd_pocket_fan_worker(struct work_struct *work)
+{
+       struct gpd_pocket_fan_data *fan =
+               container_of(work, struct gpd_pocket_fan_data, work.work);
+       int t0, t1, temp, speed, min_speed, i;
+
+       if (thermal_zone_get_temp(fan->dts0, &t0) ||
+           thermal_zone_get_temp(fan->dts1, &t1)) {
+               dev_warn(fan->dev, "Error getting temperature\n");
+               speed = MAX_SPEED;
+               goto set_speed;
+       }
+
+       temp = max(t0, t1);
+
+       speed = fan->last_speed;
+       min_speed = gpd_pocket_fan_min_speed();
+
+       /* Determine minimum speed */
+       for (i = min_speed; i < ARRAY_SIZE(temp_limits); i++) {
+               if (temp < temp_limits[i])
+                       break;
+       }
+       if (speed < i)
+               speed = i;
+
+       /* Use hysteresis before lowering speed again */
+       for (i = min_speed; i < ARRAY_SIZE(temp_limits); i++) {
+               if (temp <= (temp_limits[i] - hysteresis))
+                       break;
+       }
+       if (speed > i)
+               speed = i;
+
+       if (fan->last_speed <= 0 && speed)
+               speed = MAX_SPEED; /* kick start motor */
+
+set_speed:
+       gpd_pocket_fan_set_speed(fan, speed);
+
+       /* When mostly idle (low temp/speed), slow down the poll interval. */
+       queue_delayed_work(system_wq, &fan->work,
+                          msecs_to_jiffies(4000 / (speed + 1)));
+}
+
+static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data *fan)
+{
+       fan->last_speed = -1;
+       mod_delayed_work(system_wq, &fan->work, 0);
+}
+
+static int gpd_pocket_fan_probe(struct platform_device *pdev)
+{
+       struct gpd_pocket_fan_data *fan;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(temp_limits); i++) {
+               if (temp_limits[i] < 40000 || temp_limits[i] > 70000) {
+                       dev_err(&pdev->dev, "Invalid temp-limit %d (must be between 40000 and 70000)\n",
+                               temp_limits[i]);
+                       return -EINVAL;
+               }
+       }
+       if (hysteresis < 1000 || hysteresis > 10000) {
+               dev_err(&pdev->dev, "Invalid hysteresis %d (must be between 1000 and 10000)\n",
+                       hysteresis);
+               return -EINVAL;
+       }
+       if (speed_on_ac < 0 || speed_on_ac > MAX_SPEED) {
+               dev_err(&pdev->dev, "Invalid speed_on_ac %d (must be between 0 and 3)\n",
+                       speed_on_ac);
+               return -EINVAL;
+       }
+
+       fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
+       if (!fan)
+               return -ENOMEM;
+
+       fan->dev = &pdev->dev;
+       INIT_DELAYED_WORK(&fan->work, gpd_pocket_fan_worker);
+
+       /* Note this returns a "weak" reference which we don't need to free */
+       fan->dts0 = thermal_zone_get_zone_by_name("soc_dts0");
+       if (IS_ERR(fan->dts0))
+               return -EPROBE_DEFER;
+
+       fan->dts1 = thermal_zone_get_zone_by_name("soc_dts1");
+       if (IS_ERR(fan->dts1))
+               return -EPROBE_DEFER;
+
+       fan->gpio0 = devm_gpiod_get_index(fan->dev, NULL, 0, GPIOD_ASIS);
+       if (IS_ERR(fan->gpio0))
+               return PTR_ERR(fan->gpio0);
+
+       fan->gpio1 = devm_gpiod_get_index(fan->dev, NULL, 1, GPIOD_ASIS);
+       if (IS_ERR(fan->gpio1))
+               return PTR_ERR(fan->gpio1);
+
+       gpd_pocket_fan_force_update(fan);
+
+       platform_set_drvdata(pdev, fan);
+       return 0;
+}
+
+static int gpd_pocket_fan_remove(struct platform_device *pdev)
+{
+       struct gpd_pocket_fan_data *fan = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&fan->work);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int gpd_pocket_fan_suspend(struct device *dev)
+{
+       struct gpd_pocket_fan_data *fan = dev_get_drvdata(dev);
+
+       cancel_delayed_work_sync(&fan->work);
+       gpd_pocket_fan_set_speed(fan, gpd_pocket_fan_min_speed());
+       return 0;
+}
+
+static int gpd_pocket_fan_resume(struct device *dev)
+{
+       struct gpd_pocket_fan_data *fan = dev_get_drvdata(dev);
+
+       gpd_pocket_fan_force_update(fan);
+       return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(gpd_pocket_fan_pm_ops,
+                        gpd_pocket_fan_suspend,
+                        gpd_pocket_fan_resume);
+
+static struct acpi_device_id gpd_pocket_fan_acpi_match[] = {
+       { "FAN02501" },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, gpd_pocket_fan_acpi_match);
+
+static struct platform_driver gpd_pocket_fan_driver = {
+       .probe  = gpd_pocket_fan_probe,
+       .remove = gpd_pocket_fan_remove,
+       .driver = {
+               .name                   = "gpd_pocket_fan",
+               .acpi_match_table       = gpd_pocket_fan_acpi_match,
+               .pm                     = &gpd_pocket_fan_pm_ops,
+        },
+};
+
+module_platform_driver(gpd_pocket_fan_driver);
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
+MODULE_DESCRIPTION("GPD pocket fan driver");
+MODULE_LICENSE("GPL");
index 53ab4e0f896255614b2462d5323a03e53959a73e..b2bbddd09a52c5506104ae16a13a0b848354b46a 100644 (file)
@@ -124,10 +124,10 @@ static int read_method_int(acpi_handle handle, const char *method, int *val)
        if (ACPI_FAILURE(status)) {
                *val = -1;
                return -1;
-       } else {
-               *val = result;
-               return 0;
        }
+       *val = result;
+       return 0;
+
 }
 
 static int method_gbmd(acpi_handle handle, unsigned long *ret)
@@ -164,10 +164,10 @@ static int method_vpcr(acpi_handle handle, int cmd, int *ret)
        if (ACPI_FAILURE(status)) {
                *ret = -1;
                return -1;
-       } else {
-               *ret = result;
-               return 0;
        }
+       *ret = result;
+       return 0;
+
 }
 
 static int method_vpcw(acpi_handle handle, int cmd, int data)
@@ -231,7 +231,7 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
                if (val == 0)
                        return 0;
        }
-       pr_err("timeout in write_ec_cmd\n");
+       pr_err("timeout in %s\n", __func__);
        return -1;
 }
 
@@ -963,6 +963,13 @@ static void ideapad_wmi_notify(u32 value, void *context)
  * report all radios as hardware-blocked.
  */
 static const struct dmi_system_id no_hw_rfkill_list[] = {
+       {
+               .ident = "Lenovo RESCUER R720-15IKBN",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_BOARD_NAME, "80WW"),
+               },
+       },
        {
                .ident = "Lenovo G40-30",
                .matches = {
@@ -1103,6 +1110,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"),
                },
        },
+       {
+               .ident = "Lenovo Legion Y720-15IKB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKB"),
+               },
+       },
        {
                .ident = "Lenovo Legion Y720-15IKBN",
                .matches = {
index f470279c4c100424815c45dacc1304193677ae8a..d1a01311c1a2937b6863bea6b696566e376f8ac1 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/acpi.h>
 #include <linux/suspend.h>
 #include <acpi/acpi_bus.h>
+#include <linux/dmi.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Alex Hung");
@@ -73,6 +74,24 @@ static const struct key_entry intel_array_keymap[] = {
        { KE_END },
 };
 
+static const struct dmi_system_id button_array_table[] = {
+       {
+               .ident = "Wacom MobileStudio Pro 13",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Wacom Co.,Ltd"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 13"),
+               },
+       },
+       {
+               .ident = "Wacom MobileStudio Pro 16",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Wacom Co.,Ltd"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 16"),
+               },
+       },
+       { }
+};
+
 struct intel_hid_priv {
        struct input_dev *input_dev;
        struct input_dev *array;
@@ -263,10 +282,27 @@ wakeup:
                         ev_index);
 }
 
+static bool button_array_present(struct platform_device *device)
+{
+       acpi_handle handle = ACPI_HANDLE(&device->dev);
+       unsigned long long event_cap;
+       acpi_status status;
+       bool supported = false;
+
+       status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap);
+       if (ACPI_SUCCESS(status) && (event_cap & 0x20000))
+               supported = true;
+
+       if (dmi_check_system(button_array_table))
+               supported = true;
+
+       return supported;
+}
+
 static int intel_hid_probe(struct platform_device *device)
 {
        acpi_handle handle = ACPI_HANDLE(&device->dev);
-       unsigned long long event_cap, mode;
+       unsigned long long mode;
        struct intel_hid_priv *priv;
        acpi_status status;
        int err;
@@ -299,8 +335,7 @@ static int intel_hid_probe(struct platform_device *device)
        }
 
        /* Setup 5 button array */
-       status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap);
-       if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) {
+       if (button_array_present(device)) {
                dev_info(&device->dev, "platform supports 5 button array\n");
                err = intel_button_array_input_setup(device);
                if (err)
index 58c5ff36523ab9bc4d570ff781f91cd0d9a433ea..b703d6f5b099b3d0d74b77d60d2e5401ff3b816e 100644 (file)
@@ -1,30 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  *  Intel Virtual Button driver for Windows 8.1+
  *
  *  Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com>
  *  Copyright (C) 2016 Alex Hung <alex.hung@canonical.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
  */
 
+#include <linux/acpi.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
-#include <linux/input.h>
 #include <linux/platform_device.h>
-#include <linux/input/sparse-keymap.h>
-#include <linux/acpi.h>
 #include <linux/suspend.h>
-#include <acpi/acpi_bus.h>
+
+/* When NOT in tablet mode, VGBS returns with the flag 0x40 */
+#define TABLET_MODE_FLAG 0x40
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("AceLan Kao");
@@ -38,10 +29,16 @@ static const struct acpi_device_id intel_vbtn_ids[] = {
 static const struct key_entry intel_vbtn_keymap[] = {
        { KE_KEY, 0xC0, { KEY_POWER } },        /* power key press */
        { KE_IGNORE, 0xC1, { KEY_POWER } },     /* power key release */
+       { KE_KEY, 0xC2, { KEY_LEFTMETA } },             /* 'Windows' key press */
+       { KE_KEY, 0xC3, { KEY_LEFTMETA } },             /* 'Windows' key release */
        { KE_KEY, 0xC4, { KEY_VOLUMEUP } },             /* volume-up key press */
        { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } },          /* volume-up key release */
        { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } },           /* volume-down key press */
        { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },        /* volume-down key release */
+       { KE_KEY,    0xC8, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key press */
+       { KE_KEY,    0xC9, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key release */
+       { KE_SW,     0xCC, { .sw = { SW_TABLET_MODE, 1 } } },   /* Tablet */
+       { KE_SW,     0xCD, { .sw = { SW_TABLET_MODE, 0 } } },   /* Laptop */
        { KE_END },
 };
 
@@ -74,20 +71,35 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
 {
        struct platform_device *device = context;
        struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
+       unsigned int val = !(event & 1); /* Even=press, Odd=release */
+       const struct key_entry *ke_rel;
+       bool autorelease;
 
        if (priv->wakeup_mode) {
                if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) {
                        pm_wakeup_hard_event(&device->dev);
                        return;
                }
-       } else if (sparse_keymap_report_event(priv->input_dev, event, 1, true)) {
-               return;
+               goto out_unknown;
        }
+
+       /*
+        * Even press events are autorelease if there is no corresponding odd
+        * release event, or if the odd event is KE_IGNORE.
+        */
+       ke_rel = sparse_keymap_entry_from_scancode(priv->input_dev, event | 1);
+       autorelease = val && (!ke_rel || ke_rel->type == KE_IGNORE);
+
+       if (sparse_keymap_report_event(priv->input_dev, event, val, autorelease))
+               return;
+
+out_unknown:
        dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
 }
 
 static int intel_vbtn_probe(struct platform_device *device)
 {
+       struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
        acpi_handle handle = ACPI_HANDLE(&device->dev);
        struct intel_vbtn_priv *priv;
        acpi_status status;
@@ -110,6 +122,23 @@ static int intel_vbtn_probe(struct platform_device *device)
                return err;
        }
 
+       /*
+        * VGBS being present and returning something means we have
+        * a tablet mode switch.
+        */
+       status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
+       if (ACPI_SUCCESS(status)) {
+               union acpi_object *obj = vgbs_output.pointer;
+
+               if (obj && obj->type == ACPI_TYPE_INTEGER) {
+                       int m = !(obj->integer.value & TABLET_MODE_FLAG);
+
+                       input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
+               }
+       }
+
+       kfree(vgbs_output.pointer);
+
        status = acpi_install_notify_handler(handle,
                                             ACPI_DEVICE_NOTIFY,
                                             notify_handler,
diff --git a/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c b/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c
new file mode 100644 (file)
index 0000000..38b8e7c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Power-button driver for Dollar Cove TI PMIC
+ * Copyright (C) 2014 Intel Corp
+ * Copyright (c) 2017 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+
+#define CHTDC_TI_SIRQ_REG      0x3
+#define SIRQ_PWRBTN_REL                BIT(0)
+
+static irqreturn_t chtdc_ti_pwrbtn_interrupt(int irq, void *dev_id)
+{
+       struct input_dev *input = dev_id;
+       struct device *dev = input->dev.parent;
+       struct regmap *regmap = dev_get_drvdata(dev);
+       int state;
+
+       if (!regmap_read(regmap, CHTDC_TI_SIRQ_REG, &state)) {
+               dev_dbg(dev, "SIRQ_REG=0x%x\n", state);
+               input_report_key(input, KEY_POWER, !(state & SIRQ_PWRBTN_REL));
+               input_sync(input);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int chtdc_ti_pwrbtn_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
+       struct input_dev *input;
+       int irq, err;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+       input->name = pdev->name;
+       input->phys = "power-button/input0";
+       input->id.bustype = BUS_HOST;
+       input_set_capability(input, EV_KEY, KEY_POWER);
+       err = input_register_device(input);
+       if (err)
+               return err;
+
+       dev_set_drvdata(dev, pmic->regmap);
+
+       err = devm_request_threaded_irq(dev, irq, NULL,
+                                       chtdc_ti_pwrbtn_interrupt,
+                                       0, KBUILD_MODNAME, input);
+       if (err)
+               return err;
+
+       device_init_wakeup(dev, true);
+       dev_pm_set_wake_irq(dev, irq);
+       return 0;
+}
+
+static int chtdc_ti_pwrbtn_remove(struct platform_device *pdev)
+{
+       dev_pm_clear_wake_irq(&pdev->dev);
+       device_init_wakeup(&pdev->dev, false);
+       return 0;
+}
+
+static const struct platform_device_id chtdc_ti_pwrbtn_id_table[] = {
+       { .name = "chtdc_ti_pwrbtn" },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, chtdc_ti_pwrbtn_id_table);
+
+static struct platform_driver chtdc_ti_pwrbtn_driver = {
+       .driver = {
+               .name   = KBUILD_MODNAME,
+       },
+       .probe          = chtdc_ti_pwrbtn_probe,
+       .remove         = chtdc_ti_pwrbtn_remove,
+       .id_table       = chtdc_ti_pwrbtn_id_table,
+};
+module_platform_driver(chtdc_ti_pwrbtn_driver);
+
+MODULE_DESCRIPTION("Power-button driver for Dollar Cove TI PMIC");
+MODULE_LICENSE("GPL v2");
index f7b67e898abcf7be58e76995d73b282f684a0c7f..a473dc51b18d6d76d27d2cde2572cfcc42435bc6 100644 (file)
@@ -180,7 +180,7 @@ static int int0002_probe(struct platform_device *pdev)
         * to gpiochip_set_chained_irqchip, because the irq is shared.
         */
        ret = devm_request_irq(dev, irq, int0002_irq,
-                              IRQF_SHARED | IRQF_NO_THREAD, "INT0002", chip);
+                              IRQF_SHARED, "INT0002", chip);
        if (ret) {
                dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret);
                return ret;
index 17e08b42b0a97f02c8120f83ed51b4e2cb18d676..43bbe74743d9e85efa43584346adddc1bca1992d 100644 (file)
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/init.h>
 #include <linux/io.h>
+#include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/uaccess.h>
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include <asm/pmc_core.h>
 
 #include "intel_pmc_core.h"
 
+#define ICPU(model, data) \
+       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (kernel_ulong_t)data }
+
 static struct pmc_dev pmc;
 
 static const struct pmc_bit_map spt_pll_map[] = {
@@ -119,10 +123,88 @@ static const struct pmc_reg_map spt_reg_map = {
        .pm_read_disable_bit = SPT_PMC_READ_DISABLE_BIT,
 };
 
-static const struct pci_device_id pmc_pci_ids[] = {
-       { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID),
-                                       (kernel_ulong_t)&spt_reg_map },
-       { 0, },
+/* Cannonlake: PGD PFET Enable Ack Status Register(s) bitmap */
+static const struct pmc_bit_map cnp_pfear_map[] = {
+       {"PMC",                 BIT(0)},
+       {"OPI-DMI",             BIT(1)},
+       {"SPI/eSPI",            BIT(2)},
+       {"XHCI",                BIT(3)},
+       {"SPA",                 BIT(4)},
+       {"SPB",                 BIT(5)},
+       {"SPC",                 BIT(6)},
+       {"GBE",                 BIT(7)},
+
+       {"SATA",                BIT(0)},
+       {"HDA_PGD0",            BIT(1)},
+       {"HDA_PGD1",            BIT(2)},
+       {"HDA_PGD2",            BIT(3)},
+       {"HDA_PGD3",            BIT(4)},
+       {"SPD",                 BIT(5)},
+       {"LPSS",                BIT(6)},
+       {"LPC",                 BIT(7)},
+
+       {"SMB",                 BIT(0)},
+       {"ISH",                 BIT(1)},
+       {"P2SB",                BIT(2)},
+       {"NPK_VNN",             BIT(3)},
+       {"SDX",                 BIT(4)},
+       {"SPE",                 BIT(5)},
+       {"Fuse",                BIT(6)},
+       {"Res_23",              BIT(7)},
+
+       {"CSME_FSC",            BIT(0)},
+       {"USB3_OTG",            BIT(1)},
+       {"EXI",                 BIT(2)},
+       {"CSE",                 BIT(3)},
+       {"csme_kvm",            BIT(4)},
+       {"csme_pmt",            BIT(5)},
+       {"csme_clink",          BIT(6)},
+       {"csme_ptio",           BIT(7)},
+
+       {"csme_usbr",           BIT(0)},
+       {"csme_susram",         BIT(1)},
+       {"csme_smt1",           BIT(2)},
+       {"CSME_SMT4",           BIT(3)},
+       {"csme_sms2",           BIT(4)},
+       {"csme_sms1",           BIT(5)},
+       {"csme_rtc",            BIT(6)},
+       {"csme_psf",            BIT(7)},
+
+       {"SBR0",                BIT(0)},
+       {"SBR1",                BIT(1)},
+       {"SBR2",                BIT(2)},
+       {"SBR3",                BIT(3)},
+       {"SBR4",                BIT(4)},
+       {"SBR5",                BIT(5)},
+       {"CSME_PECI",           BIT(6)},
+       {"PSF1",                BIT(7)},
+
+       {"PSF2",                BIT(0)},
+       {"PSF3",                BIT(1)},
+       {"PSF4",                BIT(2)},
+       {"CNVI",                BIT(3)},
+       {"UFS0",                BIT(4)},
+       {"EMMC",                BIT(5)},
+       {"Res_6",               BIT(6)},
+       {"SBR6",                BIT(7)},
+
+       {"SBR7",                BIT(0)},
+       {"NPK_AON",             BIT(1)},
+       {"HDA_PGD4",            BIT(2)},
+       {"HDA_PGD5",            BIT(3)},
+       {"HDA_PGD6",            BIT(4)},
+       {}
+};
+
+static const struct pmc_reg_map cnp_reg_map = {
+       .pfear_sts = cnp_pfear_map,
+       .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
+       .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
+       .regmap_length = CNP_PMC_MMIO_REG_LEN,
+       .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
+       .ppfear_buckets = CNP_PPFEAR_NUM_ENTRIES,
+       .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
+       .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
 };
 
 static inline u8 pmc_core_reg_read_byte(struct pmc_dev *pmcdev, int offset)
@@ -146,37 +228,6 @@ 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;
-       const struct pmc_reg_map *map = pmcdev->map;
-       u32 value;
-
-       if (!pmcdev->has_slp_s0_res)
-               return -EACCES;
-
-       value = pmc_core_reg_read(pmcdev, map->slp_s0_offset);
-       *data = pmc_core_adjust_slp_s0_step(value);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
-
 static int pmc_core_dev_state_get(void *data, u64 *val)
 {
        struct pmc_dev *pmcdev = data;
@@ -437,47 +488,33 @@ static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
 
 static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
 {
-       struct dentry *dir, *file;
+       struct dentry *dir;
 
        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);
-       if (!file)
-               goto err;
-
-       file = debugfs_create_file("pch_ip_power_gating_status",
-                                  S_IFREG | S_IRUGO, dir, pmcdev,
-                                  &pmc_core_ppfear_ops);
-       if (!file)
-               goto err;
-
-       file = debugfs_create_file("mphy_core_lanes_power_gating_status",
-                                  S_IFREG | S_IRUGO, dir, pmcdev,
-                                  &pmc_core_mphy_pg_ops);
-       if (!file)
-               goto err;
-
-       file = debugfs_create_file("pll_status",
-                                  S_IFREG | S_IRUGO, dir, pmcdev,
-                                  &pmc_core_pll_ops);
-       if (!file)
-               goto err;
-
-       file = debugfs_create_file("ltr_ignore",
-                                  S_IFREG | S_IRUGO, dir, pmcdev,
-                                  &pmc_core_ltr_ignore_ops);
-
-       if (!file)
-               goto err;
+
+       debugfs_create_file("slp_s0_residency_usec", 0444, dir, pmcdev,
+                           &pmc_core_dev_state);
+
+       debugfs_create_file("pch_ip_power_gating_status", 0444, dir, pmcdev,
+                           &pmc_core_ppfear_ops);
+
+       debugfs_create_file("ltr_ignore", 0644, dir, pmcdev,
+                           &pmc_core_ltr_ignore_ops);
+
+       if (pmcdev->map->pll_sts)
+               debugfs_create_file("pll_status", 0444, dir, pmcdev,
+                                   &pmc_core_pll_ops);
+
+       if (pmcdev->map->mphy_sts)
+               debugfs_create_file("mphy_core_lanes_power_gating_status",
+                                   0444, dir, pmcdev,
+                                   &pmc_core_mphy_pg_ops);
 
        return 0;
-err:
-       pmc_core_dbgfs_unregister(pmcdev);
-       return -ENODEV;
 }
 #else
 static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
@@ -491,71 +528,76 @@ static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
 #endif /* CONFIG_DEBUG_FS */
 
 static const struct x86_cpu_id intel_pmc_core_ids[] = {
-       { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT,
-               (kernel_ulong_t)NULL},
-       { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_DESKTOP, X86_FEATURE_MWAIT,
-               (kernel_ulong_t)NULL},
-       { X86_VENDOR_INTEL, 6, INTEL_FAM6_KABYLAKE_MOBILE, X86_FEATURE_MWAIT,
-               (kernel_ulong_t)NULL},
-       { X86_VENDOR_INTEL, 6, INTEL_FAM6_KABYLAKE_DESKTOP, X86_FEATURE_MWAIT,
-               (kernel_ulong_t)NULL},
+       ICPU(INTEL_FAM6_SKYLAKE_MOBILE, &spt_reg_map),
+       ICPU(INTEL_FAM6_SKYLAKE_DESKTOP, &spt_reg_map),
+       ICPU(INTEL_FAM6_KABYLAKE_MOBILE, &spt_reg_map),
+       ICPU(INTEL_FAM6_KABYLAKE_DESKTOP, &spt_reg_map),
+       ICPU(INTEL_FAM6_CANNONLAKE_MOBILE, &cnp_reg_map),
        {}
 };
 
-static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
+MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_ids);
+
+static const struct pci_device_id pmc_pci_ids[] = {
+       { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), 0},
+       { 0, },
+};
+
+static int __init pmc_core_probe(void)
 {
-       struct device *ptr_dev = &dev->dev;
        struct pmc_dev *pmcdev = &pmc;
        const struct x86_cpu_id *cpu_id;
-       const struct pmc_reg_map *map = (struct pmc_reg_map *)id->driver_data;
+       u64 slp_s0_addr;
        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;
-       }
-       pmcdev->base_addr &= PMC_BASE_ADDR_MASK;
-       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");
+       if (!cpu_id)
+               return -ENODEV;
+
+       pmcdev->map = (struct pmc_reg_map *)cpu_id->driver_data;
+
+       /*
+        * Coffeelake has CPU ID of Kabylake and Cannonlake PCH. So here
+        * Sunrisepoint PCH regmap can't be used. Use Cannonlake PCH regmap
+        * in this case.
+        */
+       if (!pci_dev_present(pmc_pci_ids))
+               pmcdev->map = &cnp_reg_map;
+
+       if (lpit_read_residency_count_address(&slp_s0_addr))
+               pmcdev->base_addr = PMC_BASE_ADDR_DEFAULT;
+       else
+               pmcdev->base_addr = slp_s0_addr - pmcdev->map->slp_s0_offset;
+
+       pmcdev->regbase = ioremap(pmcdev->base_addr,
+                                 pmcdev->map->regmap_length);
+       if (!pmcdev->regbase)
                return -ENOMEM;
-       }
 
        mutex_init(&pmcdev->lock);
-       pmcdev->map = map;
        pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit();
 
        err = pmc_core_dbgfs_register(pmcdev);
-       if (err < 0)
-               dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n");
+       if (err < 0) {
+               pr_warn(" debugfs register failed.\n");
+               iounmap(pmcdev->regbase);
+               return err;
+       }
 
-       pmc.has_slp_s0_res = true;
+       pr_info(" initialized\n");
        return 0;
 }
+module_init(pmc_core_probe)
 
-static struct pci_driver intel_pmc_core_driver = {
-       .name = "intel_pmc_core",
-       .id_table = pmc_pci_ids,
-       .probe = pmc_core_probe,
-};
+static void __exit pmc_core_remove(void)
+{
+       struct pmc_dev *pmcdev = &pmc;
+
+       pmc_core_dbgfs_unregister(pmcdev);
+       mutex_destroy(&pmcdev->lock);
+       iounmap(pmcdev->regbase);
+}
+module_exit(pmc_core_remove)
 
-builtin_pci_driver(intel_pmc_core_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel PMC Core Driver");
index 3d225a9cc09f28dc69228188c54068fff12a8da9..5fa5f97870aafe4890ff77ec591bb94e7fba68f5 100644 (file)
 #ifndef PMC_CORE_H
 #define PMC_CORE_H
 
+#define PMC_BASE_ADDR_DEFAULT                  0xFE000000
+
 /* 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_PM_CFG_OFFSET                  0x18
@@ -122,6 +123,17 @@ enum ppfear_regs {
 #define SPT_PMC_BIT_MPHY_CMN_LANE2             BIT(2)
 #define SPT_PMC_BIT_MPHY_CMN_LANE3             BIT(3)
 
+/* Cannonlake Power Management Controller register offsets */
+#define CNP_PMC_SLP_S0_RES_COUNTER_OFFSET      0x193C
+#define CNP_PMC_LTR_IGNORE_OFFSET              0x1B0C
+#define CNP_PMC_PM_CFG_OFFSET                  0x1818
+/* Cannonlake: PGD PFET Enable Ack Status Register(s) start */
+#define CNP_PMC_HOST_PPFEAR0A                  0x1D90
+
+#define CNP_PMC_MMIO_REG_LEN                   0x2000
+#define CNP_PPFEAR_NUM_ENTRIES                 8
+#define CNP_PMC_READ_DISABLE_BIT               22
+
 struct pmc_bit_map {
        const char *name;
        u32 bit_mask;
@@ -135,7 +147,6 @@ struct pmc_bit_map {
  * @pll_sts:           Maps name of PLL to corresponding bit status
  * @slp_s0_offset:     PWRMBASE offset to read SLP_S0 residency
  * @ltr_ignore_offset: PWRMBASE offset to read/write LTR ignore bit
- * @base_address:      Base address of PWRMBASE defined in BIOS writer guide
  * @regmap_length:     Length of memory to map from PWRMBASE address to access
  * @ppfear0_offset:    PWRMBASE offset to to read PPFEAR*
  * @ppfear_buckets:    Number of 8 bits blocks to read all IP blocks from
@@ -152,7 +163,6 @@ struct pmc_reg_map {
        const struct pmc_bit_map *pll_sts;
        const u32 slp_s0_offset;
        const u32 ltr_ignore_offset;
-       const u32 base_address;
        const int regmap_length;
        const u32 ppfear0_offset;
        const int ppfear_buckets;
@@ -162,12 +172,14 @@ struct pmc_reg_map {
 
 /**
  * struct pmc_dev - pmc device structure
- * @base_addr:         comtains pmc base address
+ * @base_addr:         contains 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.
+ * @map:               pointer to pmc_reg_map struct that contains platform
+ *                     specific attributes
+ * @dbgfs_dir:         path to debugfs interface
+ * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
+ *                     used to read MPHY PG and PLL status are available
+ * @mutex_lock:                mutex to complete one transcation
  *
  * pmc_dev contains info about power management controller device.
  */
@@ -178,7 +190,6 @@ struct pmc_dev {
 #if IS_ENABLED(CONFIG_DEBUG_FS)
        struct dentry *dbgfs_dir;
 #endif /* CONFIG_DEBUG_FS */
-       bool has_slp_s0_res;
        int pmc_xram_read_bit;
        struct mutex lock; /* generic mutex lock for PMC Core */
 };
index e03fa31446ca83b107c220e438db53db38c44e80..e7edc8c6393674dfdcfcf53cccb80da50fd255f2 100644 (file)
@@ -215,11 +215,11 @@ static inline int is_gcr_valid(u32 offset)
 }
 
 /**
- * intel_pmc_gcr_read() - Read PMC GCR register
+ * intel_pmc_gcr_read() - Read a 32-bit PMC GCR register
  * @offset:    offset of GCR register from GCR address base
  * @data:      data pointer for storing the register output
  *
- * Reads the PMC GCR register of given offset.
+ * Reads the 32-bit PMC GCR register at given offset.
  *
  * Return:     negative value on error or 0 on success.
  */
@@ -243,6 +243,35 @@ int intel_pmc_gcr_read(u32 offset, u32 *data)
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
 
+/**
+ * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
+ * @offset:    offset of GCR register from GCR address base
+ * @data:      data pointer for storing the register output
+ *
+ * Reads the 64-bit PMC GCR register at given offset.
+ *
+ * Return:     negative value on error or 0 on success.
+ */
+int intel_pmc_gcr_read64(u32 offset, u64 *data)
+{
+       int ret;
+
+       spin_lock(&ipcdev.gcr_lock);
+
+       ret = is_gcr_valid(offset);
+       if (ret < 0) {
+               spin_unlock(&ipcdev.gcr_lock);
+               return ret;
+       }
+
+       *data = readq(ipcdev.gcr_mem_base + offset);
+
+       spin_unlock(&ipcdev.gcr_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
+
 /**
  * intel_pmc_gcr_write() - Write PMC GCR register
  * @offset:    offset of GCR register from GCR address base
index 4249e8267bbcfc321bd83ac678c20de8f967b584..ffd0474b0531116a308343514950684a250b0b77 100644 (file)
@@ -23,7 +23,6 @@
  */
 #include <linux/debugfs.h>
 #include <linux/device.h>
-#include <linux/io.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/seq_file.h>
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
 #include <asm/intel_pmc_ipc.h>
-#include <asm/intel_punit_ipc.h>
 #include <asm/intel_telemetry.h>
 
-#define DRIVER_NAME    "telemetry_soc_debugfs"
-#define DRIVER_VERSION "1.0.0"
+#define DRIVER_NAME                    "telemetry_soc_debugfs"
+#define DRIVER_VERSION                 "1.0.0"
 
 /* ApolloLake SoC Event-IDs */
 #define TELEM_APL_PSS_PSTATES_ID       0x2802
@@ -98,10 +96,6 @@ static u32 suspend_shlw_ctr_temp, suspend_deep_ctr_temp;
 static u64 suspend_shlw_res_temp, suspend_deep_res_temp;
 
 struct telemetry_susp_stats {
-       u32 shlw_swake_ctr;
-       u32 deep_swake_ctr;
-       u64 shlw_swake_res;
-       u64 deep_swake_res;
        u32 shlw_ctr;
        u32 deep_ctr;
        u64 shlw_res;
@@ -250,7 +244,6 @@ static struct telem_ioss_pg_info telem_apl_ioss_pg_data[] = {
        {"PRTC",        25},
 };
 
-
 struct telemetry_debugfs_conf {
        struct telemetry_susp_stats suspend_stats;
        struct dentry *telemetry_dbg_dir;
@@ -385,7 +378,6 @@ static int telem_pss_states_show(struct seq_file *s, void *unused)
                        TELEM_APL_MASK_PCS_STATE;
                }
 
-
                TELEM_CHECK_AND_PARSE_EVTS(conf->pss_idle_id,
                                           conf->pss_idle_evts - 1,
                                           pss_idle, evtlog[index].telem_evtlog,
@@ -405,7 +397,6 @@ static int telem_pss_states_show(struct seq_file *s, void *unused)
                                           conf->pcs_s0ix_blkd_data,
                                           TELEM_MASK_BYTE);
 
-
                TELEM_CHECK_AND_PARSE_EVTS(conf->pss_wakeup_id,
                                           conf->pss_wakeup_evts,
                                           pss_s0ix_wakeup,
@@ -498,7 +489,6 @@ static const struct file_operations telem_pss_ops = {
        .release        = single_release,
 };
 
-
 static int telem_ioss_states_show(struct seq_file *s, void *unused)
 {
        struct telemetry_evtlog evtlog[TELEM_MAX_OS_ALLOCATED_EVENTS];
@@ -598,19 +588,15 @@ static int telem_soc_states_show(struct seq_file *s, void *unused)
 
        seq_printf(s, "S0IX Shallow\t\t\t %10u\t %10llu\n",
                   s0ix_shlw_ctr -
-                  conf->suspend_stats.shlw_ctr -
-                  conf->suspend_stats.shlw_swake_ctr,
+                  conf->suspend_stats.shlw_ctr,
                   (u64)((s0ix_shlw_res -
-                  conf->suspend_stats.shlw_res -
-                  conf->suspend_stats.shlw_swake_res)*10/192));
+                  conf->suspend_stats.shlw_res)*10/192));
 
        seq_printf(s, "S0IX Deep\t\t\t %10u\t %10llu\n",
                   s0ix_deep_ctr -
-                  conf->suspend_stats.deep_ctr -
-                  conf->suspend_stats.deep_swake_ctr,
+                  conf->suspend_stats.deep_ctr,
                   (u64)((s0ix_deep_res -
-                  conf->suspend_stats.deep_res -
-                  conf->suspend_stats.deep_swake_res)*10/192));
+                  conf->suspend_stats.deep_res)*10/192));
 
        seq_printf(s, "Suspend(With S0ixShallow)\t %10u\t %10llu\n",
                   conf->suspend_stats.shlw_ctr,
@@ -620,14 +606,8 @@ static int telem_soc_states_show(struct seq_file *s, void *unused)
                   conf->suspend_stats.deep_ctr,
                   (u64)(conf->suspend_stats.deep_res*10)/192);
 
-       seq_printf(s, "Suspend(With Shallow-Wakes)\t %10u\t %10llu\n",
-                  conf->suspend_stats.shlw_swake_ctr +
-                  conf->suspend_stats.deep_swake_ctr,
-                  (u64)((conf->suspend_stats.shlw_swake_res +
-                  conf->suspend_stats.deep_swake_res)*10/192));
-
-       seq_printf(s, "S0IX+Suspend Total\t\t %10u\t %10llu\n", s0ix_total_ctr,
-                               (u64)(s0ix_total_res*10/192));
+       seq_printf(s, "TOTAL S0IX\t\t\t %10u\t %10llu\n", s0ix_total_ctr,
+                  (u64)(s0ix_total_res*10/192));
        seq_puts(s, "\n-------------------------------------------------\n");
        seq_puts(s, "\t\tDEVICE STATES\n");
        seq_puts(s, "-------------------------------------------------\n");
@@ -772,7 +752,6 @@ static const struct file_operations telem_pss_trc_verb_ops = {
        .release        = single_release,
 };
 
-
 static int telem_ioss_trc_verb_show(struct seq_file *s, void *unused)
 {
        u32 verbosity;
@@ -890,28 +869,45 @@ static int pm_suspend_exit_cb(void)
                goto out;
        }
 
+       /*
+        * Due to some design limitations in the firmware, sometimes the
+        * counters do not get updated by the time we reach here. As a
+        * workaround, we try to see if this was a genuine case of sleep
+        * failure or not by cross-checking from PMC GCR registers directly.
+        */
+       if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
+           suspend_deep_ctr_exit == suspend_deep_ctr_temp) {
+               ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG,
+                                         &suspend_shlw_res_exit);
+               if (ret < 0)
+                       goto out;
+
+               ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG,
+                                         &suspend_deep_res_exit);
+               if (ret < 0)
+                       goto out;
+
+               if (suspend_shlw_res_exit > suspend_shlw_res_temp)
+                       suspend_shlw_ctr_exit++;
+
+               if (suspend_deep_res_exit > suspend_deep_res_temp)
+                       suspend_deep_ctr_exit++;
+       }
+
        suspend_shlw_ctr_exit -= suspend_shlw_ctr_temp;
        suspend_deep_ctr_exit -= suspend_deep_ctr_temp;
        suspend_shlw_res_exit -= suspend_shlw_res_temp;
        suspend_deep_res_exit -= suspend_deep_res_temp;
 
-       if (suspend_shlw_ctr_exit == 1) {
+       if (suspend_shlw_ctr_exit != 0) {
                conf->suspend_stats.shlw_ctr +=
                suspend_shlw_ctr_exit;
 
                conf->suspend_stats.shlw_res +=
                suspend_shlw_res_exit;
        }
-       /* Shallow Wakes Case */
-       else if (suspend_shlw_ctr_exit > 1) {
-               conf->suspend_stats.shlw_swake_ctr +=
-               suspend_shlw_ctr_exit;
 
-               conf->suspend_stats.shlw_swake_res +=
-               suspend_shlw_res_exit;
-       }
-
-       if (suspend_deep_ctr_exit == 1) {
+       if (suspend_deep_ctr_exit != 0) {
                conf->suspend_stats.deep_ctr +=
                suspend_deep_ctr_exit;
 
@@ -919,15 +915,6 @@ static int pm_suspend_exit_cb(void)
                suspend_deep_res_exit;
        }
 
-       /* Shallow Wakes Case */
-       else if (suspend_deep_ctr_exit > 1) {
-               conf->suspend_stats.deep_swake_ctr +=
-               suspend_deep_ctr_exit;
-
-               conf->suspend_stats.deep_swake_res +=
-               suspend_deep_res_exit;
-       }
-
 out:
        suspend_prep_ok = 0;
        return NOTIFY_OK;
index 504256c3660d84e15444d265ca1fd9747f4fc0dc..27de29961f5e194c990ba3f6427aac09ede9e6f1 100644 (file)
 #include <linux/dmi.h>
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
+#include <linux/io.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/i2c-mux-reg.h>
-#include <linux/platform_data/mlxcpld-hotplug.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/regmap.h>
 
 #define MLX_PLAT_DEVICE_NAME           "mlxplat"
 
 /* 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_REG_AGGR_OFFSET       0x3a
+#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET  0x3b
+#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET     0x40
+#define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET        0x41
+#define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET                0x58
+#define MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET  0x59
+#define MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET   0x5a
+#define MLXPLAT_CPLD_LPC_REG_PWR_OFFSET                0x64
+#define MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET  0x65
+#define MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET   0x66
+#define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET                0x88
+#define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET  0x89
+#define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET   0x8a
 #define MLXPLAT_CPLD_LPC_IO_RANGE              0x100
 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF           0xdb
 #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF           0xda
@@ -81,6 +92,7 @@
 /* mlxplat_priv - platform private data
  * @pdev_i2c - i2c controller platform device
  * @pdev_mux - array of mux platform devices
+ * @pdev_hotplug - hotplug platform devices
  */
 struct mlxplat_priv {
        struct platform_device *pdev_i2c;
@@ -138,86 +150,264 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
 };
 
 /* Platform hotplug devices */
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = {
+static struct i2c_board_info mlxplat_mlxcpld_psu[] = {
        {
-               .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) },
-               .bus = 10,
+               I2C_BOARD_INFO("24c02", 0x51),
        },
        {
-               .brdinfo = { I2C_BOARD_INFO("24c02", 0x50) },
-               .bus = 10,
+               I2C_BOARD_INFO("24c02", 0x50),
        },
 };
 
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = {
+static struct i2c_board_info mlxplat_mlxcpld_pwr[] = {
        {
-               .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) },
-               .bus = 10,
+               I2C_BOARD_INFO("dps460", 0x59),
        },
        {
-               .brdinfo = { I2C_BOARD_INFO("dps460", 0x58) },
-               .bus = 10,
+               I2C_BOARD_INFO("dps460", 0x58),
        },
 };
 
-static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = {
+static struct i2c_board_info mlxplat_mlxcpld_fan[] = {
        {
-               .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
-               .bus = 11,
+               I2C_BOARD_INFO("24c32", 0x50),
        },
        {
-               .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
-               .bus = 12,
+               I2C_BOARD_INFO("24c32", 0x50),
        },
        {
-               .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
-               .bus = 13,
+               I2C_BOARD_INFO("24c32", 0x50),
        },
        {
-               .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
-               .bus = 14,
+               I2C_BOARD_INFO("24c32", 0x50),
        },
 };
 
 /* Platform hotplug default data */
+static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = {
+       {
+               .label = "psu1",
+               .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
+               .mask = BIT(0),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
+               .hpdev.nr = 10,
+       },
+       {
+               .label = "psu2",
+               .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
+               .mask = BIT(1),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
+               .hpdev.nr = 10,
+       },
+};
+
+static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
+       {
+               .label = "pwr1",
+               .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
+               .mask = BIT(0),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
+               .hpdev.nr = 10,
+       },
+       {
+               .label = "pwr2",
+               .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
+               .mask = BIT(1),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
+               .hpdev.nr = 10,
+       },
+};
+
+static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
+       {
+               .label = "fan1",
+               .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
+               .mask = BIT(0),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_fan[0],
+               .hpdev.nr = 11,
+       },
+       {
+               .label = "fan2",
+               .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
+               .mask = BIT(1),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_fan[1],
+               .hpdev.nr = 12,
+       },
+       {
+               .label = "fan3",
+               .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
+               .mask = BIT(2),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_fan[2],
+               .hpdev.nr = 13,
+       },
+       {
+               .label = "fan4",
+               .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
+               .mask = BIT(3),
+               .hpdev.brdinfo = &mlxplat_mlxcpld_fan[3],
+               .hpdev.nr = 14,
+       },
+};
+
+static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
+       {
+               .data = mlxplat_mlxcpld_default_psu_items_data,
+               .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
+               .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
+               .mask = MLXPLAT_CPLD_PSU_MASK,
+               .count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
+               .inversed = 1,
+               .health = false,
+       },
+       {
+               .data = mlxplat_mlxcpld_default_pwr_items_data,
+               .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
+               .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
+               .mask = MLXPLAT_CPLD_PWR_MASK,
+               .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
+               .inversed = 0,
+               .health = false,
+       },
+       {
+               .data = mlxplat_mlxcpld_default_fan_items_data,
+               .aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
+               .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
+               .mask = MLXPLAT_CPLD_FAN_MASK,
+               .count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
+               .inversed = 1,
+               .health = false,
+       },
+};
+
 static
-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,
+struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = {
+       .items = mlxplat_mlxcpld_default_items,
+       .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items),
+       .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
+       .mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
 };
 
 /* Platform hotplug MSN21xx system family data */
+static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = {
+       {
+               .data = mlxplat_mlxcpld_default_pwr_items_data,
+               .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
+               .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
+               .mask = MLXPLAT_CPLD_PWR_MASK,
+               .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
+               .inversed = 0,
+               .health = false,
+       },
+};
+
 static
-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),
+struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
+       .items = mlxplat_mlxcpld_msn21xx_items,
+       .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items),
+       .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
+       .mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
+};
+
+static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
+               return true;
+       }
+       return false;
+}
+
+static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
+               return true;
+       }
+       return false;
+}
+
+static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
+       case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
+               return true;
+       }
+       return false;
+}
+
+struct mlxplat_mlxcpld_regmap_context {
+       void __iomem *base;
+};
+
+static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx;
+
+static int
+mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct mlxplat_mlxcpld_regmap_context *ctx = context;
+
+       *val = ioread8(ctx->base + reg);
+       return 0;
+}
+
+static int
+mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct mlxplat_mlxcpld_regmap_context *ctx = context;
+
+       iowrite8(val, ctx->base + reg);
+       return 0;
+}
+
+static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 255,
+       .cache_type = REGCACHE_FLAT,
+       .writeable_reg = mlxplat_mlxcpld_writeable_reg,
+       .readable_reg = mlxplat_mlxcpld_readable_reg,
+       .volatile_reg = mlxplat_mlxcpld_volatile_reg,
+       .reg_read = mlxplat_mlxcpld_reg_read,
+       .reg_write = mlxplat_mlxcpld_reg_write,
 };
 
 static struct resource mlxplat_mlxcpld_resources[] = {
-       [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
+       [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
 };
 
 static struct platform_device *mlxplat_dev;
-static struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
+static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
 
 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
 {
@@ -286,6 +476,8 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
        { }
 };
 
+MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
+
 static int __init mlxplat_init(void)
 {
        struct mlxplat_priv *priv;
@@ -328,8 +520,23 @@ static int __init mlxplat_init(void)
                }
        }
 
+       mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
+                              mlxplat_lpc_resources[1].start, 1);
+       if (!mlxplat_mlxcpld_regmap_ctx.base) {
+               err = -ENOMEM;
+               goto fail_platform_mux_register;
+       }
+
+       mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
+                                       &mlxplat_mlxcpld_regmap_ctx,
+                                       &mlxplat_mlxcpld_regmap_config);
+       if (IS_ERR(mlxplat_hotplug->regmap)) {
+               err = PTR_ERR(mlxplat_hotplug->regmap);
+               goto fail_platform_mux_register;
+       }
+
        priv->pdev_hotplug = platform_device_register_resndata(
-                               &mlxplat_dev->dev, "mlxcpld-hotplug",
+                               &mlxplat_dev->dev, "mlxreg-hotplug",
                                PLATFORM_DEVID_NONE,
                                mlxplat_mlxcpld_resources,
                                ARRAY_SIZE(mlxplat_mlxcpld_resources),
@@ -339,8 +546,16 @@ static int __init mlxplat_init(void)
                goto fail_platform_mux_register;
        }
 
+       /* Sync registers with hardware. */
+       regcache_mark_dirty(mlxplat_hotplug->regmap);
+       err = regcache_sync(mlxplat_hotplug->regmap);
+       if (err)
+               goto fail_platform_hotplug_register;
+
        return 0;
 
+fail_platform_hotplug_register:
+       platform_device_unregister(priv->pdev_hotplug);
 fail_platform_mux_register:
        while (--i >= 0)
                platform_device_unregister(priv->pdev_mux[i]);
@@ -370,8 +585,3 @@ module_exit(mlxplat_exit);
 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
 MODULE_DESCRIPTION("Mellanox platform driver");
 MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:");
-MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:");
-MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:");
-MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:");
-MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:");
diff --git a/drivers/platform/x86/mlxcpld-hotplug.c b/drivers/platform/x86/mlxcpld-hotplug.c
deleted file mode 100644 (file)
index aff3686..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * drivers/platform/x86/mlxcpld-hotplug.c
- * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright holders nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <linux/bitops.h>
-#include <linux/device.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_data/mlxcpld-hotplug.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
-
-/* Offset of event and mask registers from status register */
-#define MLXCPLD_HOTPLUG_EVENT_OFF      1
-#define MLXCPLD_HOTPLUG_MASK_OFF       2
-#define MLXCPLD_HOTPLUG_AGGR_MASK_OFF  1
-
-#define MLXCPLD_HOTPLUG_ATTRS_NUM      8
-
-/**
- * enum mlxcpld_hotplug_attr_type - sysfs attributes for hotplug events:
- * @MLXCPLD_HOTPLUG_ATTR_TYPE_PSU: power supply unit attribute;
- * @MLXCPLD_HOTPLUG_ATTR_TYPE_PWR: power cable attribute;
- * @MLXCPLD_HOTPLUG_ATTR_TYPE_FAN: FAN drawer attribute;
- */
-enum mlxcpld_hotplug_attr_type {
-       MLXCPLD_HOTPLUG_ATTR_TYPE_PSU,
-       MLXCPLD_HOTPLUG_ATTR_TYPE_PWR,
-       MLXCPLD_HOTPLUG_ATTR_TYPE_FAN,
-};
-
-/**
- * struct mlxcpld_hotplug_priv_data - platform private data:
- * @irq: platform interrupt number;
- * @pdev: platform device;
- * @plat: platform data;
- * @hwmon: hwmon device;
- * @mlxcpld_hotplug_attr: sysfs attributes array;
- * @mlxcpld_hotplug_dev_attr: sysfs sensor device attribute array;
- * @group: sysfs attribute group;
- * @groups: list of sysfs attribute group for hwmon registration;
- * @dwork: delayed work template;
- * @lock: spin lock;
- * @aggr_cache: last value of aggregation register status;
- * @psu_cache: last value of PSU register status;
- * @pwr_cache: last value of power register status;
- * @fan_cache: last value of FAN register status;
- */
-struct mlxcpld_hotplug_priv_data {
-       int irq;
-       struct platform_device *pdev;
-       struct mlxcpld_hotplug_platform_data *plat;
-       struct device *hwmon;
-       struct attribute *mlxcpld_hotplug_attr[MLXCPLD_HOTPLUG_ATTRS_NUM + 1];
-       struct sensor_device_attribute_2
-                       mlxcpld_hotplug_dev_attr[MLXCPLD_HOTPLUG_ATTRS_NUM];
-       struct attribute_group group;
-       const struct attribute_group *groups[2];
-       struct delayed_work dwork;
-       spinlock_t lock;
-       u8 aggr_cache;
-       u8 psu_cache;
-       u8 pwr_cache;
-       u8 fan_cache;
-};
-
-static ssize_t mlxcpld_hotplug_attr_show(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct mlxcpld_hotplug_priv_data *priv = platform_get_drvdata(pdev);
-       int index = to_sensor_dev_attr_2(attr)->index;
-       int nr = to_sensor_dev_attr_2(attr)->nr;
-       u8 reg_val = 0;
-
-       switch (nr) {
-       case MLXCPLD_HOTPLUG_ATTR_TYPE_PSU:
-               /* Bit = 0 : PSU is present. */
-               reg_val = !!!(inb(priv->plat->psu_reg_offset) & BIT(index));
-               break;
-
-       case MLXCPLD_HOTPLUG_ATTR_TYPE_PWR:
-               /* Bit = 1 : power cable is attached. */
-               reg_val = !!(inb(priv->plat->pwr_reg_offset) & BIT(index %
-                                               priv->plat->pwr_count));
-               break;
-
-       case MLXCPLD_HOTPLUG_ATTR_TYPE_FAN:
-               /* Bit = 0 : FAN is present. */
-               reg_val = !!!(inb(priv->plat->fan_reg_offset) & BIT(index %
-                                               priv->plat->fan_count));
-               break;
-       }
-
-       return sprintf(buf, "%u\n", reg_val);
-}
-
-#define PRIV_ATTR(i) priv->mlxcpld_hotplug_attr[i]
-#define PRIV_DEV_ATTR(i) priv->mlxcpld_hotplug_dev_attr[i]
-static int mlxcpld_hotplug_attr_init(struct mlxcpld_hotplug_priv_data *priv)
-{
-       int num_attrs = priv->plat->psu_count + priv->plat->pwr_count +
-                       priv->plat->fan_count;
-       int i;
-
-       priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs *
-                                        sizeof(struct attribute *),
-                                        GFP_KERNEL);
-       if (!priv->group.attrs)
-               return -ENOMEM;
-
-       for (i = 0; i < num_attrs; i++) {
-               PRIV_ATTR(i) = &PRIV_DEV_ATTR(i).dev_attr.attr;
-
-               if (i < priv->plat->psu_count) {
-                       PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev,
-                                               GFP_KERNEL, "psu%u", i + 1);
-                       PRIV_DEV_ATTR(i).nr = MLXCPLD_HOTPLUG_ATTR_TYPE_PSU;
-               } else if (i < priv->plat->psu_count + priv->plat->pwr_count) {
-                       PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev,
-                                               GFP_KERNEL, "pwr%u", i %
-                                               priv->plat->pwr_count + 1);
-                       PRIV_DEV_ATTR(i).nr = MLXCPLD_HOTPLUG_ATTR_TYPE_PWR;
-               } else {
-                       PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev,
-                                               GFP_KERNEL, "fan%u", i %
-                                               priv->plat->fan_count + 1);
-                       PRIV_DEV_ATTR(i).nr = MLXCPLD_HOTPLUG_ATTR_TYPE_FAN;
-               }
-
-               if (!PRIV_ATTR(i)->name) {
-                       dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n",
-                               i + 1);
-                       return -ENOMEM;
-               }
-
-               PRIV_DEV_ATTR(i).dev_attr.attr.name = PRIV_ATTR(i)->name;
-               PRIV_DEV_ATTR(i).dev_attr.attr.mode = S_IRUGO;
-               PRIV_DEV_ATTR(i).dev_attr.show = mlxcpld_hotplug_attr_show;
-               PRIV_DEV_ATTR(i).index = i;
-               sysfs_attr_init(&PRIV_DEV_ATTR(i).dev_attr.attr);
-       }
-
-       priv->group.attrs = priv->mlxcpld_hotplug_attr;
-       priv->groups[0] = &priv->group;
-       priv->groups[1] = NULL;
-
-       return 0;
-}
-
-static int mlxcpld_hotplug_device_create(struct device *dev,
-                                        struct mlxcpld_hotplug_device *item)
-{
-       item->adapter = i2c_get_adapter(item->bus);
-       if (!item->adapter) {
-               dev_err(dev, "Failed to get adapter for bus %d\n",
-                       item->bus);
-               return -EFAULT;
-       }
-
-       item->client = i2c_new_device(item->adapter, &item->brdinfo);
-       if (!item->client) {
-               dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
-                       item->brdinfo.type, item->bus, item->brdinfo.addr);
-               i2c_put_adapter(item->adapter);
-               item->adapter = NULL;
-               return -EFAULT;
-       }
-
-       return 0;
-}
-
-static void mlxcpld_hotplug_device_destroy(struct mlxcpld_hotplug_device *item)
-{
-       if (item->client) {
-               i2c_unregister_device(item->client);
-               item->client = NULL;
-       }
-
-       if (item->adapter) {
-               i2c_put_adapter(item->adapter);
-               item->adapter = NULL;
-       }
-}
-
-static inline void
-mlxcpld_hotplug_work_helper(struct device *dev,
-                           struct mlxcpld_hotplug_device *item, u8 is_inverse,
-                           u16 offset, u8 mask, u8 *cache)
-{
-       u8 val, asserted;
-       int bit;
-
-       /* Mask event. */
-       outb(0, offset + MLXCPLD_HOTPLUG_MASK_OFF);
-       /* Read status. */
-       val = inb(offset) & mask;
-       asserted = *cache ^ val;
-       *cache = val;
-
-       /*
-        * Validate if item related to received signal type is valid.
-        * It should never happen, excepted the situation when some
-        * piece of hardware is broken. In such situation just produce
-        * error message and return. Caller must continue to handle the
-        * signals from other devices if any.
-        */
-       if (unlikely(!item)) {
-               dev_err(dev, "False signal is received: register at offset 0x%02x, mask 0x%02x.\n",
-                       offset, mask);
-               return;
-       }
-
-       for_each_set_bit(bit, (unsigned long *)&asserted, 8) {
-               if (val & BIT(bit)) {
-                       if (is_inverse)
-                               mlxcpld_hotplug_device_destroy(item + bit);
-                       else
-                               mlxcpld_hotplug_device_create(dev, item + bit);
-               } else {
-                       if (is_inverse)
-                               mlxcpld_hotplug_device_create(dev, item + bit);
-                       else
-                               mlxcpld_hotplug_device_destroy(item + bit);
-               }
-       }
-
-       /* Acknowledge event. */
-       outb(0, offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-       /* Unmask event. */
-       outb(mask, offset + MLXCPLD_HOTPLUG_MASK_OFF);
-}
-
-/*
- * mlxcpld_hotplug_work_handler - performs traversing of CPLD interrupt
- * registers according to the below hierarchy schema:
- *
- *                   Aggregation registers (status/mask)
- * PSU registers:           *---*
- * *-----------------*      |   |
- * |status/event/mask|----->| * |
- * *-----------------*      |   |
- * Power registers:         |   |
- * *-----------------*      |   |
- * |status/event/mask|----->| * |---> CPU
- * *-----------------*      |   |
- * FAN registers:
- * *-----------------*      |   |
- * |status/event/mask|----->| * |
- * *-----------------*      |   |
- *                          *---*
- * In case some system changed are detected: FAN in/out, PSU in/out, power
- * cable attached/detached, relevant device is created or destroyed.
- */
-static void mlxcpld_hotplug_work_handler(struct work_struct *work)
-{
-       struct mlxcpld_hotplug_priv_data *priv = container_of(work,
-                               struct mlxcpld_hotplug_priv_data, dwork.work);
-       u8 val, aggr_asserted;
-       unsigned long flags;
-
-       /* Mask aggregation event. */
-       outb(0, priv->plat->top_aggr_offset + MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
-       /* Read aggregation status. */
-       val = inb(priv->plat->top_aggr_offset) & priv->plat->top_aggr_mask;
-       aggr_asserted = priv->aggr_cache ^ val;
-       priv->aggr_cache = val;
-
-       /* Handle PSU configuration changes. */
-       if (aggr_asserted & priv->plat->top_aggr_psu_mask)
-               mlxcpld_hotplug_work_helper(&priv->pdev->dev, priv->plat->psu,
-                                           1, priv->plat->psu_reg_offset,
-                                           priv->plat->psu_mask,
-                                           &priv->psu_cache);
-
-       /* Handle power cable configuration changes. */
-       if (aggr_asserted & priv->plat->top_aggr_pwr_mask)
-               mlxcpld_hotplug_work_helper(&priv->pdev->dev, priv->plat->pwr,
-                                           0, priv->plat->pwr_reg_offset,
-                                           priv->plat->pwr_mask,
-                                           &priv->pwr_cache);
-
-       /* Handle FAN configuration changes. */
-       if (aggr_asserted & priv->plat->top_aggr_fan_mask)
-               mlxcpld_hotplug_work_helper(&priv->pdev->dev, priv->plat->fan,
-                                           1, priv->plat->fan_reg_offset,
-                                           priv->plat->fan_mask,
-                                           &priv->fan_cache);
-
-       if (aggr_asserted) {
-               spin_lock_irqsave(&priv->lock, flags);
-
-               /*
-                * It is possible, that some signals have been inserted, while
-                * interrupt has been masked by mlxcpld_hotplug_work_handler.
-                * In this case such signals will be missed. In order to handle
-                * these signals delayed work is canceled and work task
-                * re-scheduled for immediate execution. It allows to handle
-                * missed signals, if any. In other case work handler just
-                * validates that no new signals have been received during
-                * masking.
-                */
-               cancel_delayed_work(&priv->dwork);
-               schedule_delayed_work(&priv->dwork, 0);
-
-               spin_unlock_irqrestore(&priv->lock, flags);
-
-               return;
-       }
-
-       /* Unmask aggregation event (no need acknowledge). */
-       outb(priv->plat->top_aggr_mask, priv->plat->top_aggr_offset +
-                                               MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
-}
-
-static void mlxcpld_hotplug_set_irq(struct mlxcpld_hotplug_priv_data *priv)
-{
-       /* Clear psu presense event. */
-       outb(0, priv->plat->psu_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-       /* Set psu initial status as mask and unmask psu event. */
-       priv->psu_cache = priv->plat->psu_mask;
-       outb(priv->plat->psu_mask, priv->plat->psu_reg_offset +
-                                               MLXCPLD_HOTPLUG_MASK_OFF);
-
-       /* Clear power cable event. */
-       outb(0, priv->plat->pwr_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-       /* Keep power initial status as zero and unmask power event. */
-       outb(priv->plat->pwr_mask, priv->plat->pwr_reg_offset +
-                                               MLXCPLD_HOTPLUG_MASK_OFF);
-
-       /* Clear fan presense event. */
-       outb(0, priv->plat->fan_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-       /* Set fan initial status as mask and unmask fan event. */
-       priv->fan_cache = priv->plat->fan_mask;
-       outb(priv->plat->fan_mask, priv->plat->fan_reg_offset +
-                                               MLXCPLD_HOTPLUG_MASK_OFF);
-
-       /* Keep aggregation initial status as zero and unmask events. */
-       outb(priv->plat->top_aggr_mask, priv->plat->top_aggr_offset +
-                                               MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
-
-       /* Invoke work handler for initializing hot plug devices setting. */
-       mlxcpld_hotplug_work_handler(&priv->dwork.work);
-
-       enable_irq(priv->irq);
-}
-
-static void mlxcpld_hotplug_unset_irq(struct mlxcpld_hotplug_priv_data *priv)
-{
-       int i;
-
-       disable_irq(priv->irq);
-       cancel_delayed_work_sync(&priv->dwork);
-
-       /* Mask aggregation event. */
-       outb(0, priv->plat->top_aggr_offset + MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
-
-       /* Mask psu presense event. */
-       outb(0, priv->plat->psu_reg_offset + MLXCPLD_HOTPLUG_MASK_OFF);
-       /* Clear psu presense event. */
-       outb(0, priv->plat->psu_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-
-       /* Mask power cable event. */
-       outb(0, priv->plat->pwr_reg_offset + MLXCPLD_HOTPLUG_MASK_OFF);
-       /* Clear power cable event. */
-       outb(0, priv->plat->pwr_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-
-       /* Mask fan presense event. */
-       outb(0, priv->plat->fan_reg_offset + MLXCPLD_HOTPLUG_MASK_OFF);
-       /* Clear fan presense event. */
-       outb(0, priv->plat->fan_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
-
-       /* Remove all the attached devices. */
-       for (i = 0; i < priv->plat->psu_count; i++)
-               mlxcpld_hotplug_device_destroy(priv->plat->psu + i);
-
-       for (i = 0; i < priv->plat->pwr_count; i++)
-               mlxcpld_hotplug_device_destroy(priv->plat->pwr + i);
-
-       for (i = 0; i < priv->plat->fan_count; i++)
-               mlxcpld_hotplug_device_destroy(priv->plat->fan + i);
-}
-
-static irqreturn_t mlxcpld_hotplug_irq_handler(int irq, void *dev)
-{
-       struct mlxcpld_hotplug_priv_data *priv =
-                               (struct mlxcpld_hotplug_priv_data *)dev;
-
-       /* Schedule work task for immediate execution.*/
-       schedule_delayed_work(&priv->dwork, 0);
-
-       return IRQ_HANDLED;
-}
-
-static int mlxcpld_hotplug_probe(struct platform_device *pdev)
-{
-       struct mlxcpld_hotplug_platform_data *pdata;
-       struct mlxcpld_hotplug_priv_data *priv;
-       int err;
-
-       pdata = dev_get_platdata(&pdev->dev);
-       if (!pdata) {
-               dev_err(&pdev->dev, "Failed to get platform data.\n");
-               return -EINVAL;
-       }
-
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->pdev = pdev;
-       priv->plat = pdata;
-
-       priv->irq = platform_get_irq(pdev, 0);
-       if (priv->irq < 0) {
-               dev_err(&pdev->dev, "Failed to get platform irq: %d\n",
-                       priv->irq);
-               return priv->irq;
-       }
-
-       err = devm_request_irq(&pdev->dev, priv->irq,
-                               mlxcpld_hotplug_irq_handler, 0, pdev->name,
-                               priv);
-       if (err) {
-               dev_err(&pdev->dev, "Failed to request irq: %d\n", err);
-               return err;
-       }
-       disable_irq(priv->irq);
-
-       INIT_DELAYED_WORK(&priv->dwork, mlxcpld_hotplug_work_handler);
-       spin_lock_init(&priv->lock);
-
-       err = mlxcpld_hotplug_attr_init(priv);
-       if (err) {
-               dev_err(&pdev->dev, "Failed to allocate attributes: %d\n", err);
-               return err;
-       }
-
-       priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
-                                       "mlxcpld_hotplug", priv, priv->groups);
-       if (IS_ERR(priv->hwmon)) {
-               dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
-                       PTR_ERR(priv->hwmon));
-               return PTR_ERR(priv->hwmon);
-       }
-
-       platform_set_drvdata(pdev, priv);
-
-       /* Perform initial interrupts setup. */
-       mlxcpld_hotplug_set_irq(priv);
-
-       return 0;
-}
-
-static int mlxcpld_hotplug_remove(struct platform_device *pdev)
-{
-       struct mlxcpld_hotplug_priv_data *priv = platform_get_drvdata(pdev);
-
-       /* Clean interrupts setup. */
-       mlxcpld_hotplug_unset_irq(priv);
-
-       return 0;
-}
-
-static struct platform_driver mlxcpld_hotplug_driver = {
-       .driver = {
-               .name = "mlxcpld-hotplug",
-       },
-       .probe = mlxcpld_hotplug_probe,
-       .remove = mlxcpld_hotplug_remove,
-};
-
-module_platform_driver(mlxcpld_hotplug_driver);
-
-MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
-MODULE_DESCRIPTION("Mellanox CPLD hotplug platform driver");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("platform:mlxcpld-hotplug");
index 77bac859342de60b3b8253addbb9998b719bf62a..4b3c37b6288cf0e4bbf4107b87d4fdf63cd131c5 100644 (file)
@@ -208,6 +208,20 @@ static const struct pmc_data cht_data = {
        .clks = cht_clks,
 };
 
+#define DEFINE_SHOW_ATTRIBUTE(__name)                                  \
+static int __name ## _open(struct inode *inode, struct file *file)     \
+{                                                                      \
+       return single_open(file, __name ## _show, inode->i_private);    \
+}                                                                      \
+                                                                       \
+static const struct file_operations __name ## _fops = {                        \
+       .owner          = THIS_MODULE,                                  \
+       .open           = __name ## _open,                              \
+       .read           = seq_read,                                     \
+       .llseek         = seq_lseek,                                    \
+       .release        = single_release,                               \
+}
+
 static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
 {
        return readl(pmc->regmap + reg_offset);
@@ -309,17 +323,7 @@ static int pmc_dev_state_show(struct seq_file *s, void *unused)
        return 0;
 }
 
-static int pmc_dev_state_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, pmc_dev_state_show, inode->i_private);
-}
-
-static const struct file_operations pmc_dev_state_ops = {
-       .open           = pmc_dev_state_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(pmc_dev_state);
 
 static int pmc_pss_state_show(struct seq_file *s, void *unused)
 {
@@ -336,17 +340,7 @@ static int pmc_pss_state_show(struct seq_file *s, void *unused)
        return 0;
 }
 
-static int pmc_pss_state_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, pmc_pss_state_show, inode->i_private);
-}
-
-static const struct file_operations pmc_pss_state_ops = {
-       .open           = pmc_pss_state_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(pmc_pss_state);
 
 static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
 {
@@ -367,17 +361,7 @@ static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
        return 0;
 }
 
-static int pmc_sleep_tmr_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, pmc_sleep_tmr_show, inode->i_private);
-}
-
-static const struct file_operations pmc_sleep_tmr_ops = {
-       .open           = pmc_sleep_tmr_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(pmc_sleep_tmr);
 
 static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
 {
@@ -395,17 +379,17 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc)
        pmc->dbgfs_dir = dir;
 
        f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
-                               dir, pmc, &pmc_dev_state_ops);
+                               dir, pmc, &pmc_dev_state_fops);
        if (!f)
                goto err;
 
        f = debugfs_create_file("pss_state", S_IFREG | S_IRUGO,
-                               dir, pmc, &pmc_pss_state_ops);
+                               dir, pmc, &pmc_pss_state_fops);
        if (!f)
                goto err;
 
        f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
-                               dir, pmc, &pmc_sleep_tmr_ops);
+                               dir, pmc, &pmc_sleep_tmr_fops);
        if (!f)
                goto err;
 
index 266535c2a72f21dc7309962542bd71b68fe9b98f..3a624090191dd1ed0314b44b9a8c949aec8f70ac 100644 (file)
@@ -67,6 +67,21 @@ static const struct silead_ts_dmi_data dexp_ursus_7w_data = {
        .properties     = dexp_ursus_7w_props,
 };
 
+static const struct property_entry surftab_twin_10_1_st10432_8_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1900),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
+       PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1),
+       PROPERTY_ENTRY_STRING("firmware-name",
+                             "gsl3670-surftab-twin-10-1-st10432-8.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       { }
+};
+
+static const struct silead_ts_dmi_data surftab_twin_10_1_st10432_8_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = surftab_twin_10_1_st10432_8_props,
+};
+
 static const struct property_entry surftab_wintron70_st70416_6_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-size-x", 884),
        PROPERTY_ENTRY_U32("touchscreen-size-y", 632),
@@ -171,6 +186,97 @@ static const struct silead_ts_dmi_data digma_citi_e200_data = {
        .properties     = digma_citi_e200_props,
 };
 
+static const struct property_entry onda_obook_20_plus_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-obook-20-plus.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct silead_ts_dmi_data onda_obook_20_plus_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = onda_obook_20_plus_props,
+};
+
+static const struct property_entry chuwi_hi8_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1665),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"),
+       { }
+};
+
+static const struct silead_ts_dmi_data chuwi_hi8_data = {
+       .acpi_name      = "MSSL0001:00",
+       .properties     = chuwi_hi8_props,
+};
+
+static const struct property_entry chuwi_vi8_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1724),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-vi8.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct silead_ts_dmi_data chuwi_vi8_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = chuwi_vi8_props,
+};
+
+static const struct property_entry trekstor_primebook_c13_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 2624),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1920),
+       PROPERTY_ENTRY_STRING("firmware-name",
+                             "gsl1680-trekstor-primebook-c13.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct silead_ts_dmi_data trekstor_primebook_c13_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = trekstor_primebook_c13_props,
+};
+
+static const struct property_entry teclast_x98plus2_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 2048),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+       PROPERTY_ENTRY_STRING("firmware-name",
+                             "gsl1686-teclast_x98plus2.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       { }
+};
+
+static const struct silead_ts_dmi_data teclast_x98plus2_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = teclast_x98plus2_props,
+};
+
+static const struct property_entry teclast_x3_plus_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-teclast-x3-plus.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct silead_ts_dmi_data teclast_x3_plus_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = teclast_x3_plus_props,
+};
+
 static const struct dmi_system_id silead_ts_dmi_table[] = {
        {
                /* CUBE iwork8 Air */
@@ -198,6 +304,14 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "7W"),
                },
        },
+       {
+               /* TrekStor SurfTab twin 10.1 ST10432-8 */
+               .driver_data = (void *)&surftab_twin_10_1_st10432_8_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TrekStor"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SurfTab twin 10.1"),
+               },
+       },
        {
                /* Trekstor Surftab Wintron 7.0 ST70416-6 */
                .driver_data = (void *)&surftab_wintron70_st70416_6_data,
@@ -208,6 +322,17 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
                        DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA04"),
                },
        },
+       {
+               /* Trekstor Surftab Wintron 7.0 ST70416-6, newer BIOS */
+               .driver_data = (void *)&surftab_wintron70_st70416_6_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TrekStor"),
+                       DMI_MATCH(DMI_PRODUCT_NAME,
+                                            "SurfTab wintron 7.0 ST70416-6"),
+                       /* Exact match, different versions need different fw */
+                       DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA05"),
+               },
+       },
        {
                /* Ployer Momo7w (same hardware as the Trekstor ST70416-6) */
                .driver_data = (void *)&surftab_wintron70_st70416_6_data,
@@ -271,6 +396,56 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
                        DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
                },
        },
+       {
+               /* Onda oBook 20 Plus */
+               .driver_data = (void *)&onda_obook_20_plus_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ONDA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OBOOK 20 PLUS"),
+               },
+       },
+       {
+               /* Chuwi Hi8 */
+               .driver_data = (void *)&chuwi_hi8_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ilife"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "S806"),
+               },
+       },
+       {
+               /* Chuwi Vi8 (CWI506) */
+               .driver_data = (void *)&chuwi_vi8_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "i86"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"),
+               },
+       },
+       {
+               /* Trekstor Primebook C13 */
+               .driver_data = (void *)&trekstor_primebook_c13_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Primebook C13"),
+               },
+       },
+       {
+               /* Teclast X98 Plus II */
+               .driver_data = (void *)&teclast_x98plus2_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"),
+               },
+       },
+       {
+               /* Teclast X3 Plus */
+               .driver_data = (void *)&teclast_x3_plus_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X3 Plus"),
+                       DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"),
+               },
+       },
        { },
 };
 
index 117be48ff4de9a3a70df898565cf6495e92d7ddd..d5eaf3b1edba91d53723fddd0c2b49125fd64b9d 100644 (file)
@@ -214,6 +214,10 @@ enum tpacpi_hkey_event_t {
        /* AC-related events */
        TP_HKEY_EV_AC_CHANGED           = 0x6040, /* AC status changed */
 
+       /* Further user-interface events */
+       TP_HKEY_EV_PALM_DETECTED        = 0x60b0, /* palm hoveres keyboard */
+       TP_HKEY_EV_PALM_UNDETECTED      = 0x60b1, /* palm removed */
+
        /* Misc */
        TP_HKEY_EV_RFKILL_CHANGED       = 0x7000, /* rfkill switch changed */
 };
@@ -2113,12 +2117,10 @@ static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode)
                              TP_ACPI_MULTI_MODE_FLAT;
                break;
        case 4:
-               valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
-                             TP_ACPI_MULTI_MODE_TABLET |
-                             TP_ACPI_MULTI_MODE_STAND |
-                             TP_ACPI_MULTI_MODE_TENT;
-               break;
        case 5:
+               /* In mode 4, FLAT is not specified as a valid mode. However,
+                * it can be seen at least on the X1 Yoga 2nd Generation.
+                */
                valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
                              TP_ACPI_MULTI_MODE_FLAT |
                              TP_ACPI_MULTI_MODE_TABLET |
@@ -4079,6 +4081,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
                *send_acpi_ev = false;
                break;
 
+       case TP_HKEY_EV_PALM_DETECTED:
+       case TP_HKEY_EV_PALM_UNDETECTED:
+               /* palm detected hovering the keyboard, forward to user-space
+                * via netlink for consumption */
+               return true;
+
        default:
                pr_warn("unknown possible thermal alarm or keyboard event received\n");
                known = false;
diff --git a/include/linux/platform_data/mlxcpld-hotplug.h b/include/linux/platform_data/mlxcpld-hotplug.h
deleted file mode 100644 (file)
index e4cfcff..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * include/linux/platform_data/mlxcpld-hotplug.h
- * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright holders nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef __LINUX_PLATFORM_DATA_MLXCPLD_HOTPLUG_H
-#define __LINUX_PLATFORM_DATA_MLXCPLD_HOTPLUG_H
-
-/**
- * struct mlxcpld_hotplug_device - I2C device data:
- * @adapter: I2C device adapter;
- * @client: I2C device client;
- * @brdinfo: device board information;
- * @bus: I2C bus, where device is attached;
- *
- * Structure represents I2C hotplug device static data (board topology) and
- * dynamic data (related kernel objects handles).
- */
-struct mlxcpld_hotplug_device {
-       struct i2c_adapter *adapter;
-       struct i2c_client *client;
-       struct i2c_board_info brdinfo;
-       u16 bus;
-};
-
-/**
- * struct mlxcpld_hotplug_platform_data - device platform data:
- * @top_aggr_offset: offset of top aggregation interrupt register;
- * @top_aggr_mask: top aggregation interrupt common mask;
- * @top_aggr_psu_mask: top aggregation interrupt PSU mask;
- * @psu_reg_offset: offset of PSU interrupt register;
- * @psu_mask: PSU interrupt mask;
- * @psu_count: number of equipped replaceable PSUs;
- * @psu: pointer to PSU devices data array;
- * @top_aggr_pwr_mask: top aggregation interrupt power mask;
- * @pwr_reg_offset: offset of power interrupt register
- * @pwr_mask: power interrupt mask;
- * @pwr_count: number of power sources;
- * @pwr: pointer to power devices data array;
- * @top_aggr_fan_mask: top aggregation interrupt FAN mask;
- * @fan_reg_offset: offset of FAN interrupt register;
- * @fan_mask: FAN interrupt mask;
- * @fan_count: number of equipped replaceable FANs;
- * @fan: pointer to FAN devices data array;
- *
- * Structure represents board platform data, related to system hotplug events,
- * like FAN, PSU, power cable insertion and removing. This data provides the
- * number of hot-pluggable devices and hardware description for event handling.
- */
-struct mlxcpld_hotplug_platform_data {
-       u16 top_aggr_offset;
-       u8 top_aggr_mask;
-       u8 top_aggr_psu_mask;
-       u16 psu_reg_offset;
-       u8 psu_mask;
-       u8 psu_count;
-       struct mlxcpld_hotplug_device *psu;
-       u8 top_aggr_pwr_mask;
-       u16 pwr_reg_offset;
-       u8 pwr_mask;
-       u8 pwr_count;
-       struct mlxcpld_hotplug_device *pwr;
-       u8 top_aggr_fan_mask;
-       u16 fan_reg_offset;
-       u8 fan_mask;
-       u8 fan_count;
-       struct mlxcpld_hotplug_device *fan;
-};
-
-#endif /* __LINUX_PLATFORM_DATA_MLXCPLD_HOTPLUG_H */
diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h
new file mode 100644 (file)
index 0000000..fcdc707
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_MLXREG_H
+#define __LINUX_PLATFORM_DATA_MLXREG_H
+
+#define MLXREG_CORE_LABEL_MAX_SIZE     32
+
+/**
+ * struct mlxreg_hotplug_device - I2C device data:
+ *
+ * @adapter: I2C device adapter;
+ * @client: I2C device client;
+ * @brdinfo: device board information;
+ * @nr: I2C device adapter number, to which device is to be attached;
+ *
+ * Structure represents I2C hotplug device static data (board topology) and
+ * dynamic data (related kernel objects handles).
+ */
+struct mlxreg_hotplug_device {
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
+       struct i2c_board_info *brdinfo;
+       int nr;
+};
+
+/**
+ * struct mlxreg_core_data - attributes control data:
+ *
+ * @label: attribute label;
+ * @label: attribute register offset;
+ * @reg: attribute register;
+ * @mask: attribute access mask;
+ * @mode: access mode;
+ * @bit: attribute effective bit;
+ * @np - pointer to node platform associated with attribute;
+ * @hpdev - hotplug device data;
+ * @health_cntr: dynamic device health indication counter;
+ * @attached: true if device has been attached after good health indication;
+ */
+struct mlxreg_core_data {
+       char label[MLXREG_CORE_LABEL_MAX_SIZE];
+       u32 reg;
+       u32 mask;
+       u32 bit;
+       umode_t mode;
+       struct device_node *np;
+       struct mlxreg_hotplug_device hpdev;
+       u8 health_cntr;
+       bool attached;
+};
+
+/**
+ * struct mlxreg_core_item - same type components controlled by the driver:
+ *
+ * @data: component data;
+ * @aggr_mask: group aggregation mask;
+ * @reg: group interrupt status register;
+ * @mask: group interrupt mask;
+ * @cache: last status value for elements fro the same group;
+ * @count: number of available elements in the group;
+ * @ind: element's index inside the group;
+ * @inversed: if 0: 0 for signal status is OK, if 1 - 1 is OK;
+ * @health: true if device has health indication, false in other case;
+ */
+struct mlxreg_core_item {
+       struct mlxreg_core_data *data;
+       u32 aggr_mask;
+       u32 reg;
+       u32 mask;
+       u32 cache;
+       u8 count;
+       u8 ind;
+       u8 inversed;
+       u8 health;
+};
+
+/**
+ * struct mlxreg_core_platform_data - platform data:
+ *
+ * @led_data: led private data;
+ * @regmap: register map of parent device;
+ * @counter: number of led instances;
+ */
+struct mlxreg_core_platform_data {
+       struct mlxreg_core_data *data;
+       void *regmap;
+       int counter;
+};
+
+/**
+ * struct mlxreg_core_hotplug_platform_data - hotplug platform data:
+ *
+ * @items: same type components with the hotplug capability;
+ * @irq: platform interrupt number;
+ * @regmap: register map of parent device;
+ * @counter: number of the components with the hotplug capability;
+ * @cell: location of top aggregation interrupt register;
+ * @mask: top aggregation interrupt common mask;
+ * @cell_low: location of low aggregation interrupt register;
+ * @mask_low: low aggregation interrupt common mask;
+ */
+struct mlxreg_core_hotplug_platform_data {
+       struct mlxreg_core_item *items;
+       int irq;
+       void *regmap;
+       int counter;
+       u32 cell;
+       u32 mask;
+       u32 cell_low;
+       u32 mask_low;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_MLXREG_H */
index 061fa62958a223f5916c8bc0640c2efff991ad49..53fbae27b280c5a3726f9cb0a578156d4d3b2258 100644 (file)
 #define BTN_DPAD_RIGHT         0x223
 
 #define KEY_ALS_TOGGLE         0x230   /* Ambient light sensor */
+#define KEY_ROTATE_LOCK_TOGGLE 0x231   /* Display rotation lock */
 
 #define KEY_BUTTONCONFIG               0x240   /* AL Button Configuration */
 #define KEY_TASKMANAGER                0x241   /* AL Task/Project Manager */