Merge tag 'platform-drivers-x86-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 14 Mar 2024 17:44:09 +0000 (10:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 14 Mar 2024 17:44:09 +0000 (10:44 -0700)
Pull x86 platform driver updates from Ilpo Järvinen:

 - New acer-wmi HW support

 - Support for new revision of amd/pmf heartbeat notify

 - Correctly handle asus-wmi HW without LEDs

 - fujitsu-laptop battery charge control support

 - Support for new hp-wmi thermal profiles

 - Support ideapad-laptop refresh rate key

 - Put intel/pmc AI accelerator (GNA) into D3 if it has no driver to
   allow entry into low-power modes, and temporarily removed Lunar Lake
   SSRAM support due to breaking FW changes causing probe fail (further
   breaking FW changes are still pending)

 - Report pmc/punit_atom devices that prevent reacing low power levels

 - Surface Fan speed function support

 - Support for more sperial keys and complete the list of models with
   non-standard fan registers in thinkpad_acpi

 - New DMI touchscreen HW support

 - Continued modernization efforts of wmi

 - Removal of obsoleted ledtrig-audio call and the related dependency

 - Debug & metrics interface improvements

 - Miscellaneous cleanups / fixes / improvements

* tag 'platform-drivers-x86-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (87 commits)
  platform/x86/intel/pmc: Improve PKGC residency counters debug
  platform/x86: asus-wmi: Consider device is absent when the read is ~0
  Documentation/x86/amd/hsmp: Updating urls
  platform/mellanox: mlxreg-hotplug: Remove redundant NULL-check
  platform/x86/amd/pmf: Update sps power thermals according to the platform-profiles
  platform/x86/amd/pmf: Add support to get sps default APTS index values
  platform/x86/amd/pmf: Add support to get APTS index numbers for static slider
  platform/x86/amd/pmf: Add support to notify sbios heart beat event
  platform/x86/amd/pmf: Add support to get sbios requests in PMF driver
  platform/x86/amd/pmf: Disable debugfs support for querying power thermals
  platform/x86/amd/pmf: Differentiate PMF ACPI versions
  x86/platform/atom: Check state of Punit managed devices on s2idle
  platform/x86: pmc_atom: Check state of PMC clocks on s2idle
  platform/x86: pmc_atom: Check state of PMC managed devices on s2idle
  platform/x86: pmc_atom: Annotate d3_sts register bit defines
  clk: x86: Move clk-pmc-atom register defines to include/linux/platform_data/x86/pmc_atom.h
  platform/x86: make fw_attr_class constant
  platform/x86/intel/tpmi: Change vsec offset to u64
  platform/x86: intel_scu_pcidrv: Remove unused intel-mid.h
  platform/x86: intel_scu_wdt: Remove unused intel-mid.h
  ...

58 files changed:
Documentation/admin-guide/laptops/thinkpad-acpi.rst
Documentation/arch/x86/amd_hsmp.rst
Documentation/wmi/acpi-interface.rst
arch/x86/platform/atom/punit_atom_debug.c
drivers/clk/x86/clk-pmc-atom.c
drivers/hwmon/dell-smm-hwmon.c
drivers/platform/mellanox/mlxbf-bootctl.c
drivers/platform/mellanox/mlxbf-pmc.c
drivers/platform/mellanox/mlxreg-hotplug.c
drivers/platform/surface/surface_aggregator_registry.c
drivers/platform/x86/Kconfig
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/amd/Kconfig
drivers/platform/x86/amd/hsmp.c
drivers/platform/x86/amd/pmf/acpi.c
drivers/platform/x86/amd/pmf/core.c
drivers/platform/x86/amd/pmf/pmf.h
drivers/platform/x86/amd/pmf/sps.c
drivers/platform/x86/amd/pmf/tee-if.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/dell/Kconfig
drivers/platform/x86/dell/dell-laptop.c
drivers/platform/x86/dell/dell-wmi-ddv.c
drivers/platform/x86/dell/dell-wmi-privacy.c
drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
drivers/platform/x86/firmware_attributes_class.c
drivers/platform/x86/firmware_attributes_class.h
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
drivers/platform/x86/hp/hp-wmi.c
drivers/platform/x86/huawei-wmi.c
drivers/platform/x86/ibm_rtl.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel/ifs/load.c
drivers/platform/x86/intel/ifs/runtest.c
drivers/platform/x86/intel/pmc/arl.c
drivers/platform/x86/intel/pmc/core.c
drivers/platform/x86/intel/pmc/core.h
drivers/platform/x86/intel/pmc/lnl.c
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
drivers/platform/x86/intel/tpmi.c
drivers/platform/x86/intel/vsec.c
drivers/platform/x86/intel/wmi/sbl-fw-update.c
drivers/platform/x86/intel/wmi/thunderbolt.c
drivers/platform/x86/intel_scu_ipcutil.c
drivers/platform/x86/intel_scu_pcidrv.c
drivers/platform/x86/intel_scu_wdt.c
drivers/platform/x86/pmc_atom.c
drivers/platform/x86/silicom-platform.c
drivers/platform/x86/think-lmi.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/touchscreen_dmi.c
drivers/platform/x86/wmi-bmof.c
drivers/platform/x86/wmi.c
include/linux/platform_data/x86/pmc_atom.h
include/linux/wmi.h
include/trace/events/intel_ifs.h
include/uapi/linux/input-event-codes.h

index 98d30401017012212797f54627dc6ca4443cd889..7f674a6cfa8a7b2cd143452407b9f27a56fae1e7 100644 (file)
@@ -444,7 +444,9 @@ event       code    Key             Notes
 
 0x1008 0x07    FN+F8           IBM: toggle screen expand
                                Lenovo: configure UltraNav,
-                               or toggle screen expand
+                               or toggle screen expand.
+                               On newer platforms (2024+)
+                               replaced by 0x131f (see below)
 
 0x1009 0x08    FN+F9           -
 
@@ -504,6 +506,9 @@ event       code    Key             Notes
 
 0x1019 0x18    unknown
 
+0x131f ...     FN+F8           Platform Mode change.
+                               Implemented in driver.
+
 ...    ...     ...
 
 0x1020 0x1F    unknown
index c92bfd55359f2eb6654585a5cf33b00a95d815ae..1e499ecf5f4ef4b382617845383e89ef40e38c1e 100644 (file)
@@ -13,7 +13,8 @@ set of mailbox registers.
 
 More details on the interface can be found in chapter
 "7 Host System Management Port (HSMP)" of the family/model PPR
-Eg: https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip
+Eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
+
 
 HSMP interface is supported on EPYC server CPU models only.
 
@@ -97,8 +98,8 @@ what happened. The transaction returns 0 on success.
 
 More details on the interface and message definitions can be found in chapter
 "7 Host System Management Port (HSMP)" of the respective family/model PPR
-eg: https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip
+eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
 
 User space C-APIs are made available by linking against the esmi library,
-which is provided by the E-SMS project https://developer.amd.com/e-sms/.
+which is provided by the E-SMS project https://www.amd.com/en/developer/e-sms.html.
 See: https://github.com/amd/esmi_ib_library
index d31af0ed9c0836c86a6c7f19e929f8087df4e581..06fb7fcf44134ae6ca23c548121127b2a8f0f649 100644 (file)
@@ -93,4 +93,7 @@ _WED ACPI method
 ----------------
 
 Used to retrieve additional WMI event data, its single parameter is a integer
-holding the notification ID of the event.
+holding the notification ID of the event. This method should be evaluated every
+time an ACPI notification is received, since some ACPI implementations use a
+queue to store WMI event data items. This queue will overflow after a couple
+of WMI events are received without retrieving the associated WMI event data.
index f8ed5f66cd206a024f4c4ceb4f0a1373739fbdd1..6b9c6deca8ba894c0243e50b21aad573e603f969 100644 (file)
@@ -7,6 +7,9 @@
  * Copyright (c) 2015, Intel Corporation.
  */
 
+#define pr_fmt(fmt) "punit_atom: " fmt
+
+#include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -117,6 +120,51 @@ static void punit_dbgfs_unregister(void)
        debugfs_remove_recursive(punit_dbg_file);
 }
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
+static const struct punit_device *punit_dev;
+
+static void punit_s2idle_check(void)
+{
+       const struct punit_device *punit_devp;
+       u32 punit_pwr_status, dstate;
+       int status;
+
+       for (punit_devp = punit_dev; punit_devp->name; punit_devp++) {
+               /* Skip MIO, it is on till the very last moment */
+               if (punit_devp->reg == MIO_SS_PM)
+                       continue;
+
+               status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
+                                      punit_devp->reg, &punit_pwr_status);
+               if (status) {
+                       pr_err("%s read failed\n", punit_devp->name);
+               } else  {
+                       dstate = (punit_pwr_status >> punit_devp->sss_pos) & 3;
+                       if (!dstate)
+                               pr_err("%s is in D0 prior to s2idle\n", punit_devp->name);
+               }
+       }
+}
+
+static struct acpi_s2idle_dev_ops punit_s2idle_ops = {
+       .check = punit_s2idle_check,
+};
+
+static void punit_s2idle_check_register(struct punit_device *punit_device)
+{
+       punit_dev = punit_device;
+       acpi_register_lps0_dev(&punit_s2idle_ops);
+}
+
+static void punit_s2idle_check_unregister(void)
+{
+       acpi_unregister_lps0_dev(&punit_s2idle_ops);
+}
+#else
+static void punit_s2idle_check_register(struct punit_device *punit_device) {}
+static void punit_s2idle_check_unregister(void) {}
+#endif
+
 #define X86_MATCH(model, data)                                          \
        X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, INTEL_FAM6_##model, \
                                           X86_FEATURE_MWAIT, data)
@@ -131,19 +179,23 @@ MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids);
 
 static int __init punit_atom_debug_init(void)
 {
+       struct punit_device *punit_device;
        const struct x86_cpu_id *id;
 
        id = x86_match_cpu(intel_punit_cpu_ids);
        if (!id)
                return -ENODEV;
 
-       punit_dbgfs_register((struct punit_device *)id->driver_data);
+       punit_device = (struct punit_device *)id->driver_data;
+       punit_dbgfs_register(punit_device);
+       punit_s2idle_check_register(punit_device);
 
        return 0;
 }
 
 static void __exit punit_atom_debug_exit(void)
 {
+       punit_s2idle_check_unregister();
        punit_dbgfs_unregister();
 }
 
index 2974dd0ec6f4d0162dfbeec2b814e94ada5b2035..5ec9255e33faf75d8915909099465e565b2ce937 100644 (file)
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/platform_data/x86/clk-pmc-atom.h>
+#include <linux/platform_data/x86/pmc_atom.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
 #define PLT_CLK_NAME_BASE      "pmc_plt_clk"
 
-#define PMC_CLK_CTL_OFFSET             0x60
-#define PMC_CLK_CTL_SIZE               4
-#define PMC_CLK_NUM                    6
-#define PMC_CLK_CTL_GATED_ON_D3                0x0
-#define PMC_CLK_CTL_FORCE_ON           0x1
-#define PMC_CLK_CTL_FORCE_OFF          0x2
-#define PMC_CLK_CTL_RESERVED           0x3
-#define PMC_MASK_CLK_CTL               GENMASK(1, 0)
-#define PMC_MASK_CLK_FREQ              BIT(2)
-#define PMC_CLK_FREQ_XTAL              (0 << 2)        /* 25 MHz */
-#define PMC_CLK_FREQ_PLL               (1 << 2)        /* 19.2 MHz */
-
 struct clk_plt_fixed {
        struct clk_hw *clk;
        struct clk_lookup *lookup;
index 4fa837e65a61713f28554eae0e2e6f753cf66339..a8d42c9d5d04c35989abee839211686dd2030a0d 100644 (file)
@@ -1600,6 +1600,7 @@ static struct wmi_driver dell_smm_wmi_driver = {
        },
        .id_table = dell_smm_wmi_id_table,
        .probe = dell_smm_wmi_probe,
+       .no_singleton = true,
 };
 
 /*
index c1aef3a8fb2de28cd78bb0a86e979eb8ea2d580b..dd5f370c316823260bf83ccb6bce49aa90f7817b 100644 (file)
@@ -463,7 +463,7 @@ static ssize_t large_icm_show(struct device *dev,
        if (res.a0)
                return -EPERM;
 
-       return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1);
+       return sysfs_emit(buf, "0x%lx", res.a1);
 }
 
 static ssize_t large_icm_store(struct device *dev,
@@ -581,7 +581,7 @@ static ssize_t opn_show(struct device *dev,
        }
        mutex_unlock(&mfg_ops_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data);
+       return sysfs_emit(buf, "%s", (char *)opn_data);
 }
 
 static ssize_t opn_store(struct device *dev,
@@ -632,7 +632,7 @@ static ssize_t sku_show(struct device *dev,
        }
        mutex_unlock(&mfg_ops_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data);
+       return sysfs_emit(buf, "%s", (char *)sku_data);
 }
 
 static ssize_t sku_store(struct device *dev,
@@ -683,7 +683,7 @@ static ssize_t modl_show(struct device *dev,
        }
        mutex_unlock(&mfg_ops_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data);
+       return sysfs_emit(buf, "%s", (char *)modl_data);
 }
 
 static ssize_t modl_store(struct device *dev,
@@ -734,7 +734,7 @@ static ssize_t sn_show(struct device *dev,
        }
        mutex_unlock(&mfg_ops_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data);
+       return sysfs_emit(buf, "%s", (char *)sn_data);
 }
 
 static ssize_t sn_store(struct device *dev,
@@ -785,7 +785,7 @@ static ssize_t uuid_show(struct device *dev,
        }
        mutex_unlock(&mfg_ops_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data);
+       return sysfs_emit(buf, "%s", (char *)uuid_data);
 }
 
 static ssize_t uuid_store(struct device *dev,
@@ -836,7 +836,7 @@ static ssize_t rev_show(struct device *dev,
        }
        mutex_unlock(&mfg_ops_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data);
+       return sysfs_emit(buf, "%s", (char *)rev_data);
 }
 
 static ssize_t rev_store(struct device *dev,
index b1995ac268d77a9c56c81ce6e65048e2c465c449..4ed9c7fd2b62af05213180429eb4845d73c80dad 100644 (file)
@@ -99,8 +99,8 @@
  */
 struct mlxbf_pmc_attribute {
        struct device_attribute dev_attr;
-       int index;
-       int nr;
+       unsigned int index;
+       unsigned int nr;
 };
 
 /**
@@ -121,7 +121,7 @@ struct mlxbf_pmc_block_info {
        void __iomem *mmio_base;
        size_t blk_size;
        size_t counters;
-       int type;
+       unsigned int type;
        struct mlxbf_pmc_attribute *attr_counter;
        struct mlxbf_pmc_attribute *attr_event;
        struct mlxbf_pmc_attribute attr_event_list;
@@ -149,17 +149,17 @@ struct mlxbf_pmc_block_info {
  */
 struct mlxbf_pmc_context {
        struct platform_device *pdev;
-       uint32_t total_blocks;
-       uint32_t tile_count;
-       uint8_t llt_enable;
-       uint8_t mss_enable;
-       uint32_t group_num;
+       u32 total_blocks;
+       u32 tile_count;
+       u8 llt_enable;
+       u8 mss_enable;
+       u32 group_num;
        struct device *hwmon_dev;
        const char *block_name[MLXBF_PMC_MAX_BLOCKS];
        struct mlxbf_pmc_block_info block[MLXBF_PMC_MAX_BLOCKS];
        const struct attribute_group *groups[MLXBF_PMC_MAX_BLOCKS];
        bool svc_sreg_support;
-       uint32_t sreg_tbl_perf;
+       u32 sreg_tbl_perf;
        unsigned int event_set;
 };
 
@@ -169,7 +169,7 @@ struct mlxbf_pmc_context {
  * @evt_name: Name of the event
  */
 struct mlxbf_pmc_events {
-       int evt_num;
+       u32 evt_num;
        char *evt_name;
 };
 
@@ -865,8 +865,7 @@ static struct mlxbf_pmc_context *pmc;
 static const char *mlxbf_pmc_svc_uuid_str = "89c036b4-e7d7-11e6-8797-001aca00bfc4";
 
 /* Calls an SMC to access a performance register */
-static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command,
-                                uint64_t *result)
+static int mlxbf_pmc_secure_read(void __iomem *addr, u32 command, u64 *result)
 {
        struct arm_smccc_res res;
        int status, err = 0;
@@ -892,8 +891,7 @@ static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command,
 }
 
 /* Read from a performance counter */
-static int mlxbf_pmc_read(void __iomem *addr, uint32_t command,
-                         uint64_t *result)
+static int mlxbf_pmc_read(void __iomem *addr, u32 command, u64 *result)
 {
        if (pmc->svc_sreg_support)
                return mlxbf_pmc_secure_read(addr, command, result);
@@ -907,22 +905,21 @@ static int mlxbf_pmc_read(void __iomem *addr, uint32_t command,
 }
 
 /* Convenience function for 32-bit reads */
-static int mlxbf_pmc_readl(void __iomem *addr, uint32_t *result)
+static int mlxbf_pmc_readl(void __iomem *addr, u32 *result)
 {
-       uint64_t read_out;
+       u64 read_out;
        int status;
 
        status = mlxbf_pmc_read(addr, MLXBF_PMC_READ_REG_32, &read_out);
        if (status)
                return status;
-       *result = (uint32_t)read_out;
+       *result = (u32)read_out;
 
        return 0;
 }
 
 /* Calls an SMC to access a performance register */
-static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command,
-                                 uint64_t value)
+static int mlxbf_pmc_secure_write(void __iomem *addr, u32 command, u64 value)
 {
        struct arm_smccc_res res;
        int status, err = 0;
@@ -945,7 +942,7 @@ static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command,
 }
 
 /* Write to a performance counter */
-static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value)
+static int mlxbf_pmc_write(void __iomem *addr, int command, u64 value)
 {
        if (pmc->svc_sreg_support)
                return mlxbf_pmc_secure_write(addr, command, value);
@@ -959,7 +956,7 @@ static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value)
 }
 
 /* Check if the register offset is within the mapped region for the block */
-static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset)
+static bool mlxbf_pmc_valid_range(unsigned int blk_num, u32 offset)
 {
        if ((offset >= 0) && !(offset % MLXBF_PMC_REG_SIZE) &&
            (offset + MLXBF_PMC_REG_SIZE <= pmc->block[blk_num].blk_size))
@@ -969,33 +966,33 @@ static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset)
 }
 
 /* Get the event list corresponding to a certain block */
-static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk,
-                                                          int *size)
+static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size_t *psize)
 {
        const struct mlxbf_pmc_events *events;
+       size_t size;
 
        if (strstr(blk, "tilenet")) {
                events = mlxbf_pmc_hnfnet_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events);
+               size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events);
        } else if (strstr(blk, "tile")) {
                events = mlxbf_pmc_hnf_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_hnf_events);
+               size = ARRAY_SIZE(mlxbf_pmc_hnf_events);
        } else if (strstr(blk, "triogen")) {
                events = mlxbf_pmc_smgen_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
+               size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
        } else if (strstr(blk, "trio")) {
                switch (pmc->event_set) {
                case MLXBF_PMC_EVENT_SET_BF1:
                        events = mlxbf_pmc_trio_events_1;
-                       *size = ARRAY_SIZE(mlxbf_pmc_trio_events_1);
+                       size = ARRAY_SIZE(mlxbf_pmc_trio_events_1);
                        break;
                case MLXBF_PMC_EVENT_SET_BF2:
                        events = mlxbf_pmc_trio_events_2;
-                       *size = ARRAY_SIZE(mlxbf_pmc_trio_events_2);
+                       size = ARRAY_SIZE(mlxbf_pmc_trio_events_2);
                        break;
                default:
                        events = NULL;
-                       *size = 0;
+                       size = 0;
                        break;
                }
        } else if (strstr(blk, "mss")) {
@@ -1003,51 +1000,60 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk,
                case MLXBF_PMC_EVENT_SET_BF1:
                case MLXBF_PMC_EVENT_SET_BF2:
                        events = mlxbf_pmc_mss_events_1;
-                       *size = ARRAY_SIZE(mlxbf_pmc_mss_events_1);
+                       size = ARRAY_SIZE(mlxbf_pmc_mss_events_1);
                        break;
                case MLXBF_PMC_EVENT_SET_BF3:
                        events = mlxbf_pmc_mss_events_3;
-                       *size = ARRAY_SIZE(mlxbf_pmc_mss_events_3);
+                       size = ARRAY_SIZE(mlxbf_pmc_mss_events_3);
                        break;
                default:
                        events = NULL;
-                       *size = 0;
+                       size = 0;
                        break;
                }
        } else if (strstr(blk, "ecc")) {
                events = mlxbf_pmc_ecc_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_ecc_events);
+               size = ARRAY_SIZE(mlxbf_pmc_ecc_events);
        } else if (strstr(blk, "pcie")) {
                events = mlxbf_pmc_pcie_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_pcie_events);
+               size = ARRAY_SIZE(mlxbf_pmc_pcie_events);
        } else if (strstr(blk, "l3cache")) {
                events = mlxbf_pmc_l3c_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_l3c_events);
+               size = ARRAY_SIZE(mlxbf_pmc_l3c_events);
        } else if (strstr(blk, "gic")) {
                events = mlxbf_pmc_smgen_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
+               size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
        } else if (strstr(blk, "smmu")) {
                events = mlxbf_pmc_smgen_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
+               size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
        } else if (strstr(blk, "llt_miss")) {
                events = mlxbf_pmc_llt_miss_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events);
+               size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events);
        } else if (strstr(blk, "llt")) {
                events = mlxbf_pmc_llt_events;
-               *size = ARRAY_SIZE(mlxbf_pmc_llt_events);
+               size = ARRAY_SIZE(mlxbf_pmc_llt_events);
        } else {
                events = NULL;
-               *size = 0;
+               size = 0;
        }
 
+       if (psize)
+               *psize = size;
+
        return events;
 }
 
+static bool mlxbf_pmc_event_supported(const char *blk)
+{
+       return !!mlxbf_pmc_event_list(blk, NULL);
+}
+
 /* Get the event number given the name */
 static int mlxbf_pmc_get_event_num(const char *blk, const char *evt)
 {
        const struct mlxbf_pmc_events *events;
-       int i, size;
+       unsigned int i;
+       size_t size;
 
        events = mlxbf_pmc_event_list(blk, &size);
        if (!events)
@@ -1062,10 +1068,11 @@ static int mlxbf_pmc_get_event_num(const char *blk, const char *evt)
 }
 
 /* Get the event number given the name */
-static char *mlxbf_pmc_get_event_name(const char *blk, int evt)
+static char *mlxbf_pmc_get_event_name(const char *blk, u32 evt)
 {
        const struct mlxbf_pmc_events *events;
-       int i, size;
+       unsigned int i;
+       size_t size;
 
        events = mlxbf_pmc_event_list(blk, &size);
        if (!events)
@@ -1080,9 +1087,9 @@ static char *mlxbf_pmc_get_event_name(const char *blk, int evt)
 }
 
 /* Method to enable/disable/reset l3cache counters */
-static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset)
+static int mlxbf_pmc_config_l3_counters(unsigned int blk_num, bool enable, bool reset)
 {
-       uint32_t perfcnt_cfg = 0;
+       u32 perfcnt_cfg = 0;
 
        if (enable)
                perfcnt_cfg |= MLXBF_PMC_L3C_PERF_CNT_CFG_EN;
@@ -1095,12 +1102,9 @@ static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset)
 }
 
 /* Method to handle l3cache counter programming */
-static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num,
-                                       uint32_t evt)
+static int mlxbf_pmc_program_l3_counter(unsigned int blk_num, u32 cnt_num, u32 evt)
 {
-       uint32_t perfcnt_sel_1 = 0;
-       uint32_t perfcnt_sel = 0;
-       uint32_t *wordaddr;
+       u32 perfcnt_sel_1 = 0, perfcnt_sel = 0, *wordaddr;
        void __iomem *pmcaddr;
        int ret;
 
@@ -1162,11 +1166,10 @@ static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to handle crspace counter programming */
-static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
-                                            uint32_t evt)
+static int mlxbf_pmc_program_crspace_counter(unsigned int blk_num, u32 cnt_num, u32 evt)
 {
-       uint32_t word;
        void *addr;
+       u32 word;
        int ret;
 
        addr = pmc->block[blk_num].mmio_base +
@@ -1187,7 +1190,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to clear crspace counter value */
-static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num)
+static int mlxbf_pmc_clear_crspace_counter(unsigned int blk_num, u32 cnt_num)
 {
        void *addr;
 
@@ -1199,10 +1202,9 @@ static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num)
 }
 
 /* Method to program a counter to monitor an event */
-static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num,
-                                    uint32_t evt, bool is_l3)
+static int mlxbf_pmc_program_counter(unsigned int blk_num, u32 cnt_num, u32 evt, bool is_l3)
 {
-       uint64_t perfctl, perfevt, perfmon_cfg;
+       u64 perfctl, perfevt, perfmon_cfg;
 
        if (cnt_num >= pmc->block[blk_num].counters)
                return -ENODEV;
@@ -1263,12 +1265,11 @@ static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to handle l3 counter reads */
-static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num,
-                                    uint64_t *result)
+static int mlxbf_pmc_read_l3_counter(unsigned int blk_num, u32 cnt_num, u64 *result)
 {
-       uint32_t perfcnt_low = 0, perfcnt_high = 0;
-       uint64_t value;
+       u32 perfcnt_low = 0, perfcnt_high = 0;
        int status;
+       u64 value;
 
        status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
                                         MLXBF_PMC_L3C_PERF_CNT_LOW +
@@ -1295,11 +1296,10 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to handle crspace counter reads */
-static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num,
-                                         uint64_t *result)
+static int mlxbf_pmc_read_crspace_counter(unsigned int blk_num, u32 cnt_num, u64 *result)
 {
-       uint32_t value;
        int status = 0;
+       u32 value;
 
        status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
                MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) +
@@ -1313,11 +1313,10 @@ static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to read the counter value */
-static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3,
-                                 uint64_t *result)
+static int mlxbf_pmc_read_counter(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)
 {
-       uint32_t perfcfg_offset, perfval_offset;
-       uint64_t perfmon_cfg;
+       u32 perfcfg_offset, perfval_offset;
+       u64 perfmon_cfg;
        int status;
 
        if (cnt_num >= pmc->block[blk_num].counters)
@@ -1351,13 +1350,11 @@ static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3,
 }
 
 /* Method to read L3 block event */
-static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num,
-                                  uint64_t *result)
+static int mlxbf_pmc_read_l3_event(unsigned int blk_num, u32 cnt_num, u64 *result)
 {
-       uint32_t perfcnt_sel = 0, perfcnt_sel_1 = 0;
-       uint32_t *wordaddr;
+       u32 perfcnt_sel = 0, perfcnt_sel_1 = 0, *wordaddr;
        void __iomem *pmcaddr;
-       uint64_t evt;
+       u64 evt;
 
        /* Select appropriate register information */
        switch (cnt_num) {
@@ -1405,10 +1402,9 @@ static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to read crspace block event */
-static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
-                                       uint64_t *result)
+static int mlxbf_pmc_read_crspace_event(unsigned int blk_num, u32 cnt_num, u64 *result)
 {
-       uint32_t word, evt;
+       u32 word, evt;
        void *addr;
        int ret;
 
@@ -1429,11 +1425,10 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
 }
 
 /* Method to find the event currently being monitored by a counter */
-static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3,
-                               uint64_t *result)
+static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)
 {
-       uint32_t perfcfg_offset, perfval_offset;
-       uint64_t perfmon_cfg, perfevt;
+       u32 perfcfg_offset, perfval_offset;
+       u64 perfmon_cfg, perfevt;
 
        if (cnt_num >= pmc->block[blk_num].counters)
                return -EINVAL;
@@ -1469,9 +1464,9 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3,
 }
 
 /* Method to read a register */
-static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result)
+static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
 {
-       uint32_t ecc_out;
+       u32 ecc_out;
 
        if (strstr(pmc->block_name[blk_num], "ecc")) {
                if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset,
@@ -1490,7 +1485,7 @@ static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result)
 }
 
 /* Method to write to a register */
-static int mlxbf_pmc_write_reg(int blk_num, uint32_t offset, uint64_t data)
+static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data)
 {
        if (strstr(pmc->block_name[blk_num], "ecc")) {
                return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset,
@@ -1510,9 +1505,10 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_counter = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       int blk_num, cnt_num, offset;
+       unsigned int blk_num, cnt_num;
        bool is_l3 = false;
-       uint64_t value;
+       int offset;
+       u64 value;
 
        blk_num = attr_counter->nr;
        cnt_num = attr_counter->index;
@@ -1544,14 +1540,16 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_counter = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       int blk_num, cnt_num, offset, err, data;
+       unsigned int blk_num, cnt_num, data;
        bool is_l3 = false;
-       uint64_t evt_num;
+       u64 evt_num;
+       int offset;
+       int err;
 
        blk_num = attr_counter->nr;
        cnt_num = attr_counter->index;
 
-       err = kstrtoint(buf, 0, &data);
+       err = kstrtouint(buf, 0, &data);
        if (err < 0)
                return err;
 
@@ -1580,7 +1578,7 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev,
                if (err)
                        return err;
        } else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) {
-               if (sscanf(attr->attr.name, "counter%d", &cnt_num) != 1)
+               if (sscanf(attr->attr.name, "counter%u", &cnt_num) != 1)
                        return -EINVAL;
                err = mlxbf_pmc_clear_crspace_counter(blk_num, cnt_num);
        } else
@@ -1595,10 +1593,11 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_event = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       int blk_num, cnt_num, err;
+       unsigned int blk_num, cnt_num;
        bool is_l3 = false;
-       uint64_t evt_num;
        char *evt_name;
+       u64 evt_num;
+       int err;
 
        blk_num = attr_event->nr;
        cnt_num = attr_event->index;
@@ -1624,8 +1623,10 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_event = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       int blk_num, cnt_num, evt_num, err;
+       unsigned int blk_num, cnt_num;
        bool is_l3 = false;
+       int evt_num;
+       int err;
 
        blk_num = attr_event->nr;
        cnt_num = attr_event->index;
@@ -1636,7 +1637,7 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev,
                if (evt_num < 0)
                        return -EINVAL;
        } else {
-               err = kstrtoint(buf, 0, &evt_num);
+               err = kstrtouint(buf, 0, &evt_num);
                if (err < 0)
                        return err;
        }
@@ -1658,9 +1659,11 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_event_list = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       int blk_num, i, size, len = 0, ret = 0;
        const struct mlxbf_pmc_events *events;
        char e_info[MLXBF_PMC_EVENT_INFO_LEN];
+       unsigned int blk_num, i, len = 0;
+       size_t size;
+       int ret = 0;
 
        blk_num = attr_event_list->nr;
 
@@ -1686,8 +1689,8 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_enable = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       uint32_t perfcnt_cfg, word;
-       int blk_num, value;
+       unsigned int blk_num, value;
+       u32 perfcnt_cfg, word;
 
        blk_num = attr_enable->nr;
 
@@ -1707,7 +1710,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev,
                value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg);
        }
 
-       return sysfs_emit(buf, "%d\n", value);
+       return sysfs_emit(buf, "%u\n", value);
 }
 
 /* Store function for "enable" sysfs files - only for l3cache & crspace */
@@ -1717,12 +1720,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
 {
        struct mlxbf_pmc_attribute *attr_enable = container_of(
                attr, struct mlxbf_pmc_attribute, dev_attr);
-       int err, en, blk_num;
-       uint32_t word;
+       unsigned int en, blk_num;
+       u32 word;
+       int err;
 
        blk_num = attr_enable->nr;
 
-       err = kstrtoint(buf, 0, &en);
+       err = kstrtouint(buf, 0, &en);
        if (err < 0)
                return err;
 
@@ -1760,10 +1764,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
 }
 
 /* Populate attributes for blocks with counters to monitor performance */
-static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
+static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num)
 {
        struct mlxbf_pmc_attribute *attr;
-       int i = 0, j = 0;
+       unsigned int i = 0, j = 0;
+
+       if (!mlxbf_pmc_event_supported(pmc->block_name[blk_num]))
+               return -ENOENT;
 
        /* "event_list" sysfs to list events supported by the block */
        attr = &pmc->block[blk_num].attr_event_list;
@@ -1812,8 +1819,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
                attr->dev_attr.store = mlxbf_pmc_counter_store;
                attr->index = j;
                attr->nr = blk_num;
-               attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
-                                                         "counter%d", j);
+               attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "counter%u", j);
                if (!attr->dev_attr.attr.name)
                        return -ENOMEM;
                pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
@@ -1825,8 +1831,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
                attr->dev_attr.store = mlxbf_pmc_event_store;
                attr->index = j;
                attr->nr = blk_num;
-               attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
-                                                         "event%d", j);
+               attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "event%u", j);
                if (!attr->dev_attr.attr.name)
                        return -ENOMEM;
                pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
@@ -1837,30 +1842,31 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
 }
 
 /* Populate attributes for blocks with registers to monitor performance */
-static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num)
+static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num)
 {
-       struct mlxbf_pmc_attribute *attr;
        const struct mlxbf_pmc_events *events;
-       int i = 0, j = 0;
+       struct mlxbf_pmc_attribute *attr;
+       unsigned int i = 0;
+       size_t count = 0;
 
-       events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &j);
+       events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &count);
        if (!events)
-               return -EINVAL;
+               return -ENOENT;
 
        pmc->block[blk_num].attr_event = devm_kcalloc(
-               dev, j, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
+               dev, count, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
        if (!pmc->block[blk_num].attr_event)
                return -ENOMEM;
 
-       while (j > 0) {
-               --j;
-               attr = &pmc->block[blk_num].attr_event[j];
+       while (count > 0) {
+               --count;
+               attr = &pmc->block[blk_num].attr_event[count];
                attr->dev_attr.attr.mode = 0644;
                attr->dev_attr.show = mlxbf_pmc_counter_show;
                attr->dev_attr.store = mlxbf_pmc_counter_store;
                attr->nr = blk_num;
                attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
-                                                         events[j].evt_name);
+                                                         events[count].evt_name);
                if (!attr->dev_attr.attr.name)
                        return -ENOMEM;
                pmc->block[blk_num].block_attr[i] = &attr->dev_attr.attr;
@@ -1872,7 +1878,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num)
 }
 
 /* Helper to create the bfperf sysfs sub-directories and files */
-static int mlxbf_pmc_create_groups(struct device *dev, int blk_num)
+static int mlxbf_pmc_create_groups(struct device *dev, unsigned int blk_num)
 {
        int err;
 
@@ -1883,7 +1889,7 @@ static int mlxbf_pmc_create_groups(struct device *dev, int blk_num)
        else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER)
                err = mlxbf_pmc_init_perftype_reg(dev, blk_num);
        else
-               err = -EINVAL;
+               err = -ENOENT;
 
        if (err)
                return err;
@@ -1914,19 +1920,20 @@ static bool mlxbf_pmc_guid_match(const guid_t *guid,
 /* Helper to map the Performance Counters from the varios blocks */
 static int mlxbf_pmc_map_counters(struct device *dev)
 {
-       uint64_t info[MLXBF_PMC_INFO_SZ];
-       int i, tile_num, ret;
+       u64 info[MLXBF_PMC_INFO_SZ];
+       unsigned int tile_num, i;
+       int ret;
 
        for (i = 0; i < pmc->total_blocks; ++i) {
                /* Create sysfs for tiles only if block number <  tile_count */
                if (strstr(pmc->block_name[i], "tilenet")) {
-                       if (sscanf(pmc->block_name[i], "tilenet%d", &tile_num) != 1)
+                       if (sscanf(pmc->block_name[i], "tilenet%u", &tile_num) != 1)
                                continue;
 
                        if (tile_num >= pmc->tile_count)
                                continue;
                } else if (strstr(pmc->block_name[i], "tile")) {
-                       if (sscanf(pmc->block_name[i], "tile%d", &tile_num) != 1)
+                       if (sscanf(pmc->block_name[i], "tile%u", &tile_num) != 1)
                                continue;
 
                        if (tile_num >= pmc->tile_count)
@@ -1936,9 +1943,9 @@ static int mlxbf_pmc_map_counters(struct device *dev)
                /* Create sysfs only for enabled MSS blocks */
                if (strstr(pmc->block_name[i], "mss") &&
                    pmc->event_set == MLXBF_PMC_EVENT_SET_BF3) {
-                       int mss_num;
+                       unsigned int mss_num;
 
-                       if (sscanf(pmc->block_name[i], "mss%d", &mss_num) != 1)
+                       if (sscanf(pmc->block_name[i], "mss%u", &mss_num) != 1)
                                continue;
 
                        if (!((pmc->mss_enable >> mss_num) & 0x1))
@@ -1947,17 +1954,17 @@ static int mlxbf_pmc_map_counters(struct device *dev)
 
                /* Create sysfs only for enabled LLT blocks */
                if (strstr(pmc->block_name[i], "llt_miss")) {
-                       int llt_num;
+                       unsigned int llt_num;
 
-                       if (sscanf(pmc->block_name[i], "llt_miss%d", &llt_num) != 1)
+                       if (sscanf(pmc->block_name[i], "llt_miss%u", &llt_num) != 1)
                                continue;
 
                        if (!((pmc->llt_enable >> llt_num) & 0x1))
                                continue;
                } else if (strstr(pmc->block_name[i], "llt")) {
-                       int llt_num;
+                       unsigned int llt_num;
 
-                       if (sscanf(pmc->block_name[i], "llt%d", &llt_num) != 1)
+                       if (sscanf(pmc->block_name[i], "llt%u", &llt_num) != 1)
                                continue;
 
                        if (!((pmc->llt_enable >> llt_num) & 0x1))
@@ -1987,6 +1994,10 @@ static int mlxbf_pmc_map_counters(struct device *dev)
                        return -ENOMEM;
 
                ret = mlxbf_pmc_create_groups(dev, i);
+               if (ret == -ENOENT) {
+                       dev_warn(dev, "ignoring unsupported block: '%s'\n", pmc->block_name[i]);
+                       continue;
+               }
                if (ret)
                        return ret;
        }
index 5c022b258f91af91204f3eda15e2d34bb3da625e..0ce9fff1f7d4d41cf3891d26ae91bff45d87e891 100644 (file)
@@ -348,20 +348,6 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
        u32 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);
index aeb3feae40ff35f153fdf35881ed4ec9de949590..035d6b4105cd637f5413661fe2bae53531693cf4 100644 (file)
@@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
        .parent = &ssam_node_root,
 };
 
+/* Fan speed function. */
+static const struct software_node ssam_node_fan_speed = {
+       .name = "ssam:01:05:01:01:01",
+       .parent = &ssam_node_root,
+};
+
 /* Tablet-mode switch via KIP subsystem. */
 static const struct software_node ssam_node_kip_tablet_switch = {
        .name = "ssam:01:0e:01:00:01",
@@ -305,6 +311,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
        &ssam_node_bat_ac,
        &ssam_node_bat_main,
        &ssam_node_tmp_pprof,
+       &ssam_node_fan_speed,
        &ssam_node_pos_tablet_switch,
        &ssam_node_hid_kip_keyboard,
        &ssam_node_hid_kip_penstash,
index bdd302274b9ab360c2d7bf0cf36ac99f6efed4b4..7e9251fc33416ed8def47fca7a986a342a7f6f2d 100644 (file)
@@ -56,8 +56,6 @@ config HUAWEI_WMI
        depends on INPUT
        select INPUT_SPARSEKMAP
        select LEDS_CLASS
-       select LEDS_TRIGGERS
-       select LEDS_TRIGGER_AUDIO
        select NEW_LEDS
        help
          This driver provides support for Huawei WMI hotkeys, battery charge
@@ -269,8 +267,6 @@ config ASUS_WMI
        select INPUT_SPARSEKMAP
        select LEDS_CLASS
        select NEW_LEDS
-       select LEDS_TRIGGERS
-       select LEDS_TRIGGER_AUDIO
        select ACPI_PLATFORM_PROFILE
        help
          Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
@@ -374,6 +370,7 @@ config FUJITSU_LAPTOP
        depends on ACPI
        depends on INPUT
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_BATTERY
        depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        select NEW_LEDS
@@ -507,8 +504,6 @@ config THINKPAD_ACPI
        select NVRAM
        select NEW_LEDS
        select LEDS_CLASS
-       select LEDS_TRIGGERS
-       select LEDS_TRIGGER_AUDIO
        help
          This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
          support for Fn-Fx key combinations, Bluetooth control, video
index 88b826e88ebd79218aefb3da3a83ade71390dd9a..ee2e164f86b9c2973e317bfbfe3297571a8cab48 100644 (file)
@@ -276,6 +276,7 @@ static bool has_type_aa;
 static u16 commun_func_bitmap;
 static u8 commun_fn_key_number;
 static bool cycle_gaming_thermal_profile = true;
+static bool predator_v4;
 
 module_param(mailled, int, 0444);
 module_param(brightness, int, 0444);
@@ -284,6 +285,7 @@ module_param(force_series, int, 0444);
 module_param(force_caps, int, 0444);
 module_param(ec_raw_mode, bool, 0444);
 module_param(cycle_gaming_thermal_profile, bool, 0644);
+module_param(predator_v4, bool, 0444);
 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
@@ -292,6 +294,8 @@ MODULE_PARM_DESC(force_caps, "Force the capability bitmask to this value");
 MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
 MODULE_PARM_DESC(cycle_gaming_thermal_profile,
        "Set thermal mode key in cycle mode. Disabling it sets the mode key in turbo toggle mode");
+MODULE_PARM_DESC(predator_v4,
+       "Enable features for predator laptops that use predator sense v4");
 
 struct acer_data {
        int mailled;
@@ -584,6 +588,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
                },
                .driver_data = &quirk_acer_predator_v4,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Predator PH16-71",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Predator PH16-71"),
+               },
+               .driver_data = &quirk_acer_predator_v4,
+       },
        {
                .callback = set_force_caps,
                .ident = "Acer Aspire Switch 10E SW3-016",
@@ -725,7 +738,9 @@ enum acer_predator_v4_thermal_profile_wmi {
 /* Find which quirks are needed for a particular vendor/ model pair */
 static void __init find_quirks(void)
 {
-       if (!force_series) {
+       if (predator_v4) {
+               quirks = &quirk_acer_predator_v4;
+       } else if (!force_series) {
                dmi_check_system(acer_quirks);
                dmi_check_system(non_acer_quirks);
        } else if (force_series == 2490) {
index 54753213cc61c21373f0769ffa2af4b8b0cb1a88..f88682d36447c2c56791c407782457bd12dd094d 100644 (file)
@@ -8,7 +8,7 @@ source "drivers/platform/x86/amd/pmc/Kconfig"
 
 config AMD_HSMP
        tristate "AMD HSMP Driver"
-       depends on AMD_NB && X86_64
+       depends on AMD_NB && X86_64 && ACPI
        help
          The driver provides a way for user space tools to monitor and manage
          system management functionality on EPYC server CPUs from AMD.
index b55d80e291393e902bb2b9786ce78eb0c39e1ab9..1927be901108e3c9f0b7eacadf007ad9b849688e 100644 (file)
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/semaphore.h>
+#include <linux/acpi.h>
 
 #define DRIVER_NAME            "amd_hsmp"
-#define DRIVER_VERSION         "2.0"
+#define DRIVER_VERSION         "2.2"
+#define ACPI_HSMP_DEVICE_HID   "AMDI0097"
 
 /* HSMP Status / Error codes */
 #define HSMP_STATUS_NOT_READY  0x00
  * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
  * Below are required SMN address for HSMP Mailbox register offsets in SMU address space
  */
-#define SMN_HSMP_MSG_ID                0x3B10534
-#define SMN_HSMP_MSG_RESP      0x3B10980
-#define SMN_HSMP_MSG_DATA      0x3B109E0
+#define SMN_HSMP_BASE          0x3B00000
+#define SMN_HSMP_MSG_ID                0x0010534
+#define SMN_HSMP_MSG_ID_F1A_M0H        0x0010934
+#define SMN_HSMP_MSG_RESP      0x0010980
+#define SMN_HSMP_MSG_DATA      0x00109E0
 
 #define HSMP_INDEX_REG         0xc4
 #define HSMP_DATA_REG          0xc8
 
 #define HSMP_ATTR_GRP_NAME_SIZE        10
 
+/* These are the strings specified in ACPI table */
+#define MSG_IDOFF_STR          "MsgIdOffset"
+#define MSG_ARGOFF_STR         "MsgArgOffset"
+#define MSG_RESPOFF_STR                "MsgRspOffset"
+
+#define MAX_AMD_SOCKETS 8
+
+struct hsmp_mbaddr_info {
+       u32 base_addr;
+       u32 msg_id_off;
+       u32 msg_resp_off;
+       u32 msg_arg_off;
+       u32 size;
+};
+
 struct hsmp_socket {
        struct bin_attribute hsmp_attr;
+       struct hsmp_mbaddr_info mbinfo;
        void __iomem *metric_tbl_addr;
+       void __iomem *virt_base_addr;
        struct semaphore hsmp_sem;
        char name[HSMP_ATTR_GRP_NAME_SIZE];
+       struct pci_dev *root;
+       struct device *dev;
        u16 sock_ind;
 };
 
 struct hsmp_plat_device {
        struct miscdevice hsmp_device;
        struct hsmp_socket *sock;
-       struct device *dev;
        u32 proto_ver;
        u16 num_sockets;
+       bool is_acpi_device;
+       bool is_probed;
 };
 
 static struct hsmp_plat_device plat_dev;
 
-static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
-                        u32 *value, bool write)
+static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
+                            u32 *value, bool write)
 {
        int ret;
 
-       ret = pci_write_config_dword(root, HSMP_INDEX_REG, address);
+       if (!sock->root)
+               return -ENODEV;
+
+       ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG,
+                                    sock->mbinfo.base_addr + offset);
        if (ret)
                return ret;
 
-       ret = (write ? pci_write_config_dword(root, HSMP_DATA_REG, *value)
-                    : pci_read_config_dword(root, HSMP_DATA_REG, value));
+       ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value)
+                    : pci_read_config_dword(sock->root, HSMP_DATA_REG, value));
 
        return ret;
 }
 
+static void amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset,
+                              u32 *value, bool write)
+{
+       if (write)
+               iowrite32(*value, sock->virt_base_addr + offset);
+       else
+               *value = ioread32(sock->virt_base_addr + offset);
+}
+
+static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset,
+                        u32 *value, bool write)
+{
+       if (plat_dev.is_acpi_device)
+               amd_hsmp_acpi_rdwr(sock, offset, value, write);
+       else
+               return amd_hsmp_pci_rdwr(sock, offset, value, write);
+
+       return 0;
+}
+
 /*
- * Send a message to the HSMP port via PCI-e config space registers.
+ * Send a message to the HSMP port via PCI-e config space registers
+ * or by writing to MMIO space.
  *
  * The caller is expected to zero out any unused arguments.
  * If a response is expected, the number of response words should be greater than 0.
@@ -95,16 +144,19 @@ static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
  * Returns 0 for success and populates the requested number of arguments.
  * Returns a negative error code for failure.
  */
-static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
+static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *msg)
 {
+       struct hsmp_mbaddr_info *mbinfo;
        unsigned long timeout, short_sleep;
        u32 mbox_status;
        u32 index;
        int ret;
 
+       mbinfo = &sock->mbinfo;
+
        /* Clear the status register */
        mbox_status = HSMP_STATUS_NOT_READY;
-       ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR);
+       ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR);
        if (ret) {
                pr_err("Error %d clearing mailbox status register\n", ret);
                return ret;
@@ -113,7 +165,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
        index = 0;
        /* Write any message arguments */
        while (index < msg->num_args) {
-               ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
+               ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2),
                                    &msg->args[index], HSMP_WR);
                if (ret) {
                        pr_err("Error %d writing message argument %d\n", ret, index);
@@ -123,7 +175,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
        }
 
        /* Write the message ID which starts the operation */
-       ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR);
+       ret = amd_hsmp_rdwr(sock, mbinfo->msg_id_off, &msg->msg_id, HSMP_WR);
        if (ret) {
                pr_err("Error %d writing message ID %u\n", ret, msg->msg_id);
                return ret;
@@ -140,7 +192,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
        timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT);
 
        while (time_before(jiffies, timeout)) {
-               ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD);
+               ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD);
                if (ret) {
                        pr_err("Error %d reading mailbox status\n", ret);
                        return ret;
@@ -175,7 +227,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
         */
        index = 0;
        while (index < msg->response_sz) {
-               ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
+               ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2),
                                    &msg->args[index], HSMP_RD);
                if (ret) {
                        pr_err("Error %d reading response %u for message ID:%u\n",
@@ -208,21 +260,19 @@ static int validate_message(struct hsmp_message *msg)
 
 int hsmp_send_message(struct hsmp_message *msg)
 {
-       struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind];
-       struct amd_northbridge *nb;
+       struct hsmp_socket *sock;
        int ret;
 
        if (!msg)
                return -EINVAL;
-
-       nb = node_to_amd_nb(msg->sock_ind);
-       if (!nb || !nb->root)
-               return -ENODEV;
-
        ret = validate_message(msg);
        if (ret)
                return ret;
 
+       if (!plat_dev.sock || msg->sock_ind >= plat_dev.num_sockets)
+               return -ENODEV;
+       sock = &plat_dev.sock[msg->sock_ind];
+
        /*
         * The time taken by smu operation to complete is between
         * 10us to 1ms. Sometime it may take more time.
@@ -233,7 +283,7 @@ int hsmp_send_message(struct hsmp_message *msg)
        if (ret < 0)
                return ret;
 
-       ret = __hsmp_send_message(nb->root, msg);
+       ret = __hsmp_send_message(sock, msg);
 
        up(&sock->hsmp_sem);
 
@@ -244,12 +294,7 @@ EXPORT_SYMBOL_GPL(hsmp_send_message);
 static int hsmp_test(u16 sock_ind, u32 value)
 {
        struct hsmp_message msg = { 0 };
-       struct amd_northbridge *nb;
-       int ret = -ENODEV;
-
-       nb = node_to_amd_nb(sock_ind);
-       if (!nb || !nb->root)
-               return ret;
+       int ret;
 
        /*
         * Test the hsmp port by performing TEST command. The test message
@@ -261,14 +306,15 @@ static int hsmp_test(u16 sock_ind, u32 value)
        msg.args[0]     = value;
        msg.sock_ind    = sock_ind;
 
-       ret = __hsmp_send_message(nb->root, &msg);
+       ret = hsmp_send_message(&msg);
        if (ret)
                return ret;
 
        /* Check the response value */
        if (msg.args[0] != (value + 1)) {
-               pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
-                      sock_ind, (value + 1), msg.args[0]);
+               dev_err(plat_dev.sock[sock_ind].dev,
+                       "Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
+                       sock_ind, (value + 1), msg.args[0]);
                return -EBADE;
        }
 
@@ -337,6 +383,181 @@ static const struct file_operations hsmp_fops = {
        .compat_ioctl   = hsmp_ioctl,
 };
 
+/* This is the UUID used for HSMP */
+static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd,
+                                               0xa6, 0x9f, 0x4e, 0xa2,
+                                               0x87, 0x1f, 0xc2, 0xf6);
+
+static inline bool is_acpi_hsmp_uuid(union acpi_object *obj)
+{
+       if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE)
+               return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid);
+
+       return false;
+}
+
+static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind)
+{
+       char *uid;
+
+       /*
+        * UID (ID00, ID01..IDXX) is used for differentiating sockets,
+        * read it and strip the "ID" part of it and convert the remaining
+        * bytes to integer.
+        */
+       uid = acpi_device_uid(ACPI_COMPANION(dev));
+
+       return kstrtou16(uid + 2, 10, sock_ind);
+}
+
+static acpi_status hsmp_resource(struct acpi_resource *res, void *data)
+{
+       struct hsmp_socket *sock = data;
+       struct resource r;
+
+       switch (res->type) {
+       case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+               if (!acpi_dev_resource_memory(res, &r))
+                       return AE_ERROR;
+               if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE))
+                       return AE_ERROR;
+               sock->mbinfo.base_addr = r.start;
+               sock->mbinfo.size = resource_size(&r);
+               break;
+       case ACPI_RESOURCE_TYPE_END_TAG:
+               break;
+       default:
+               return AE_ERROR;
+       }
+
+       return AE_OK;
+}
+
+static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
+{
+       struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *guid, *mailbox_package;
+       union acpi_object *dsd;
+       acpi_status status;
+       int ret = 0;
+       int j;
+
+       status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL,
+                                           &buf, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
+                       acpi_format_exception(status));
+               return -ENODEV;
+       }
+
+       dsd = buf.pointer;
+
+       /* HSMP _DSD property should contain 2 objects.
+        * 1. guid which is an acpi object of type ACPI_TYPE_BUFFER
+        * 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE
+        *    This mailbox object contains 3 more acpi objects of type
+        *    ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets
+        *    these packages inturn contain 2 acpi objects of type
+        *    ACPI_TYPE_STRING and ACPI_TYPE_INTEGER
+        */
+       if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) {
+               ret = -EINVAL;
+               goto free_buf;
+       }
+
+       guid = &dsd->package.elements[0];
+       mailbox_package = &dsd->package.elements[1];
+       if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) {
+               dev_err(sock->dev, "Invalid hsmp _DSD table data\n");
+               ret = -EINVAL;
+               goto free_buf;
+       }
+
+       for (j = 0; j < mailbox_package->package.count; j++) {
+               union acpi_object *msgobj, *msgstr, *msgint;
+
+               msgobj  = &mailbox_package->package.elements[j];
+               msgstr  = &msgobj->package.elements[0];
+               msgint  = &msgobj->package.elements[1];
+
+               /* package should have 1 string and 1 integer object */
+               if (msgobj->type != ACPI_TYPE_PACKAGE ||
+                   msgstr->type != ACPI_TYPE_STRING ||
+                   msgint->type != ACPI_TYPE_INTEGER) {
+                       ret = -EINVAL;
+                       goto free_buf;
+               }
+
+               if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR,
+                            msgstr->string.length)) {
+                       sock->mbinfo.msg_id_off = msgint->integer.value;
+               } else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR,
+                                   msgstr->string.length)) {
+                       sock->mbinfo.msg_resp_off =  msgint->integer.value;
+               } else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR,
+                                   msgstr->string.length)) {
+                       sock->mbinfo.msg_arg_off = msgint->integer.value;
+               } else {
+                       ret = -ENOENT;
+                       goto free_buf;
+               }
+       }
+
+       if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off ||
+           !sock->mbinfo.msg_arg_off)
+               ret = -EINVAL;
+
+free_buf:
+       ACPI_FREE(buf.pointer);
+       return ret;
+}
+
+static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
+{
+       acpi_status status;
+
+       status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS,
+                                    hsmp_resource, sock);
+       if (ACPI_FAILURE(status)) {
+               dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
+                       acpi_format_exception(status));
+               return -EINVAL;
+       }
+       if (!sock->mbinfo.base_addr || !sock->mbinfo.size)
+               return -EINVAL;
+
+       /* The mapped region should be un cached */
+       sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr,
+                                              sock->mbinfo.size);
+       if (!sock->virt_base_addr) {
+               dev_err(sock->dev, "Failed to ioremap MP1 base address\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/* Parse the ACPI table to read the data */
+static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
+{
+       struct hsmp_socket *sock = &plat_dev.sock[sock_ind];
+       int ret;
+
+       sock->sock_ind          = sock_ind;
+       sock->dev               = dev;
+       plat_dev.is_acpi_device = true;
+
+       sema_init(&sock->hsmp_sem, 1);
+
+       /* Read MP1 base address from CRS method */
+       ret = hsmp_read_acpi_crs(sock);
+       if (ret)
+               return ret;
+
+       /* Read mailbox offsets from DSD table */
+       return hsmp_read_acpi_dsd(sock);
+}
+
 static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
                                    struct bin_attribute *bin_attr, char *buf,
                                    loff_t off, size_t count)
@@ -345,14 +566,12 @@ static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
        struct hsmp_message msg = { 0 };
        int ret;
 
-       /* Do not support lseek(), reads entire metric table */
-       if (count < bin_attr->size) {
-               dev_err(plat_dev.dev, "Wrong buffer size\n");
+       if (!sock)
                return -EINVAL;
-       }
 
-       if (!sock) {
-               dev_err(plat_dev.dev, "Failed to read attribute private data\n");
+       /* Do not support lseek(), reads entire metric table */
+       if (count < bin_attr->size) {
+               dev_err(sock->dev, "Wrong buffer size\n");
                return -EINVAL;
        }
 
@@ -388,13 +607,13 @@ static int hsmp_get_tbl_dram_base(u16 sock_ind)
         */
        dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32);
        if (!dram_addr) {
-               dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n");
+               dev_err(sock->dev, "Invalid DRAM address for metric table\n");
                return -ENOMEM;
        }
-       sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr,
+       sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr,
                                             sizeof(struct hsmp_metric_table));
        if (!sock->metric_tbl_addr) {
-               dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n");
+               dev_err(sock->dev, "Failed to ioremap metric table addr\n");
                return -ENOMEM;
        }
        return 0;
@@ -422,65 +641,91 @@ static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock
        hattrs[0]               = hattr;
 
        if (plat_dev.proto_ver == HSMP_PROTO_VER6)
-               return (hsmp_get_tbl_dram_base(sock_ind));
+               return hsmp_get_tbl_dram_base(sock_ind);
        else
                return 0;
 }
 
-/* One bin sysfs for metrics table*/
+/* One bin sysfs for metrics table */
 #define NUM_HSMP_ATTRS         1
 
-static int hsmp_create_sysfs_interface(void)
+static int hsmp_create_attr_list(struct attribute_group *attr_grp,
+                                struct device *dev, u16 sock_ind)
 {
-       const struct attribute_group **hsmp_attr_grps;
        struct bin_attribute **hsmp_bin_attrs;
+
+       /* Null terminated list of attributes */
+       hsmp_bin_attrs = devm_kcalloc(dev, NUM_HSMP_ATTRS + 1,
+                                     sizeof(*hsmp_bin_attrs),
+                                     GFP_KERNEL);
+       if (!hsmp_bin_attrs)
+               return -ENOMEM;
+
+       attr_grp->bin_attrs = hsmp_bin_attrs;
+
+       return hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, sock_ind);
+}
+
+static int hsmp_create_non_acpi_sysfs_if(struct device *dev)
+{
+       const struct attribute_group **hsmp_attr_grps;
        struct attribute_group *attr_grp;
-       int ret;
        u16 i;
 
-       /* String formatting is currently limited to u8 sockets */
-       if (WARN_ON(plat_dev.num_sockets > U8_MAX))
-               return -ERANGE;
-
-       hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) *
-                                     (plat_dev.num_sockets + 1), GFP_KERNEL);
+       hsmp_attr_grps = devm_kcalloc(dev, plat_dev.num_sockets + 1,
+                                     sizeof(*hsmp_attr_grps),
+                                     GFP_KERNEL);
        if (!hsmp_attr_grps)
                return -ENOMEM;
 
        /* Create a sysfs directory for each socket */
        for (i = 0; i < plat_dev.num_sockets; i++) {
-               attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL);
+               attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group),
+                                       GFP_KERNEL);
                if (!attr_grp)
                        return -ENOMEM;
 
                snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i);
-               attr_grp->name = plat_dev.sock[i].name;
-
-               /* Null terminated list of attributes */
-               hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) *
-                                             (NUM_HSMP_ATTRS + 1), GFP_KERNEL);
-               if (!hsmp_bin_attrs)
-                       return -ENOMEM;
-
-               attr_grp->bin_attrs             = hsmp_bin_attrs;
+               attr_grp->name                  = plat_dev.sock[i].name;
                attr_grp->is_bin_visible        = hsmp_is_sock_attr_visible;
                hsmp_attr_grps[i]               = attr_grp;
 
-               /* Now create the leaf nodes */
-               ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i);
-               if (ret)
-                       return ret;
+               hsmp_create_attr_list(attr_grp, dev, i);
        }
-       return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps);
+
+       return devm_device_add_groups(dev, hsmp_attr_grps);
+}
+
+static int hsmp_create_acpi_sysfs_if(struct device *dev)
+{
+       struct attribute_group *attr_grp;
+       u16 sock_ind;
+       int ret;
+
+       attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL);
+       if (!attr_grp)
+               return -ENOMEM;
+
+       attr_grp->is_bin_visible = hsmp_is_sock_attr_visible;
+
+       ret = hsmp_get_uid(dev, &sock_ind);
+       if (ret)
+               return ret;
+
+       ret = hsmp_create_attr_list(attr_grp, dev, sock_ind);
+       if (ret)
+               return ret;
+
+       return devm_device_add_group(dev, attr_grp);
 }
 
-static int hsmp_cache_proto_ver(void)
+static int hsmp_cache_proto_ver(u16 sock_ind)
 {
        struct hsmp_message msg = { 0 };
        int ret;
 
        msg.msg_id      = HSMP_GET_PROTO_VER;
-       msg.sock_ind    = 0;
+       msg.sock_ind    = sock_ind;
        msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz;
 
        ret = hsmp_send_message(&msg);
@@ -490,45 +735,150 @@ static int hsmp_cache_proto_ver(void)
        return ret;
 }
 
-static int hsmp_pltdrv_probe(struct platform_device *pdev)
+static inline bool is_f1a_m0h(void)
 {
-       int ret, i;
+       if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F)
+               return true;
 
-       plat_dev.sock = devm_kzalloc(&pdev->dev,
-                                    (plat_dev.num_sockets * sizeof(struct hsmp_socket)),
-                                    GFP_KERNEL);
-       if (!plat_dev.sock)
-               return -ENOMEM;
-       plat_dev.dev = &pdev->dev;
+       return false;
+}
+
+static int init_platform_device(struct device *dev)
+{
+       struct hsmp_socket *sock;
+       int ret, i;
 
        for (i = 0; i < plat_dev.num_sockets; i++) {
-               sema_init(&plat_dev.sock[i].hsmp_sem, 1);
-               plat_dev.sock[i].sock_ind = i;
+               if (!node_to_amd_nb(i))
+                       return -ENODEV;
+               sock = &plat_dev.sock[i];
+               sock->root                      = node_to_amd_nb(i)->root;
+               sock->sock_ind                  = i;
+               sock->dev                       = dev;
+               sock->mbinfo.base_addr          = SMN_HSMP_BASE;
+
+               /*
+                * This is a transitional change from non-ACPI to ACPI, only
+                * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI.
+                */
+               if (is_f1a_m0h())
+                       sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H;
+               else
+                       sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID;
+
+               sock->mbinfo.msg_resp_off       = SMN_HSMP_MSG_RESP;
+               sock->mbinfo.msg_arg_off        = SMN_HSMP_MSG_DATA;
+               sema_init(&sock->hsmp_sem, 1);
+
+               /* Test the hsmp interface on each socket */
+               ret = hsmp_test(i, 0xDEADBEEF);
+               if (ret) {
+                       dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
+                               boot_cpu_data.x86, boot_cpu_data.x86_model);
+                       dev_err(dev, "Is HSMP disabled in BIOS ?\n");
+                       return ret;
+               }
        }
 
-       plat_dev.hsmp_device.name       = HSMP_CDEV_NAME;
-       plat_dev.hsmp_device.minor      = MISC_DYNAMIC_MINOR;
-       plat_dev.hsmp_device.fops       = &hsmp_fops;
-       plat_dev.hsmp_device.parent     = &pdev->dev;
-       plat_dev.hsmp_device.nodename   = HSMP_DEVNODE_NAME;
-       plat_dev.hsmp_device.mode       = 0644;
+       return 0;
+}
+
+static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
+       {ACPI_HSMP_DEVICE_HID, 0},
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
+
+static int hsmp_pltdrv_probe(struct platform_device *pdev)
+{
+       struct acpi_device *adev;
+       u16 sock_ind = 0;
+       int ret;
+
+       /*
+        * On ACPI supported BIOS, there is an ACPI HSMP device added for
+        * each socket, so the per socket probing, but the memory allocated for
+        * sockets should be contiguous to access it as an array,
+        * Hence allocate memory for all the sockets at once instead of allocating
+        * on each probe.
+        */
+       if (!plat_dev.is_probed) {
+               plat_dev.sock = devm_kcalloc(&pdev->dev, plat_dev.num_sockets,
+                                            sizeof(*plat_dev.sock),
+                                            GFP_KERNEL);
+               if (!plat_dev.sock)
+                       return -ENOMEM;
+       }
+       adev = ACPI_COMPANION(&pdev->dev);
+       if (adev && !acpi_match_device_ids(adev, amd_hsmp_acpi_ids)) {
+               ret = hsmp_get_uid(&pdev->dev, &sock_ind);
+               if (ret)
+                       return ret;
+               if (sock_ind >= plat_dev.num_sockets)
+                       return -EINVAL;
+               ret = hsmp_parse_acpi_table(&pdev->dev, sock_ind);
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed to parse ACPI table\n");
+                       return ret;
+               }
+               /* Test the hsmp interface */
+               ret = hsmp_test(sock_ind, 0xDEADBEEF);
+               if (ret) {
+                       dev_err(&pdev->dev, "HSMP test message failed on Fam:%x model:%x\n",
+                               boot_cpu_data.x86, boot_cpu_data.x86_model);
+                       dev_err(&pdev->dev, "Is HSMP disabled in BIOS ?\n");
+                       return ret;
+               }
+       } else {
+               ret = init_platform_device(&pdev->dev);
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
+                       return ret;
+               }
+       }
 
-       ret = hsmp_cache_proto_ver();
+       ret = hsmp_cache_proto_ver(sock_ind);
        if (ret) {
-               dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n");
+               dev_err(&pdev->dev, "Failed to read HSMP protocol version\n");
                return ret;
        }
 
-       ret = hsmp_create_sysfs_interface();
+       if (plat_dev.is_acpi_device)
+               ret = hsmp_create_acpi_sysfs_if(&pdev->dev);
+       else
+               ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev);
        if (ret)
-               dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n");
+               dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
+
+       if (!plat_dev.is_probed) {
+               plat_dev.hsmp_device.name       = HSMP_CDEV_NAME;
+               plat_dev.hsmp_device.minor      = MISC_DYNAMIC_MINOR;
+               plat_dev.hsmp_device.fops       = &hsmp_fops;
+               plat_dev.hsmp_device.parent     = &pdev->dev;
+               plat_dev.hsmp_device.nodename   = HSMP_DEVNODE_NAME;
+               plat_dev.hsmp_device.mode       = 0644;
+
+               ret = misc_register(&plat_dev.hsmp_device);
+               if (ret)
+                       return ret;
+
+               plat_dev.is_probed = true;
+       }
+
+       return 0;
 
-       return misc_register(&plat_dev.hsmp_device);
 }
 
 static void hsmp_pltdrv_remove(struct platform_device *pdev)
 {
-       misc_deregister(&plat_dev.hsmp_device);
+       /*
+        * We register only one misc_device even on multi socket system.
+        * So, deregister should happen only once.
+        */
+       if (plat_dev.is_probed) {
+               misc_deregister(&plat_dev.hsmp_device);
+               plat_dev.is_probed = false;
+       }
 }
 
 static struct platform_driver amd_hsmp_driver = {
@@ -536,15 +886,30 @@ static struct platform_driver amd_hsmp_driver = {
        .remove_new     = hsmp_pltdrv_remove,
        .driver         = {
                .name   = DRIVER_NAME,
+               .acpi_match_table = amd_hsmp_acpi_ids,
        },
 };
 
 static struct platform_device *amd_hsmp_platdev;
 
+static int hsmp_plat_dev_register(void)
+{
+       int ret;
+
+       amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
+       if (!amd_hsmp_platdev)
+               return -ENOMEM;
+
+       ret = platform_device_add(amd_hsmp_platdev);
+       if (ret)
+               platform_device_put(amd_hsmp_platdev);
+
+       return ret;
+}
+
 static int __init hsmp_plt_init(void)
 {
        int ret = -ENODEV;
-       int i;
 
        if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) {
                pr_err("HSMP is not supported on Family:%x model:%x\n",
@@ -557,40 +922,19 @@ static int __init hsmp_plt_init(void)
         * if we have N SMN/DF interfaces that ideally means N sockets
         */
        plat_dev.num_sockets = amd_nb_num();
-       if (plat_dev.num_sockets == 0)
+       if (plat_dev.num_sockets == 0 || plat_dev.num_sockets > MAX_AMD_SOCKETS)
                return ret;
 
-       /* Test the hsmp interface on each socket */
-       for (i = 0; i < plat_dev.num_sockets; i++) {
-               ret = hsmp_test(i, 0xDEADBEEF);
-               if (ret) {
-                       pr_err("HSMP test message failed on Fam:%x model:%x\n",
-                              boot_cpu_data.x86, boot_cpu_data.x86_model);
-                       pr_err("Is HSMP disabled in BIOS ?\n");
-                       return ret;
-               }
-       }
-
        ret = platform_driver_register(&amd_hsmp_driver);
        if (ret)
                return ret;
 
-       amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
-       if (!amd_hsmp_platdev) {
-               ret = -ENOMEM;
-               goto drv_unregister;
-       }
-
-       ret = platform_device_add(amd_hsmp_platdev);
-       if (ret) {
-               platform_device_put(amd_hsmp_platdev);
-               goto drv_unregister;
+       if (!plat_dev.is_acpi_device) {
+               ret = hsmp_plat_dev_register();
+               if (ret)
+                       platform_driver_unregister(&amd_hsmp_driver);
        }
 
-       return 0;
-
-drv_unregister:
-       platform_driver_unregister(&amd_hsmp_driver);
        return ret;
 }
 
index f2eb07ef855afbb09da5b4c0a24650900b787652..d0cf46e2fc8e8a073149c61c52b27e9cc9051da6 100644 (file)
@@ -90,12 +90,96 @@ out:
        return err;
 }
 
+static union acpi_object *apts_if_call(struct amd_pmf_dev *pdev, u32 state_index)
+{
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       acpi_handle ahandle = ACPI_HANDLE(pdev->dev);
+       struct acpi_object_list apts_if_arg_list;
+       union acpi_object apts_if_args[3];
+       acpi_status status;
+
+       apts_if_arg_list.count = 3;
+       apts_if_arg_list.pointer = &apts_if_args[0];
+
+       apts_if_args[0].type = ACPI_TYPE_INTEGER;
+       apts_if_args[0].integer.value = 1;
+       apts_if_args[1].type = ACPI_TYPE_INTEGER;
+       apts_if_args[1].integer.value = state_index;
+       apts_if_args[2].type = ACPI_TYPE_INTEGER;
+       apts_if_args[2].integer.value = 0;
+
+       status = acpi_evaluate_object(ahandle, "APTS", &apts_if_arg_list, &buffer);
+       if (ACPI_FAILURE(status)) {
+               dev_err(pdev->dev, "APTS state_idx:%u call failed\n", state_index);
+               kfree(buffer.pointer);
+               return NULL;
+       }
+
+       return buffer.pointer;
+}
+
+static int apts_if_call_store_buffer(struct amd_pmf_dev *pdev,
+                                    u32 index, void *data, size_t out_sz)
+{
+       union acpi_object *info;
+       size_t size;
+       int err = 0;
+
+       info = apts_if_call(pdev, index);
+       if (!info)
+               return -EIO;
+
+       if (info->type != ACPI_TYPE_BUFFER) {
+               dev_err(pdev->dev, "object is not a buffer\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       size = *(u16 *)info->buffer.pointer;
+       if (info->buffer.length < size) {
+               dev_err(pdev->dev, "buffer smaller than header size %u < %zu\n",
+                       info->buffer.length, size);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (size < out_sz) {
+               dev_err(pdev->dev, "buffer too small %zu\n", size);
+               err = -EINVAL;
+               goto out;
+       }
+
+       memcpy(data, info->buffer.pointer, out_sz);
+out:
+       kfree(info);
+       return err;
+}
+
 int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
 {
        /* If bit-n is set, that indicates function n+1 is supported */
        return !!(pdev->supported_func & BIT(index - 1));
 }
 
+int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
+                                      struct amd_pmf_apts_granular_output *data, u32 apts_idx)
+{
+       if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
+               return -EINVAL;
+
+       return apts_if_call_store_buffer(pdev, apts_idx, data, sizeof(*data));
+}
+
+int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
+                                      struct apmf_static_slider_granular_output_v2 *data)
+{
+       if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
+               return -EINVAL;
+
+       return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR,
+                                        data, sizeof(*data));
+}
+
 int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
                                    struct apmf_static_slider_granular_output *data)
 {
@@ -140,6 +224,43 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
        kfree(info);
 }
 
+int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag)
+{
+       struct sbios_hb_event_v2 args = { };
+       struct acpi_buffer params;
+       union acpi_object *info;
+
+       args.size = sizeof(args);
+
+       switch (flag) {
+       case ON_LOAD:
+               args.load = 1;
+               break;
+       case ON_UNLOAD:
+               args.unload = 1;
+               break;
+       case ON_SUSPEND:
+               args.suspend = 1;
+               break;
+       case ON_RESUME:
+               args.resume = 1;
+               break;
+       default:
+               dev_dbg(dev->dev, "Failed to send v2 heartbeat event, flag:0x%x\n", flag);
+               return -EINVAL;
+       }
+
+       params.length = sizeof(args);
+       params.pointer = &args;
+
+       info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2, &params);
+       if (!info)
+               return -EIO;
+
+       kfree(info);
+       return 0;
+}
+
 int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
 {
        union acpi_object *info;
@@ -166,6 +287,11 @@ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data
        return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data));
 }
 
+int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req)
+{
+       return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req));
+}
+
 int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req)
 {
        return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS,
@@ -218,8 +344,10 @@ static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
                return err;
 
        pdev->supported_func = output.supported_functions;
-       dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
-               output.supported_functions, output.notification_mask);
+       dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x version:%u\n",
+               output.supported_functions, output.notification_mask, output.version);
+
+       pdev->pmf_if_version = output.version;
 
        return 0;
 }
@@ -320,7 +448,7 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
 {
        acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
 
-       if (pmf_dev->hb_interval)
+       if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1)
                cancel_delayed_work_sync(&pmf_dev->heart_beat);
 
        if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) &&
@@ -344,7 +472,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
                goto out;
        }
 
-       if (pmf_dev->hb_interval) {
+       if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1) {
                /* send heartbeats only if the interval is not zero */
                INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
                schedule_delayed_work(&pmf_dev->heart_beat, 0);
index 4f734e049f4a46b60b139cf38ec7c7a2e193a4f6..5d4f80698a8b8824bdb59b4e5632ca5f05982c48 100644 (file)
@@ -113,8 +113,9 @@ static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
 static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
 {
        dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
-       debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
-                           &current_power_limits_fops);
+       if (dev->pmf_if_version == PMF_IF_V1)
+               debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
+                                   &current_power_limits_fops);
 }
 
 int amd_pmf_get_power_source(void)
@@ -299,6 +300,9 @@ static int amd_pmf_suspend_handler(struct device *dev)
        if (pdev->smart_pc_enabled)
                cancel_delayed_work_sync(&pdev->pb_work);
 
+       if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+               amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_SUSPEND);
+
        return 0;
 }
 
@@ -313,6 +317,9 @@ static int amd_pmf_resume_handler(struct device *dev)
                        return ret;
        }
 
+       if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+               amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_RESUME);
+
        if (pdev->smart_pc_enabled)
                schedule_delayed_work(&pdev->pb_work, msecs_to_jiffies(2000));
 
@@ -443,6 +450,8 @@ static int amd_pmf_probe(struct platform_device *pdev)
        amd_pmf_dbgfs_register(dev);
        amd_pmf_init_features(dev);
        apmf_install_handler(dev);
+       if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+               amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
 
        dev_info(dev->dev, "registered PMF device successfully\n");
 
@@ -454,6 +463,8 @@ static void amd_pmf_remove(struct platform_device *pdev)
        struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
 
        amd_pmf_deinit_features(dev);
+       if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+               amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD);
        apmf_acpi_deinit(dev);
        amd_pmf_dbgfs_unregister(dev);
        mutex_destroy(&dev->lock);
index 66cae1cca73cc16b73210e49af1c836c3da4d260..8c4df5753f40d48fefc05c6373a64d0a00469149 100644 (file)
 #define POLICY_BUF_MAX_SZ              0x4b000
 #define POLICY_SIGN_COOKIE             0x31535024
 #define POLICY_COOKIE_OFFSET           0x10
-#define POLICY_COOKIE_LEN              0x14
+
+struct cookie_header {
+       u32 sign;
+       u32 length;
+} __packed;
 
 /* APMF Functions */
 #define APMF_FUNC_VERIFY_INTERFACE                     0
@@ -30,6 +34,7 @@
 #define APMF_FUNC_STATIC_SLIDER_GRANULAR       9
 #define APMF_FUNC_DYN_SLIDER_AC                                11
 #define APMF_FUNC_DYN_SLIDER_DC                                12
+#define APMF_FUNC_SBIOS_HEARTBEAT_V2                   16
 
 /* Message Definitions */
 #define SET_SPL                                0x03 /* SPL: Sustained Power Limit */
@@ -50,6 +55,8 @@
 #define GET_STT_LIMIT_APU      0x20
 #define GET_STT_LIMIT_HS2      0x21
 #define SET_P3T                                0x23 /* P3T: Peak Package Power Limit */
+#define SET_PMF_PPT            0x25
+#define SET_PMF_PPT_APU_ONLY   0x26
 
 /* OS slider update notification */
 #define DC_BEST_PERF           0
 #define TA_OUTPUT_RESERVED_MEM                         906
 #define MAX_OPERATION_PARAMS                                   4
 
+#define PMF_IF_V1              1
+#define PMF_IF_V2              2
+
+#define APTS_MAX_STATES                16
+
+/* APTS PMF BIOS Interface */
+struct amd_pmf_apts_output {
+       u16 table_version;
+       u32 fan_table_idx;
+       u32 pmf_ppt;
+       u32 ppt_pmf_apu_only;
+       u32 stt_min_limit;
+       u8 stt_skin_temp_limit_apu;
+       u8 stt_skin_temp_limit_hs2;
+} __packed;
+
+struct amd_pmf_apts_granular_output {
+       u16 size;
+       struct amd_pmf_apts_output val;
+} __packed;
+
+struct amd_pmf_apts_granular {
+       u16 size;
+       struct amd_pmf_apts_output val[APTS_MAX_STATES];
+};
+
+struct sbios_hb_event_v2 {
+       u16 size;
+       u8 load;
+       u8 unload;
+       u8 suspend;
+       u8 resume;
+} __packed;
+
+enum sbios_hb_v2 {
+       ON_LOAD,
+       ON_UNLOAD,
+       ON_SUSPEND,
+       ON_RESUME,
+};
+
 /* AMD PMF BIOS interfaces */
 struct apmf_verify_interface {
        u16 size;
@@ -114,6 +162,18 @@ struct apmf_sbios_req {
        u8 skin_temp_hs2;
 } __packed;
 
+struct apmf_sbios_req_v2 {
+       u16 size;
+       u32 pending_req;
+       u8 rsd;
+       u32 ppt_pmf;
+       u32 ppt_pmf_apu_only;
+       u32 stt_min_limit;
+       u8 skin_temp_apu;
+       u8 skin_temp_hs2;
+       u32 custom_policy[10];
+} __packed;
+
 struct apmf_fan_idx {
        u16 size;
        u8 fan_ctl_mode;
@@ -194,6 +254,14 @@ enum power_modes {
        POWER_MODE_MAX,
 };
 
+enum power_modes_v2 {
+       POWER_MODE_BEST_PERFORMANCE,
+       POWER_MODE_BALANCED,
+       POWER_MODE_BEST_POWER_EFFICIENCY,
+       POWER_MODE_ENERGY_SAVE,
+       POWER_MODE_V2_MAX,
+};
+
 struct amd_pmf_dev {
        void __iomem *regbase;
        void __iomem *smu_virt_addr;
@@ -229,10 +297,15 @@ struct amd_pmf_dev {
        struct delayed_work pb_work;
        struct pmf_action_table *prev_data;
        u64 policy_addr;
-       void *policy_base;
+       void __iomem *policy_base;
        bool smart_pc_enabled;
+       u16 pmf_if_version;
 };
 
+struct apmf_sps_prop_granular_v2 {
+       u8 power_states[POWER_SOURCE_MAX][POWER_MODE_V2_MAX];
+} __packed;
+
 struct apmf_sps_prop_granular {
        u32 fppt;
        u32 sppt;
@@ -254,6 +327,16 @@ struct amd_pmf_static_slider_granular {
        struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
 };
 
+struct apmf_static_slider_granular_output_v2 {
+       u16 size;
+       struct apmf_sps_prop_granular_v2 sps_idx;
+} __packed;
+
+struct amd_pmf_static_slider_granular_v2 {
+       u16 size;
+       struct apmf_sps_prop_granular_v2 sps_idx;
+};
+
 struct os_power_slider {
        u16 size;
        u8 slider_event;
@@ -585,6 +668,7 @@ int amd_pmf_get_power_source(void);
 int apmf_install_handler(struct amd_pmf_dev *pmf_dev);
 int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
 int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
+int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
 
 /* SPS Layer */
 int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
@@ -602,6 +686,10 @@ const char *amd_pmf_source_as_str(unsigned int state);
 
 int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx);
 int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf);
+int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *dev,
+                                      struct apmf_static_slider_granular_output_v2 *data);
+int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
+                                      struct amd_pmf_apts_granular_output *data, u32 apts_idx);
 
 /* Auto Mode Layer */
 int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data);
@@ -609,6 +697,7 @@ void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
 void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
 void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
 int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req);
+int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req);
 
 void amd_pmf_update_2_cql(struct amd_pmf_dev *dev, bool is_cql_event);
 int amd_pmf_reset_amt(struct amd_pmf_dev *dev);
index 33e23e25c8b1efe6c2c032ecdcbfdb6672949128..92f7fb22277dca773a6b88f404bd94ea1e697a9c 100644 (file)
 
 #include "pmf.h"
 
+static struct amd_pmf_static_slider_granular_v2 config_store_v2;
 static struct amd_pmf_static_slider_granular config_store;
+static struct amd_pmf_apts_granular apts_config_store;
 
 #ifdef CONFIG_AMD_PMF_DEBUG
+static const char *slider_v2_as_str(unsigned int state)
+{
+       switch (state) {
+       case POWER_MODE_BEST_PERFORMANCE:
+               return "Best Performance";
+       case POWER_MODE_BALANCED:
+               return "Balanced";
+       case POWER_MODE_BEST_POWER_EFFICIENCY:
+               return "Best Power Efficiency";
+       case POWER_MODE_ENERGY_SAVE:
+               return "Energy Save";
+       default:
+               return "Unknown Power Mode";
+       }
+}
+
 static const char *slider_as_str(unsigned int state)
 {
        switch (state) {
@@ -63,10 +81,88 @@ static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *dat
 
        pr_debug("Static Slider Data - END\n");
 }
+
+static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data)
+{
+       unsigned int i, j;
+
+       pr_debug("Static Slider APTS state index data - BEGIN");
+       pr_debug("size: %u\n", data->size);
+
+       for (i = 0; i < POWER_SOURCE_MAX; i++)
+               for (j = 0; j < POWER_MODE_V2_MAX; j++)
+                       pr_debug("%s %s: %u\n", amd_pmf_source_as_str(i), slider_v2_as_str(j),
+                                data->sps_idx.power_states[i][j]);
+
+       pr_debug("Static Slider APTS state index data - END\n");
+}
+
+static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info)
+{
+       int i;
+
+       pr_debug("Static Slider APTS index default values data - BEGIN");
+
+       for (i = 0; i < APTS_MAX_STATES; i++) {
+               pr_debug("Table Version[%d] = %u\n", i, info->val[i].table_version);
+               pr_debug("Fan Index[%d] = %u\n", i, info->val[i].fan_table_idx);
+               pr_debug("PPT[%d] = %u\n", i, info->val[i].pmf_ppt);
+               pr_debug("PPT APU[%d] = %u\n", i, info->val[i].ppt_pmf_apu_only);
+               pr_debug("STT Min[%d] = %u\n", i, info->val[i].stt_min_limit);
+               pr_debug("STT APU[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_apu);
+               pr_debug("STT HS2[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_hs2);
+       }
+
+       pr_debug("Static Slider APTS index default values data - END");
+}
 #else
 static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data) {}
+static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data) {}
+static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info) {}
 #endif
 
+static void amd_pmf_load_apts_defaults_sps_v2(struct amd_pmf_dev *pdev)
+{
+       struct amd_pmf_apts_granular_output output;
+       struct amd_pmf_apts_output *ps;
+       int i;
+
+       memset(&apts_config_store, 0, sizeof(apts_config_store));
+
+       ps = apts_config_store.val;
+
+       for (i = 0; i < APTS_MAX_STATES; i++) {
+               apts_get_static_slider_granular_v2(pdev, &output, i);
+               ps[i].table_version = output.val.table_version;
+               ps[i].fan_table_idx = output.val.fan_table_idx;
+               ps[i].pmf_ppt = output.val.pmf_ppt;
+               ps[i].ppt_pmf_apu_only = output.val.ppt_pmf_apu_only;
+               ps[i].stt_min_limit = output.val.stt_min_limit;
+               ps[i].stt_skin_temp_limit_apu = output.val.stt_skin_temp_limit_apu;
+               ps[i].stt_skin_temp_limit_hs2 = output.val.stt_skin_temp_limit_hs2;
+       }
+
+       amd_pmf_dump_apts_sps_defaults(&apts_config_store);
+}
+
+static void amd_pmf_load_defaults_sps_v2(struct amd_pmf_dev *dev)
+{
+       struct apmf_static_slider_granular_output_v2 output;
+       unsigned int i, j;
+
+       memset(&config_store_v2, 0, sizeof(config_store_v2));
+       apmf_get_static_slider_granular_v2(dev, &output);
+
+       config_store_v2.size = output.size;
+
+       for (i = 0; i < POWER_SOURCE_MAX; i++)
+               for (j = 0; j < POWER_MODE_V2_MAX; j++)
+                       config_store_v2.sps_idx.power_states[i][j] =
+                                                       output.sps_idx.power_states[i][j];
+
+       amd_pmf_dump_sps_defaults_v2(&config_store_v2);
+}
+
 static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
 {
        struct apmf_static_slider_granular_output output;
@@ -94,6 +190,19 @@ static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
        amd_pmf_dump_sps_defaults(&config_store);
 }
 
+static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
+{
+       amd_pmf_send_cmd(dev, SET_PMF_PPT, false, apts_config_store.val[idx].pmf_ppt, NULL);
+       amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false,
+                        apts_config_store.val[idx].ppt_pmf_apu_only, NULL);
+       amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
+                        apts_config_store.val[idx].stt_min_limit, NULL);
+       amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
+                        apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL);
+       amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+                        apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL);
+}
+
 void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
                           struct amd_pmf_static_slider_granular *table)
 {
@@ -126,6 +235,32 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
        }
 }
 
+static int amd_pmf_update_sps_power_limits_v2(struct amd_pmf_dev *pdev, int pwr_mode)
+{
+       int src, index;
+
+       src = amd_pmf_get_power_source();
+
+       switch (pwr_mode) {
+       case POWER_MODE_PERFORMANCE:
+               index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_PERFORMANCE];
+               amd_pmf_update_slider_v2(pdev, index);
+               break;
+       case POWER_MODE_BALANCED_POWER:
+               index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BALANCED];
+               amd_pmf_update_slider_v2(pdev, index);
+               break;
+       case POWER_MODE_POWER_SAVER:
+               index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_POWER_EFFICIENCY];
+               amd_pmf_update_slider_v2(pdev, index);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
 {
        int mode;
@@ -134,6 +269,9 @@ int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
        if (mode < 0)
                return mode;
 
+       if (pmf->pmf_if_version == PMF_IF_V2)
+               return amd_pmf_update_sps_power_limits_v2(pmf, mode);
+
        amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
 
        return 0;
@@ -256,7 +394,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
        dev->current_profile = PLATFORM_PROFILE_BALANCED;
 
        if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
-               amd_pmf_load_defaults_sps(dev);
+               if (dev->pmf_if_version == PMF_IF_V2) {
+                       amd_pmf_load_defaults_sps_v2(dev);
+                       amd_pmf_load_apts_defaults_sps_v2(dev);
+               } else {
+                       amd_pmf_load_defaults_sps(dev);
+               }
 
                /* update SPS balanced power mode thermals */
                amd_pmf_set_sps_power_limits(dev);
index dcbe8f85e122947be014778c14478a7374698b49..b438de4d6bfcea2bd8f7b2c39a4df3bea438ddc2 100644 (file)
@@ -246,19 +246,24 @@ static void amd_pmf_invoke_cmd(struct work_struct *work)
 
 static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
 {
-       u32 cookie, length;
+       struct cookie_header *header;
        int res;
 
-       cookie = readl(dev->policy_buf + POLICY_COOKIE_OFFSET);
-       length = readl(dev->policy_buf + POLICY_COOKIE_LEN);
+       if (dev->policy_sz < POLICY_COOKIE_OFFSET + sizeof(*header))
+               return -EINVAL;
+
+       header = (struct cookie_header *)(dev->policy_buf + POLICY_COOKIE_OFFSET);
 
-       if (cookie != POLICY_SIGN_COOKIE || !length) {
+       if (header->sign != POLICY_SIGN_COOKIE || !header->length) {
                dev_dbg(dev->dev, "cookie doesn't match\n");
                return -EINVAL;
        }
 
+       if (dev->policy_sz < header->length + 512)
+               return -EINVAL;
+
        /* Update the actual length */
-       dev->policy_sz = length + 512;
+       dev->policy_sz = header->length + 512;
        res = amd_pmf_invoke_cmd_init(dev);
        if (res == TA_PMF_TYPE_SUCCESS) {
                /* Now its safe to announce that smart pc is enabled */
@@ -271,7 +276,7 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
        } else {
                dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res);
                dev->smart_pc_enabled = false;
-               return res;
+               return -EIO;
        }
 
        return 0;
@@ -311,8 +316,8 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
 
        amd_pmf_hex_dump_pb(dev);
        ret = amd_pmf_start_policy_engine(dev);
-       if (ret)
-               return -EINVAL;
+       if (ret < 0)
+               return ret;
 
        return length;
 }
@@ -453,7 +458,7 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
                goto error;
        }
 
-       memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz);
+       memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz);
 
        amd_pmf_hex_dump_pb(dev);
 
index 18be35fdb3814d1d6659f48d3fc871bf30ee581d..3f07bbf809ef01990c7e3bc64f02dc4eddfaf435 100644 (file)
@@ -101,13 +101,6 @@ module_param(fnlock_default, bool, 0444);
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
 #define ASUS_ACPI_UID_ASUSWMI          "ASUSWMI"
-#define ASUS_ACPI_UID_ATK              "ATK"
-
-#define WMI_EVENT_QUEUE_SIZE           0x10
-#define WMI_EVENT_QUEUE_END            0x1
-#define WMI_EVENT_MASK                 0xFFFF
-/* The WMI hotkey event value is always the same. */
-#define WMI_EVENT_VALUE_ATK            0xFF
 
 #define WMI_EVENT_MASK                 0xFFFF
 
@@ -219,7 +212,6 @@ struct asus_wmi {
        int dsts_id;
        int spec;
        int sfun;
-       bool wmi_event_queue;
 
        struct input_dev *inputdev;
        struct backlight_device *backlight_device;
@@ -489,7 +481,17 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
 
 static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
 {
-       return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
+       int err;
+
+       err = asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
+
+       if (err)
+               return err;
+
+       if (*retval == ~0)
+               return -ENODEV;
+
+       return 0;
 }
 
 static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
@@ -1620,7 +1622,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
        if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) {
                asus->micmute_led.name = "platform::micmute";
                asus->micmute_led.max_brightness = 1;
-               asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
                asus->micmute_led.brightness_set_blocking = micmute_led_set;
                asus->micmute_led.default_trigger = "audio-micmute";
 
@@ -4020,50 +4021,14 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
 static void asus_wmi_notify(u32 value, void *context)
 {
        struct asus_wmi *asus = context;
-       int code;
-       int i;
-
-       for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
-               code = asus_wmi_get_event_code(value);
-               if (code < 0) {
-                       pr_warn("Failed to get notify code: %d\n", code);
-                       return;
-               }
-
-               if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
-                       return;
+       int code = asus_wmi_get_event_code(value);
 
-               asus_wmi_handle_event_code(code, asus);
-
-               /*
-                * Double check that queue is present:
-                * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
-                */
-               if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
-                       return;
-       }
-
-       pr_warn("Failed to process event queue, last code: 0x%x\n", code);
-}
-
-static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
-{
-       int code;
-       int i;
-
-       for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
-               code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
-               if (code < 0) {
-                       pr_warn("Failed to get event during flush: %d\n", code);
-                       return code;
-               }
-
-               if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
-                       return 0;
+       if (code < 0) {
+               pr_warn("Failed to get notify code: %d\n", code);
+               return;
        }
 
-       pr_warn("Failed to flush event queue\n");
-       return -EIO;
+       asus_wmi_handle_event_code(code, asus);
 }
 
 /* Sysfs **********************************************************************/
@@ -4303,23 +4268,6 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
                asus->dsts_id = ASUS_WMI_METHODID_DSTS;
        }
 
-       /*
-        * Some devices can have multiple event codes stored in a queue before
-        * the module load if it was unloaded intermittently after calling
-        * the INIT method (enables event handling). The WMI notify handler is
-        * expected to retrieve all event codes until a retrieved code equals
-        * queue end marker (One or Ones). Old codes are flushed from the queue
-        * upon module load. Not enabling this when it should be has minimal
-        * visible impact so fall back if anything goes wrong.
-        */
-       wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
-       if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
-               dev_info(dev, "Detected ATK, enable event queue\n");
-
-               if (!asus_wmi_notify_queue_flush(asus))
-                       asus->wmi_event_queue = true;
-       }
-
        /* CWAP allow to define the behavior of the Fn+F2 key,
         * this method doesn't seems to be present on Eee PCs */
        if (asus->driver->quirks->wapf >= 0)
index e712df67fa6be047c71061083b75ef3b8269a679..bd9f445974cc683abad5a7734970becff10feb9e 100644 (file)
@@ -57,8 +57,6 @@ config DELL_LAPTOP
        select POWER_SUPPLY
        select LEDS_CLASS
        select NEW_LEDS
-       select LEDS_TRIGGERS
-       select LEDS_TRIGGER_AUDIO
        help
        This driver adds support for rfkill and backlight control to Dell
        laptops (except for some models covered by the Compal driver).
@@ -165,7 +163,6 @@ config DELL_WMI
 
 config DELL_WMI_PRIVACY
        bool "Dell WMI Hardware Privacy Support"
-       depends on LEDS_TRIGGER_AUDIO = y || DELL_WMI = LEDS_TRIGGER_AUDIO
        depends on DELL_WMI
        help
          This option adds integration with the "Dell Hardware Privacy"
index 6586438356de76fbab93cd88d85ef6c88574d852..42f7de2b45223fe904649b897636840f37361f65 100644 (file)
@@ -2252,7 +2252,6 @@ static int __init dell_init(void)
        if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
            dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) &&
            !dell_privacy_has_mic_mute()) {
-               micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
                ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
                if (ret < 0)
                        goto fail_led;
@@ -2261,7 +2260,6 @@ static int __init dell_init(void)
 
        if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) &&
            dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) {
-               mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE);
                ret = led_classdev_register(&platform_device->dev, &mute_led_cdev);
                if (ret < 0)
                        goto fail_backlight;
index db1e9240dd02c27e07926dd865222288632e3d4e..0b2299f7a2de5aacab3847e1956c62e71fbbc6be 100644 (file)
@@ -882,6 +882,7 @@ static struct wmi_driver dell_wmi_ddv_driver = {
        },
        .id_table = dell_wmi_ddv_id_table,
        .probe = dell_wmi_ddv_probe,
+       .no_singleton = true,
 };
 module_wmi_driver(dell_wmi_ddv_driver);
 
index c517bd45dd32e4154f239e01ccf1bd98c606ded2..4b65e1655d42a0ac982cb9cd5ee5183e2f363200 100644 (file)
@@ -288,7 +288,6 @@ static int dell_privacy_leds_setup(struct device *dev)
        priv->cdev.max_brightness = 1;
        priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set;
        priv->cdev.default_trigger = "audio-micmute";
-       priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
        return devm_led_classdev_register(dev, &priv->cdev);
 }
 
@@ -298,10 +297,6 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
        struct key_entry *keymap;
        int ret, i, j;
 
-       ret = wmi_has_guid(DELL_PRIVACY_GUID);
-       if (!ret)
-               pr_debug("Unable to detect available Dell privacy devices!\n");
-
        priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
index b929b4f824205e092930e6e790707aa227358924..9def7983d7d66ad4aa7d084915014f87b9480447 100644 (file)
@@ -25,7 +25,7 @@ struct wmi_sysman_priv wmi_priv = {
 /* reset bios to defaults */
 static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
 static int reset_option = -1;
-static struct class *fw_attr_class;
+static const struct class *fw_attr_class;
 
 
 /**
index fafe8eaf6e3e4e164c325584302f9389bfc4f737..dd8240009565dde6381c6103a93110f181463b68 100644 (file)
 static DEFINE_MUTEX(fw_attr_lock);
 static int fw_attr_inuse;
 
-static struct class firmware_attributes_class = {
+static const struct class firmware_attributes_class = {
        .name = "firmware-attributes",
 };
 
-int fw_attributes_class_get(struct class **fw_attr_class)
+int fw_attributes_class_get(const struct class **fw_attr_class)
 {
        int err;
 
index 486485cb1f54e3f5f89cb48ad0e65345d58b22a0..363c75f1ac1b89df879a8689b070e6b11d3bb7fd 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef FW_ATTR_CLASS_H
 #define FW_ATTR_CLASS_H
 
-int fw_attributes_class_get(struct class **fw_attr_class);
+int fw_attributes_class_get(const struct class **fw_attr_class);
 int fw_attributes_class_put(void);
 
 #endif /* FW_ATTR_CLASS_H */
index 085e044e888e4e788fa99894841e4d538d042bca..94480af4946783e60e4ee1940950df2d0dc0132e 100644 (file)
@@ -49,6 +49,8 @@
 #include <linux/kfifo.h>
 #include <linux/leds.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <acpi/battery.h>
 #include <acpi/video.h>
 
 #define FUJITSU_DRIVER_VERSION         "0.6.0"
 #define BACKLIGHT_OFF                  (BIT(0) | BIT(1))
 #define BACKLIGHT_ON                   0
 
+/* FUNC interface - battery control interface */
+#define FUNC_S006_METHOD               0x1006
+#define CHARGE_CONTROL_RW              0x21
+
 /* Scancodes read from the GIRB register */
 #define KEY1_CODE                      0x410
 #define KEY2_CODE                      0x411
@@ -132,6 +138,7 @@ struct fujitsu_laptop {
        spinlock_t fifo_lock;
        int flags_supported;
        int flags_state;
+       bool charge_control_supported;
 };
 
 static struct acpi_device *fext;
@@ -164,6 +171,110 @@ static int call_fext_func(struct acpi_device *device,
        return value;
 }
 
+/* Battery charge control code */
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       int cc_end_value, s006_cc_return;
+       int value, ret;
+
+       ret = kstrtouint(buf, 10, &value);
+       if (ret)
+               return ret;
+
+       if (value < 50 || value > 100)
+               return -EINVAL;
+
+       cc_end_value = value * 0x100 + 0x20;
+       s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+                                       CHARGE_CONTROL_RW, cc_end_value, 0x0);
+       if (s006_cc_return < 0)
+               return s006_cc_return;
+       /*
+        * The S006 0x21 method returns 0x00 in case the provided value
+        * is invalid.
+        */
+       if (s006_cc_return == 0x00)
+               return -EINVAL;
+
+       return count;
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       int status;
+
+       status = call_fext_func(fext, FUNC_S006_METHOD,
+                               CHARGE_CONTROL_RW, 0x21, 0x0);
+       if (status < 0)
+               return status;
+
+       return sysfs_emit(buf, "%d\n", status);
+}
+
+static DEVICE_ATTR_RW(charge_control_end_threshold);
+
+/* ACPI battery hook */
+static int fujitsu_battery_add_hook(struct power_supply *battery,
+                              struct acpi_battery_hook *hook)
+{
+       return device_create_file(&battery->dev,
+                                 &dev_attr_charge_control_end_threshold);
+}
+
+static int fujitsu_battery_remove_hook(struct power_supply *battery,
+                                 struct acpi_battery_hook *hook)
+{
+       device_remove_file(&battery->dev,
+                          &dev_attr_charge_control_end_threshold);
+
+       return 0;
+}
+
+static struct acpi_battery_hook battery_hook = {
+       .add_battery = fujitsu_battery_add_hook,
+       .remove_battery = fujitsu_battery_remove_hook,
+       .name = "Fujitsu Battery Extension",
+};
+
+/*
+ * These functions are intended to be called from acpi_fujitsu_laptop_add and
+ * acpi_fujitsu_laptop_remove.
+ */
+static int fujitsu_battery_charge_control_add(struct acpi_device *device)
+{
+       struct fujitsu_laptop *priv = acpi_driver_data(device);
+       int s006_cc_return;
+
+       priv->charge_control_supported = false;
+       /*
+        * Check if the S006 0x21 method exists by trying to get the current
+        * battery charge limit.
+        */
+       s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+                                       CHARGE_CONTROL_RW, 0x21, 0x0);
+       if (s006_cc_return < 0)
+               return s006_cc_return;
+       if (s006_cc_return == UNSUPPORTED_CMD)
+               return -ENODEV;
+
+       priv->charge_control_supported = true;
+       battery_hook_register(&battery_hook);
+
+       return 0;
+}
+
+static void fujitsu_battery_charge_control_remove(struct acpi_device *device)
+{
+       struct fujitsu_laptop *priv = acpi_driver_data(device);
+
+       if (priv->charge_control_supported)
+               battery_hook_unregister(&battery_hook);
+}
+
 /* Hardware access for LCD brightness control */
 
 static int set_lcd_level(struct acpi_device *device, int level)
@@ -839,6 +950,10 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
        if (ret)
                goto err_free_fifo;
 
+       ret = fujitsu_battery_charge_control_add(device);
+       if (ret < 0)
+               pr_warn("Unable to register battery charge control: %d\n", ret);
+
        return 0;
 
 err_free_fifo:
@@ -851,6 +966,8 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device)
 {
        struct fujitsu_laptop *priv = acpi_driver_data(device);
 
+       fujitsu_battery_charge_control_remove(device);
+
        fujitsu_laptop_platform_remove(device);
 
        kfifo_free(&priv->fifo);
index 8c9f4f3227fc6dcf244210edd932301f491efb15..2dc50152158a3dd0401d9d9e1de288c0d76ef937 100644 (file)
@@ -24,7 +24,7 @@ struct bioscfg_priv bioscfg_drv = {
        .mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
 };
 
-static struct class *fw_attr_class;
+static const struct class *fw_attr_class;
 
 ssize_t display_name_language_code_show(struct kobject *kobj,
                                        struct kobj_attribute *attr,
index e536604225c51a4a8ead58090e3e7d7a9a0c36a6..630519c086171deb88939f134b49746ae6915084 100644 (file)
 #include <linux/dmi.h>
 
 MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
-MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
+MODULE_DESCRIPTION("HP laptop WMI driver");
 MODULE_LICENSE("GPL");
 
 MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
-MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
+MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
 
 #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
-#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
+
+#define HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET 0x62
+#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
 #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
+
 #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
 
 /* DMI board names of devices that should use the omen specific path for
@@ -55,17 +59,25 @@ static const char * const omen_thermal_profile_boards[] = {
        "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C",
        "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD",
        "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912",
-       "8917", "8918", "8949", "894A", "89EB"
+       "8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42"
 };
 
 /* DMI Board names of Omen laptops that are specifically set to be thermal
  * profile version 0 by the Omen Command Center app, regardless of what
  * the get system design information WMI call returns
  */
-static const char *const omen_thermal_profile_force_v0_boards[] = {
+static const char * const omen_thermal_profile_force_v0_boards[] = {
        "8607", "8746", "8747", "8749", "874A", "8748"
 };
 
+/* DMI board names of Omen laptops that have a thermal profile timer which will
+ * cause the embedded controller to set the thermal profile back to
+ * "balanced" when reaching zero.
+ */
+static const char * const omen_timed_thermal_profile_boards[] = {
+       "8BAD", "8A42"
+};
+
 /* DMI Board names of Victus laptops */
 static const char * const victus_thermal_profile_boards[] = {
        "8A25"
@@ -182,6 +194,12 @@ enum hp_thermal_profile_omen_v1 {
        HP_OMEN_V1_THERMAL_PROFILE_COOL         = 0x50,
 };
 
+enum hp_thermal_profile_omen_flags {
+       HP_OMEN_EC_FLAGS_TURBO          = 0x04,
+       HP_OMEN_EC_FLAGS_NOTIMER        = 0x02,
+       HP_OMEN_EC_FLAGS_JUSTSET        = 0x01,
+};
+
 enum hp_thermal_profile_victus {
        HP_VICTUS_THERMAL_PROFILE_DEFAULT               = 0x00,
        HP_VICTUS_THERMAL_PROFILE_PERFORMANCE           = 0x01,
@@ -449,7 +467,11 @@ static int hp_wmi_get_tablet_mode(void)
 
 static int omen_thermal_profile_set(int mode)
 {
-       char buffer[2] = {0, mode};
+       /* The Omen Control Center actively sets the first byte of the buffer to
+        * 255, so let's mimic this behaviour to be as close as possible to
+        * the original software.
+        */
+       char buffer[2] = {-1, mode};
        int ret;
 
        ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM,
@@ -1201,10 +1223,33 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
        return 0;
 }
 
+static bool has_omen_thermal_profile_ec_timer(void)
+{
+       const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+       if (!board_name)
+               return false;
+
+       return match_string(omen_timed_thermal_profile_boards,
+                           ARRAY_SIZE(omen_timed_thermal_profile_boards),
+                           board_name) >= 0;
+}
+
+inline int omen_thermal_profile_ec_flags_set(enum hp_thermal_profile_omen_flags flags)
+{
+       return ec_write(HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET, flags);
+}
+
+inline int omen_thermal_profile_ec_timer_set(u8 value)
+{
+       return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
+}
+
 static int platform_profile_omen_set(struct platform_profile_handler *pprof,
                                     enum platform_profile_option profile)
 {
        int err, tp, tp_version;
+       enum hp_thermal_profile_omen_flags flags = 0;
 
        tp_version = omen_get_thermal_policy_version();
 
@@ -1238,6 +1283,20 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof,
        if (err < 0)
                return err;
 
+       if (has_omen_thermal_profile_ec_timer()) {
+               err = omen_thermal_profile_ec_timer_set(0);
+               if (err < 0)
+                       return err;
+
+               if (profile == PLATFORM_PROFILE_PERFORMANCE)
+                       flags = HP_OMEN_EC_FLAGS_NOTIMER |
+                               HP_OMEN_EC_FLAGS_TURBO;
+
+               err = omen_thermal_profile_ec_flags_set(flags);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
index 0ef1c46b617b63e7219357b3a93981bfee61bcd9..dde139c69945eb3ff6fd3d162e956c321531bf90 100644 (file)
@@ -310,7 +310,6 @@ static void huawei_wmi_leds_setup(struct device *dev)
        huawei->cdev.max_brightness = 1;
        huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
        huawei->cdev.default_trigger = "audio-micmute";
-       huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
        huawei->cdev.dev = dev;
        huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
 
index 2ab7d9ac542d1814fad533c9e8b68c5ac53815d4..1d4bbae115f1fb583446cf4913c687c243374880 100644 (file)
@@ -179,7 +179,7 @@ static ssize_t rtl_set_state(struct device *dev,
        return ret;
 }
 
-static struct bus_type rtl_subsys = {
+static const struct bus_type rtl_subsys = {
        .name = "ibm_rtl",
        .dev_name = "ibm_rtl",
 };
index 88eefccb6ed276a60a3c6d02b03f4716a48aea8f..901849810ce2e71387e920eebf60440fd3af12b5 100644 (file)
@@ -1091,6 +1091,8 @@ static const struct key_entry ideapad_keymap[] = {
        { KE_KEY,       0x07 | IDEAPAD_WMI_KEY, { KEY_HELP } },
        { KE_KEY,       0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } },
        { KE_KEY,       0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } },
+       /* Refresh Rate Toggle (Fn+R) */
+       { KE_KEY,       0x10 | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },
        /* Dark mode toggle */
        { KE_KEY,       0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },
        /* Sound profile switch */
@@ -1100,7 +1102,7 @@ static const struct key_entry ideapad_keymap[] = {
        /* Lenovo Support */
        { KE_KEY,       0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } },
        /* Refresh Rate Toggle */
-       { KE_KEY,       0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } },
+       { KE_KEY,       0x0a | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },
 
        { KE_END },
 };
index 2cf3b4a8813f9b30cb5a79aaf2ee6acee2474c68..584c44387e103ccb83bf3f7a63fd07ae94eca001 100644 (file)
@@ -383,7 +383,7 @@ int ifs_load_firmware(struct device *dev)
        unsigned int expected_size;
        const struct firmware *fw;
        char scan_path[64];
-       int ret = -EINVAL;
+       int ret;
 
        snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan",
                 test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
index 13ecd55c66680339af31b10484cf1929041f0a0e..95b4b71fab537c1c1b5b449d8d55ed327319b257 100644 (file)
 /* Max retries on the same chunk */
 #define MAX_IFS_RETRIES  5
 
+struct run_params {
+       struct ifs_data *ifsd;
+       union ifs_scan *activate;
+       union ifs_status status;
+};
+
 /*
  * Number of TSC cycles that a logical CPU will wait for the other
  * logical CPU on the core in the WRMSR(ACTIVATE_SCAN).
@@ -134,19 +140,56 @@ static bool can_restart(union ifs_status status)
        return false;
 }
 
+#define SPINUNIT 100 /* 100 nsec */
+static atomic_t array_cpus_in;
+static atomic_t scan_cpus_in;
+
+/*
+ * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
+ */
+static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
+{
+       int cpu = smp_processor_id();
+       const struct cpumask *smt_mask = cpu_smt_mask(cpu);
+       int all_cpus = cpumask_weight(smt_mask);
+
+       atomic_inc(t);
+       while (atomic_read(t) < all_cpus) {
+               if (timeout < SPINUNIT)
+                       return;
+               ndelay(SPINUNIT);
+               timeout -= SPINUNIT;
+               touch_nmi_watchdog();
+       }
+}
+
 /*
  * Execute the scan. Called "simultaneously" on all threads of a core
  * at high priority using the stop_cpus mechanism.
  */
 static int doscan(void *data)
 {
-       int cpu = smp_processor_id();
-       u64 *msrs = data;
+       int cpu = smp_processor_id(), start, stop;
+       struct run_params *params = data;
+       union ifs_status status;
+       struct ifs_data *ifsd;
        int first;
 
+       ifsd = params->ifsd;
+
+       if (ifsd->generation) {
+               start = params->activate->gen2.start;
+               stop = params->activate->gen2.stop;
+       } else {
+               start = params->activate->gen0.start;
+               stop = params->activate->gen0.stop;
+       }
+
        /* Only the first logical CPU on a core reports result */
        first = cpumask_first(cpu_smt_mask(cpu));
 
+       wait_for_sibling_cpu(&scan_cpus_in, NSEC_PER_SEC);
+
        /*
         * This WRMSR will wait for other HT threads to also write
         * to this MSR (at most for activate.delay cycles). Then it
@@ -155,12 +198,14 @@ static int doscan(void *data)
         * take up to 200 milliseconds (in the case where all chunks
         * are processed in a single pass) before it retires.
         */
-       wrmsrl(MSR_ACTIVATE_SCAN, msrs[0]);
+       wrmsrl(MSR_ACTIVATE_SCAN, params->activate->data);
+       rdmsrl(MSR_SCAN_STATUS, status.data);
 
-       if (cpu == first) {
-               /* Pass back the result of the scan */
-               rdmsrl(MSR_SCAN_STATUS, msrs[1]);
-       }
+       trace_ifs_status(ifsd->cur_batch, start, stop, status.data);
+
+       /* Pass back the result of the scan */
+       if (cpu == first)
+               params->status = status;
 
        return 0;
 }
@@ -179,7 +224,7 @@ static void ifs_test_core(int cpu, struct device *dev)
        struct ifs_data *ifsd;
        int to_start, to_stop;
        int status_chunk;
-       u64 msrvals[2];
+       struct run_params params;
        int retries;
 
        ifsd = ifs_get_data(dev);
@@ -190,6 +235,8 @@ static void ifs_test_core(int cpu, struct device *dev)
        to_start = 0;
        to_stop = ifsd->valid_chunks - 1;
 
+       params.ifsd = ifs_get_data(dev);
+
        if (ifsd->generation) {
                activate.gen2.start = to_start;
                activate.gen2.stop = to_stop;
@@ -207,12 +254,11 @@ static void ifs_test_core(int cpu, struct device *dev)
                        break;
                }
 
-               msrvals[0] = activate.data;
-               stop_core_cpuslocked(cpu, doscan, msrvals);
-
-               status.data = msrvals[1];
+               params.activate = &activate;
+               atomic_set(&scan_cpus_in, 0);
+               stop_core_cpuslocked(cpu, doscan, &params);
 
-               trace_ifs_status(cpu, to_start, to_stop, status.data);
+               status = params.status;
 
                /* Some cases can be retried, give up for others */
                if (!can_restart(status))
@@ -250,34 +296,14 @@ static void ifs_test_core(int cpu, struct device *dev)
        }
 }
 
-#define SPINUNIT 100 /* 100 nsec */
-static atomic_t array_cpus_out;
-
-/*
- * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
- */
-static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
-{
-       int cpu = smp_processor_id();
-       const struct cpumask *smt_mask = cpu_smt_mask(cpu);
-       int all_cpus = cpumask_weight(smt_mask);
-
-       atomic_inc(t);
-       while (atomic_read(t) < all_cpus) {
-               if (timeout < SPINUNIT)
-                       return;
-               ndelay(SPINUNIT);
-               timeout -= SPINUNIT;
-               touch_nmi_watchdog();
-       }
-}
-
 static int do_array_test(void *data)
 {
        union ifs_array *command = data;
        int cpu = smp_processor_id();
        int first;
 
+       wait_for_sibling_cpu(&array_cpus_in, NSEC_PER_SEC);
+
        /*
         * Only one logical CPU on a core needs to trigger the Array test via MSR write.
         */
@@ -289,9 +315,6 @@ static int do_array_test(void *data)
                rdmsrl(MSR_ARRAY_BIST, command->data);
        }
 
-       /* Tests complete faster if the sibling is spinning here */
-       wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC);
-
        return 0;
 }
 
@@ -312,7 +335,7 @@ static void ifs_array_test_core(int cpu, struct device *dev)
                        timed_out = true;
                        break;
                }
-               atomic_set(&array_cpus_out, 0);
+               atomic_set(&array_cpus_in, 0);
                stop_core_cpuslocked(cpu, do_array_test, &command);
 
                if (command.ctrl_result)
index 683ae828276b8c456144dc7d5f19bb50761d3827..34b4cd23bfe591cba691f5fcbc08ab007efc75ab 100644 (file)
@@ -673,6 +673,7 @@ static struct pmc_info arl_pmc_info_list[] = {
 };
 
 #define ARL_NPU_PCI_DEV                        0xad1d
+#define ARL_GNA_PCI_DEV                        0xae4c
 /*
  * Set power state of select devices that do not have drivers to D3
  * so that they do not block Package C entry.
@@ -680,6 +681,7 @@ static struct pmc_info arl_pmc_info_list[] = {
 static void arl_d3_fixup(void)
 {
        pmc_core_set_device_d3(ARL_NPU_PCI_DEV);
+       pmc_core_set_device_d3(ARL_GNA_PCI_DEV);
 }
 
 static int arl_resume(struct pmc_dev *pmcdev)
index 8f9c036809c79d17742ff84c858c9f4d25837647..10c96c1a850afed12b7c720771c4ccbcaaddd2f9 100644 (file)
@@ -1389,6 +1389,15 @@ static int pmc_core_probe(struct platform_device *pdev)
                return -ENOMEM;
        pmcdev->pmcs[PMC_IDX_MAIN] = primary_pmc;
 
+       /* The last element in msr_map is empty */
+       pmcdev->num_of_pkgc = ARRAY_SIZE(msr_map) - 1;
+       pmcdev->pkgc_res_cnt = devm_kcalloc(&pdev->dev,
+                                           pmcdev->num_of_pkgc,
+                                           sizeof(*pmcdev->pkgc_res_cnt),
+                                           GFP_KERNEL);
+       if (!pmcdev->pkgc_res_cnt)
+               return -ENOMEM;
+
        /*
         * Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here
         * Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap
@@ -1432,6 +1441,7 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
 {
        struct pmc_dev *pmcdev = dev_get_drvdata(dev);
        struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
+       unsigned int i;
 
        if (pmcdev->suspend)
                pmcdev->suspend(pmcdev);
@@ -1440,9 +1450,11 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
        if (pm_suspend_via_firmware())
                return 0;
 
-       /* Save PC10 residency for checking later */
-       if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pmcdev->pc10_counter))
-               return -EIO;
+       /* Save PKGC residency for checking later */
+       for (i = 0; i < pmcdev->num_of_pkgc; i++) {
+               if (rdmsrl_safe(msr_map[i].bit_mask, &pmcdev->pkgc_res_cnt[i]))
+                       return -EIO;
+       }
 
        /* Save S0ix residency for checking later */
        if (pmc_core_dev_state_get(pmc, &pmcdev->s0ix_counter))
@@ -1451,14 +1463,15 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
        return 0;
 }
 
-static inline bool pmc_core_is_pc10_failed(struct pmc_dev *pmcdev)
+static inline bool pmc_core_is_deepest_pkgc_failed(struct pmc_dev *pmcdev)
 {
-       u64 pc10_counter;
+       u32 deepest_pkgc_msr = msr_map[pmcdev->num_of_pkgc - 1].bit_mask;
+       u64 deepest_pkgc_residency;
 
-       if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pc10_counter))
+       if (rdmsrl_safe(deepest_pkgc_msr, &deepest_pkgc_residency))
                return false;
 
-       if (pc10_counter == pmcdev->pc10_counter)
+       if (deepest_pkgc_residency == pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1])
                return true;
 
        return false;
@@ -1497,10 +1510,22 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev)
        if (!warn_on_s0ix_failures)
                return 0;
 
-       if (pmc_core_is_pc10_failed(pmcdev)) {
-               /* S0ix failed because of PC10 entry failure */
-               dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n",
-                        pmcdev->pc10_counter);
+       if (pmc_core_is_deepest_pkgc_failed(pmcdev)) {
+               /* S0ix failed because of deepest PKGC entry failure */
+               dev_info(dev, "CPU did not enter %s!!! (%s cnt=0x%llx)\n",
+                        msr_map[pmcdev->num_of_pkgc - 1].name,
+                        msr_map[pmcdev->num_of_pkgc - 1].name,
+                        pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1]);
+
+               for (i = 0; i < pmcdev->num_of_pkgc; i++) {
+                       u64 pc_cnt;
+
+                       if (!rdmsrl_safe(msr_map[i].bit_mask, &pc_cnt)) {
+                               dev_info(dev, "Prev %s cnt = 0x%llx, Current %s cnt = 0x%llx\n",
+                                        msr_map[i].name, pmcdev->pkgc_res_cnt[i],
+                                        msr_map[i].name, pc_cnt);
+                       }
+               }
                return 0;
        }
 
index 54137faaae2b2a8af640b3ebc4fe8710666722af..83504c49a0e313cba1bd201f4efaa88fb915cb8b 100644 (file)
@@ -385,7 +385,8 @@ struct pmc {
  * @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
- * @pc10_counter:      PC10 residency counter
+ * @pkgc_res_cnt:      Array of PKGC residency counters
+ * @num_of_pkgc:       Number of PKGC
  * @s0ix_counter:      S0ix residency (step adjusted)
  * @num_lpm_modes:     Count of enabled modes
  * @lpm_en_modes:      Array of enabled modes from lowest to highest priority
@@ -403,13 +404,15 @@ struct pmc_dev {
        int pmc_xram_read_bit;
        struct mutex lock; /* generic mutex lock for PMC Core */
 
-       u64 pc10_counter;
        u64 s0ix_counter;
        int num_lpm_modes;
        int lpm_en_modes[LPM_MAX_NUM_MODES];
        void (*suspend)(struct pmc_dev *pmcdev);
        int (*resume)(struct pmc_dev *pmcdev);
 
+       u64 *pkgc_res_cnt;
+       u8 num_of_pkgc;
+
        bool has_die_c6;
        u32 die_c6_offset;
        struct telem_endpoint *punit_ep;
index abad17cdd3d787cff7a311e6e7f7ed11d7c7281f..068d72504683f492a8c5671b8fb58a021797ed9d 100644 (file)
 
 #include "core.h"
 
-#define SOCM_LPM_REQ_GUID      0x11594920
-
-#define PMC_DEVID_SOCM 0xa87f
-
-static const u8 LNL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20};
-
-static struct pmc_info lnl_pmc_info_list[] = {
-       {
-               .guid   = SOCM_LPM_REQ_GUID,
-               .devid  = PMC_DEVID_SOCM,
-               .map    = &lnl_socm_reg_map,
-       },
-       {}
-};
-
 const struct pmc_bit_map lnl_ltr_show_map[] = {
        {"SOUTHPORT_A",         CNP_PMC_LTR_SPA},
        {"SOUTHPORT_B",         CNP_PMC_LTR_SPB},
@@ -490,7 +475,6 @@ const struct pmc_reg_map lnl_socm_reg_map = {
        .lpm_sts = lnl_lpm_maps,
        .lpm_status_offset = MTL_LPM_STATUS_OFFSET,
        .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
-       .lpm_reg_index = LNL_LPM_REG_INDEX,
 };
 
 #define LNL_NPU_PCI_DEV                0x643e
@@ -517,33 +501,19 @@ static int lnl_resume(struct pmc_dev *pmcdev)
 int lnl_core_init(struct pmc_dev *pmcdev)
 {
        int ret;
-       int func = 2;
-       bool ssram_init = true;
        struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC];
 
        lnl_d3_fixup();
 
        pmcdev->suspend = cnl_suspend;
        pmcdev->resume = lnl_resume;
-       pmcdev->regmap_list = lnl_pmc_info_list;
-       ret = pmc_core_ssram_init(pmcdev, func);
-
-       /* If regbase not assigned, set map and discover using legacy method */
-       if (ret) {
-               ssram_init = false;
-               pmc->map = &lnl_socm_reg_map;
-               ret = get_primary_reg_base(pmc);
-               if (ret)
-                       return ret;
-       }
 
-       pmc_core_get_low_power_modes(pmcdev);
+       pmc->map = &lnl_socm_reg_map;
+       ret = get_primary_reg_base(pmc);
+       if (ret)
+               return ret;
 
-       if (ssram_init) {
-               ret = pmc_core_ssram_get_lpm_reqs(pmcdev);
-               if (ret)
-                       return ret;
-       }
+       pmc_core_get_low_power_modes(pmcdev);
 
        return 0;
 }
index 2662fbbddf0cc76edfa2e4dfce0e492bfbcd6fdd..1d918000d72b4b21bd959c9309405f2a7036e92a 100644 (file)
@@ -462,10 +462,10 @@ static long isst_if_core_power_state(void __user *argp)
        struct tpmi_per_power_domain_info *power_domain_info;
        struct isst_core_power core_power;
 
-       if (disable_dynamic_sst_features())
+       if (copy_from_user(&core_power, argp, sizeof(core_power)))
                return -EFAULT;
 
-       if (copy_from_user(&core_power, argp, sizeof(core_power)))
+       if (core_power.get_set && disable_dynamic_sst_features())
                return -EFAULT;
 
        power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id);
index e73cdea67fff854c5bc0236769e42aae3539d0bb..910df7c654f481983d725b01b2b840170573fbc7 100644 (file)
@@ -96,7 +96,7 @@ struct intel_tpmi_pfs_entry {
  */
 struct intel_tpmi_pm_feature {
        struct intel_tpmi_pfs_entry pfs_header;
-       unsigned int vsec_offset;
+       u64 vsec_offset;
        struct intel_vsec_device *vsec_dev;
 };
 
@@ -376,7 +376,7 @@ static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused)
                        read_blocked = feature_state.read_blocked ? 'Y' : 'N';
                        write_blocked = feature_state.write_blocked ? 'Y' : 'N';
                }
-               seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\t\t%c\t\t%c\n",
+               seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%016llx\t%c\t%c\t\t%c\t\t%c\n",
                           pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries,
                           pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset,
                           pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled,
@@ -395,7 +395,8 @@ static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
        struct intel_tpmi_pm_feature *pfs = s->private;
        int count, ret = 0;
        void __iomem *mem;
-       u32 off, size;
+       u32 size;
+       u64 off;
        u8 *buffer;
 
        size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
@@ -411,7 +412,7 @@ static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
        mutex_lock(&tpmi_dev_lock);
 
        for (count = 0; count < pfs->pfs_header.num_entries; ++count) {
-               seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off);
+               seq_printf(s, "TPMI Instance:%d offset:0x%llx\n", count, off);
 
                mem = ioremap(off, size);
                if (!mem) {
index 778eb0aa3479a5bcdd31ceef5dec4446db55069e..0fdfaf3a4f5cde4a2945a3a8860b1eb220c4f265 100644 (file)
@@ -236,10 +236,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev,
 
        for ( ; *header; header++) {
                ret = intel_vsec_add_dev(pdev, *header, info);
-               if (ret)
-                       dev_info(&pdev->dev, "Could not add device for VSEC id %d\n",
-                                (*header)->id);
-               else
+               if (!ret)
                        have_devices = true;
        }
 
index 040153ad67c1cb7c36fe85616cb97d10f7f99c46..75c82c08117f5c2a7701bf95706f2030eb3467b5 100644 (file)
@@ -131,6 +131,7 @@ static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
        .probe = intel_wmi_sbl_fw_update_probe,
        .remove = intel_wmi_sbl_fw_update_remove,
        .id_table = intel_wmi_sbl_id_table,
+       .no_singleton = true,
 };
 module_wmi_driver(intel_wmi_sbl_fw_update_driver);
 
index e2ad3f46f3569f97d883cb21735b6b571d87ffdd..08df560a2c7a88c0f615ac428dbcb312ea0116a6 100644 (file)
@@ -63,6 +63,7 @@ static struct wmi_driver intel_wmi_thunderbolt_driver = {
                .dev_groups = tbt_groups,
        },
        .id_table = intel_wmi_thunderbolt_id_table,
+       .no_singleton = true,
 };
 
 module_wmi_driver(intel_wmi_thunderbolt_driver);
index b7c10c15a3d633bf9b4c804f445935e2930c7d81..7d87cbd4b9c63c7ceba40284041265315efe6663 100644 (file)
@@ -22,7 +22,7 @@
 
 static int major;
 
-struct intel_scu_ipc_dev *scu;
+static struct intel_scu_ipc_dev *scu;
 static DEFINE_MUTEX(scu_lock);
 
 /* IOCTL commands */
index d904fad499aa5de916ce58ff448ab478ee249cda..dbf0310448da9a3c407802e06754329cff8cc7fc 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 
-#include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
 static int intel_scu_pci_probe(struct pci_dev *pdev,
index c2479777a1d6050422f8e5ef5a9803b661a0b32d..a5031a25632e60de5c30b10b4212583847636ef0 100644 (file)
@@ -13,7 +13,6 @@
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include <asm/intel-mid.h>
 #include <asm/io_apic.h>
 #include <asm/hw_irq.h>
 
index 93a6414c661141d6078d6cb55a91a30f057240c3..0aa7076bc9cc7f5974a73620075e299f8919653a 100644 (file)
@@ -6,6 +6,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/acpi.h>
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/dmi.h>
@@ -17,6 +18,7 @@
 #include <linux/platform_device.h>
 #include <linux/pci.h>
 #include <linux/seq_file.h>
+#include <linux/suspend.h>
 
 struct pmc_bit_map {
        const char *name;
@@ -448,6 +450,82 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
        return 0;
 }
 
+#ifdef CONFIG_SUSPEND
+static void pmc_dev_state_check(u32 sts, const struct pmc_bit_map *sts_map,
+                               u32 fd, const struct pmc_bit_map *fd_map,
+                               u32 sts_possible_false_pos)
+{
+       int index;
+
+       for (index = 0; sts_map[index].name; index++) {
+               if (!(fd_map[index].bit_mask & fd) &&
+                   !(sts_map[index].bit_mask & sts)) {
+                       if (sts_map[index].bit_mask & sts_possible_false_pos)
+                               pm_pr_dbg("%s is in D0 prior to s2idle\n",
+                                         sts_map[index].name);
+                       else
+                               pr_err("%s is in D0 prior to s2idle\n",
+                                      sts_map[index].name);
+               }
+       }
+}
+
+static void pmc_s2idle_check(void)
+{
+       struct pmc_dev *pmc = &pmc_device;
+       const struct pmc_reg_map *m = pmc->map;
+       u32 func_dis, func_dis_2;
+       u32 d3_sts_0, d3_sts_1;
+       u32 false_pos_sts_0, false_pos_sts_1;
+       int i;
+
+       func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
+       func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
+       d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
+       d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
+
+       /*
+        * Some blocks are not used on lower-featured versions of the SoC and
+        * always report D0, add these to false_pos mask to log at debug level.
+        */
+       if (m->d3_sts_1 == byt_d3_sts_1_map) {
+               /* Bay Trail */
+               false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_PCIE_PORT0 |
+                       BIT_PCIE_PORT1 | BIT_PCIE_PORT2 | BIT_PCIE_PORT3 |
+                       BIT_LPSS2_F5_I2C5;
+               false_pos_sts_1 = BIT_SMB | BIT_USH_SS_PHY | BIT_DFX;
+       } else {
+               /* Cherry Trail */
+               false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_LPSS2_F7_I2C7;
+               false_pos_sts_1 = BIT_SMB | BIT_STS_ISH;
+       }
+
+       pmc_dev_state_check(d3_sts_0, m->d3_sts_0, func_dis, m->func_dis, false_pos_sts_0);
+       pmc_dev_state_check(d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2, false_pos_sts_1);
+
+       /* Forced-on PMC clocks prevent S0i3 */
+       for (i = 0; i < PMC_CLK_NUM; i++) {
+               u32 ctl = pmc_reg_read(pmc, PMC_CLK_CTL_OFFSET + 4 * i);
+
+               if ((ctl & PMC_MASK_CLK_CTL) != PMC_CLK_CTL_FORCE_ON)
+                       continue;
+
+               pr_err("clock %d is ON prior to freeze (ctl 0x%08x)\n", i, ctl);
+       }
+}
+
+static struct acpi_s2idle_dev_ops pmc_s2idle_ops = {
+       .check = pmc_s2idle_check,
+};
+
+static void pmc_s2idle_check_register(void)
+{
+       acpi_register_lps0_dev(&pmc_s2idle_ops);
+}
+#else
+static void pmc_s2idle_check_register(void) {}
+#endif
+
 static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        struct pmc_dev *pmc = &pmc_device;
@@ -485,6 +563,7 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_warn(&pdev->dev, "platform clocks register failed: %d\n",
                         ret);
 
+       pmc_s2idle_check_register();
        pmc->init = true;
        return ret;
 }
index 6ce43ccb3112eb542256ac697108af261acf8b93..c0910af16a3ac2719cd330eb6617232411e4f7ed 100644 (file)
@@ -256,12 +256,7 @@ static void silicom_gpio_set(struct gpio_chip *gc,
        if (direction == GPIO_LINE_DIRECTION_IN)
                return;
 
-       if (value)
-               silicom_mec_port_set(channel, 0);
-       else if (value == 0)
-               silicom_mec_port_set(channel, 1);
-       else
-               pr_err("Wrong argument value: %d\n", value);
+       silicom_mec_port_set(channel, !value);
 }
 
 static int silicom_gpio_direction_output(struct gpio_chip *gc,
index ce3e08815a8e647f2bf5578d0383dd4621d8526f..9345316b45dbe42476da55917178afeaedea432c 100644 (file)
@@ -195,7 +195,7 @@ static const char * const level_options[] = {
        [TLMI_LEVEL_MASTER] = "master",
 };
 static struct think_lmi tlmi_priv;
-static struct class *fw_attr_class;
+static const struct class *fw_attr_class;
 static DEFINE_MUTEX(tlmi_mutex);
 
 /* Convert BIOS WMI error string to suitable error code */
index 5ecd9d33250d78f3a38c7f99b8b6c5c903cdf25d..82429e59999da211429220aefc7896d01eff3e3e 100644 (file)
@@ -69,6 +69,7 @@
 #include <linux/sysfs.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/units.h>
 #include <linux/workqueue.h>
 
 #include <acpi/battery.h>
@@ -166,6 +167,7 @@ enum tpacpi_hkey_event_t {
        TP_HKEY_EV_VOL_MUTE             = 0x1017, /* Mixer output mute */
        TP_HKEY_EV_PRIVACYGUARD_TOGGLE  = 0x130f, /* Toggle priv.guard on/off */
        TP_HKEY_EV_AMT_TOGGLE           = 0x131a, /* Toggle AMT on/off */
+       TP_HKEY_EV_PROFILE_TOGGLE       = 0x131f, /* Toggle platform profile */
 
        /* Reasons for waking up from S3/S4 */
        TP_HKEY_EV_WKUP_S3_UNDOCK       = 0x2304, /* undock requested, S3 */
@@ -3731,6 +3733,7 @@ static bool hotkey_notify_extended_hotkey(const u32 hkey)
        switch (hkey) {
        case TP_HKEY_EV_PRIVACYGUARD_TOGGLE:
        case TP_HKEY_EV_AMT_TOGGLE:
+       case TP_HKEY_EV_PROFILE_TOGGLE:
                tpacpi_driver_event(hkey);
                return true;
        }
@@ -6126,12 +6129,15 @@ enum thermal_access_mode {
        TPACPI_THERMAL_ACPI_TMP07,      /* Use ACPI TMP0-7 */
        TPACPI_THERMAL_ACPI_UPDT,       /* Use ACPI TMP0-7 with UPDT */
        TPACPI_THERMAL_TPEC_8,          /* Use ACPI EC regs, 8 sensors */
+       TPACPI_THERMAL_TPEC_12,         /* Use ACPI EC regs, 12 sensors */
        TPACPI_THERMAL_TPEC_16,         /* Use ACPI EC regs, 16 sensors */
 };
 
 enum { /* TPACPI_THERMAL_TPEC_* */
        TP_EC_THERMAL_TMP0 = 0x78,      /* ACPI EC regs TMP 0..7 */
        TP_EC_THERMAL_TMP8 = 0xC0,      /* ACPI EC regs TMP 8..15 */
+       TP_EC_THERMAL_TMP0_NS = 0xA8,   /* ACPI EC Non-Standard regs TMP 0..7 */
+       TP_EC_THERMAL_TMP8_NS = 0xB8,   /* ACPI EC Non-standard regs TMP 8..11 */
        TP_EC_FUNCREV      = 0xEF,      /* ACPI EC Functional revision */
        TP_EC_THERMAL_TMP_NA = -128,    /* ACPI EC sensor not available */
 
@@ -6144,8 +6150,104 @@ struct ibm_thermal_sensors_struct {
        s32 temp[TPACPI_MAX_THERMAL_SENSORS];
 };
 
+static const struct tpacpi_quirk thermal_quirk_table[] __initconst = {
+       /* Non-standard address for thermal registers on some ThinkPads */
+       TPACPI_Q_LNV3('R', '1', 'F', true),     /* L13 Yoga Gen 2 */
+       TPACPI_Q_LNV3('N', '2', 'U', true),     /* X13 Yoga Gen 2*/
+       TPACPI_Q_LNV3('R', '0', 'R', true),     /* L380 */
+       TPACPI_Q_LNV3('R', '1', '5', true),     /* L13 Yoga Gen 1*/
+       TPACPI_Q_LNV3('R', '1', '0', true),     /* L390 */
+       TPACPI_Q_LNV3('N', '2', 'L', true),     /* X13 Yoga Gen 1*/
+       TPACPI_Q_LNV3('R', '0', 'T', true),     /* 11e Gen5 GL*/
+       TPACPI_Q_LNV3('R', '1', 'D', true),     /* 11e Gen5 GL-R*/
+       TPACPI_Q_LNV3('R', '0', 'V', true),     /* 11e Gen5 KL-Y*/
+};
+
 static enum thermal_access_mode thermal_read_mode;
 static bool thermal_use_labels;
+static bool thermal_with_ns_address;   /* Non-standard thermal reg address */
+
+/* Function to check thermal read mode */
+static enum thermal_access_mode __init thermal_read_mode_check(void)
+{
+       u8 t, ta1, ta2, ver = 0;
+       int i;
+       int acpi_tmp7;
+
+       acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+
+       if (thinkpad_id.ec_model) {
+               /*
+                * Direct EC access mode: sensors at registers 0x78-0x7F,
+                * 0xC0-0xC7. Registers return 0x00 for non-implemented,
+                * thermal sensors return 0x80 when not available.
+                *
+                * In some special cases (when Power Supply ID is 0xC2)
+                * above rule causes thermal control issues. Offset 0xEF
+                * determines EC version. 0xC0-0xC7 are not thermal registers
+                * in Ver 3.
+                */
+               if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
+                       pr_warn("Thinkpad ACPI EC unable to access EC version\n");
+
+               /* Quirks to check non-standard EC */
+               thermal_with_ns_address = tpacpi_check_quirks(thermal_quirk_table,
+                                                       ARRAY_SIZE(thermal_quirk_table));
+
+               /* Support for Thinkpads with non-standard address */
+               if (thermal_with_ns_address) {
+                       pr_info("ECFW with non-standard thermal registers found\n");
+                       return TPACPI_THERMAL_TPEC_12;
+               }
+
+               ta1 = ta2 = 0;
+               for (i = 0; i < 8; i++) {
+                       if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
+                               ta1 |= t;
+                       } else {
+                               ta1 = 0;
+                               break;
+                       }
+                       if (ver < 3) {
+                               if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
+                                       ta2 |= t;
+                               } else {
+                                       ta1 = 0;
+                                       break;
+                               }
+                       }
+               }
+
+               if (ta1 == 0) {
+                       /* This is sheer paranoia, but we handle it anyway */
+                       if (acpi_tmp7) {
+                               pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
+                               return TPACPI_THERMAL_ACPI_TMP07;
+                       }
+                       pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
+                       return TPACPI_THERMAL_NONE;
+               }
+
+               if (ver >= 3) {
+                       thermal_use_labels = true;
+                       return TPACPI_THERMAL_TPEC_8;
+               }
+
+               return (ta2 != 0) ? TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
+       }
+
+       if (acpi_tmp7) {
+               if (tpacpi_is_ibm() && acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+                       /* 600e/x, 770e, 770x */
+                       return TPACPI_THERMAL_ACPI_UPDT;
+               }
+               /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
+               return TPACPI_THERMAL_ACPI_TMP07;
+       }
+
+       /* temperatures not supported on 570, G4x, R30, R31, R32 */
+       return TPACPI_THERMAL_NONE;
+}
 
 /* idx is zero-based */
 static int thermal_get_sensor(int idx, s32 *value)
@@ -6174,6 +6276,20 @@ static int thermal_get_sensor(int idx, s32 *value)
                }
                break;
 
+       /* The Non-standard EC uses 12 Thermal areas */
+       case TPACPI_THERMAL_TPEC_12:
+               if (idx >= 12)
+                       return -EINVAL;
+
+               t = idx < 8 ? TP_EC_THERMAL_TMP0_NS + idx :
+                               TP_EC_THERMAL_TMP8_NS + (idx - 8);
+
+               if (!acpi_ec_read(t, &tmp))
+                       return -EIO;
+
+               *value = tmp * MILLIDEGREE_PER_DEGREE;
+               return 0;
+
        case TPACPI_THERMAL_ACPI_UPDT:
                if (idx <= 7) {
                        snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
@@ -6208,17 +6324,17 @@ static int thermal_get_sensor(int idx, s32 *value)
 
 static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
 {
-       int res, i;
-       int n;
-
-       n = 8;
-       i = 0;
+       int res, i, n;
 
        if (!s)
                return -EINVAL;
 
        if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
                n = 16;
+       else if (thermal_read_mode == TPACPI_THERMAL_TPEC_12)
+               n = 12;
+       else
+               n = 8;
 
        for (i = 0 ; i < n; i++) {
                res = thermal_get_sensor(i, &s->temp[i]);
@@ -6317,18 +6433,36 @@ static struct attribute *thermal_temp_input_attr[] = {
        NULL
 };
 
+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
 static umode_t thermal_attr_is_visible(struct kobject *kobj,
                                       struct attribute *attr, int n)
 {
-       if (thermal_read_mode == TPACPI_THERMAL_NONE)
+       struct device_attribute *dev_attr = to_dev_attr(attr);
+       struct sensor_device_attribute *sensor_attr =
+                                       to_sensor_dev_attr(dev_attr);
+
+       int idx = sensor_attr->index;
+
+       switch (thermal_read_mode) {
+       case TPACPI_THERMAL_NONE:
                return 0;
 
-       if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) ||
-           attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) ||
-           attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) ||
-           attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) {
-               if (thermal_read_mode != TPACPI_THERMAL_TPEC_16)
+       case TPACPI_THERMAL_ACPI_TMP07:
+       case TPACPI_THERMAL_ACPI_UPDT:
+       case TPACPI_THERMAL_TPEC_8:
+               if (idx >= 8)
+                       return 0;
+               break;
+
+       case TPACPI_THERMAL_TPEC_12:
+               if (idx >= 12)
                        return 0;
+               break;
+
+       default:
+               break;
+
        }
 
        return attr->mode;
@@ -6375,78 +6509,9 @@ static const struct attribute_group temp_label_attr_group = {
 
 static int __init thermal_init(struct ibm_init_struct *iibm)
 {
-       u8 t, ta1, ta2, ver = 0;
-       int i;
-       int acpi_tmp7;
-
        vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
 
-       acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
-
-       if (thinkpad_id.ec_model) {
-               /*
-                * Direct EC access mode: sensors at registers
-                * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
-                * non-implemented, thermal sensors return 0x80 when
-                * not available
-                * The above rule is unfortunately flawed. This has been seen with
-                * 0xC2 (power supply ID) causing thermal control problems.
-                * The EC version can be determined by offset 0xEF and at least for
-                * version 3 the Lenovo firmware team confirmed that registers 0xC0-0xC7
-                * are not thermal registers.
-                */
-               if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
-                       pr_warn("Thinkpad ACPI EC unable to access EC version\n");
-
-               ta1 = ta2 = 0;
-               for (i = 0; i < 8; i++) {
-                       if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
-                               ta1 |= t;
-                       } else {
-                               ta1 = 0;
-                               break;
-                       }
-                       if (ver < 3) {
-                               if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
-                                       ta2 |= t;
-                               } else {
-                                       ta1 = 0;
-                                       break;
-                               }
-                       }
-               }
-               if (ta1 == 0) {
-                       /* This is sheer paranoia, but we handle it anyway */
-                       if (acpi_tmp7) {
-                               pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
-                               thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
-                       } else {
-                               pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
-                               thermal_read_mode = TPACPI_THERMAL_NONE;
-                       }
-               } else {
-                       if (ver >= 3) {
-                               thermal_read_mode = TPACPI_THERMAL_TPEC_8;
-                               thermal_use_labels = true;
-                       } else {
-                               thermal_read_mode =
-                                       (ta2 != 0) ?
-                                       TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
-                       }
-               }
-       } else if (acpi_tmp7) {
-               if (tpacpi_is_ibm() &&
-                   acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
-                       /* 600e/x, 770e, 770x */
-                       thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
-               } else {
-                       /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
-                       thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
-               }
-       } else {
-               /* temperatures not supported on 570, G4x, R30, R31, R32 */
-               thermal_read_mode = TPACPI_THERMAL_NONE;
-       }
+       thermal_read_mode = thermal_read_mode_check();
 
        vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
                str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
@@ -8767,6 +8832,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
        TPACPI_Q_LNV3('N', '3', '7', TPACPI_FAN_2CTL),  /* T15g (2nd gen) */
        TPACPI_Q_LNV3('R', '1', 'F', TPACPI_FAN_NS),    /* L13 Yoga Gen 2 */
        TPACPI_Q_LNV3('N', '2', 'U', TPACPI_FAN_NS),    /* X13 Yoga Gen 2*/
+       TPACPI_Q_LNV3('R', '0', 'R', TPACPI_FAN_NS),    /* L380 */
+       TPACPI_Q_LNV3('R', '1', '5', TPACPI_FAN_NS),    /* L13 Yoga Gen 1 */
+       TPACPI_Q_LNV3('R', '1', '0', TPACPI_FAN_NS),    /* L390 */
+       TPACPI_Q_LNV3('N', '2', 'L', TPACPI_FAN_NS),    /* X13 Yoga Gen 1 */
+       TPACPI_Q_LNV3('R', '0', 'T', TPACPI_FAN_NS),    /* 11e Gen5 GL */
+       TPACPI_Q_LNV3('R', '1', 'D', TPACPI_FAN_NS),    /* 11e Gen5 GL-R */
+       TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS),    /* 11e Gen5 KL-Y */
        TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */
 };
 
@@ -9285,7 +9357,6 @@ static int mute_led_init(struct ibm_init_struct *iibm)
                        continue;
                }
 
-               mute_led_cdev[i].brightness = ledtrig_audio_get(i);
                err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
                if (err < 0) {
                        while (i--)
@@ -11119,7 +11190,23 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
                else
                        dytc_control_amt(!dytc_amt_active);
        }
-
+       if (hkey_event == TP_HKEY_EV_PROFILE_TOGGLE) {
+               switch (dytc_current_profile) {
+               case PLATFORM_PROFILE_LOW_POWER:
+                       dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED);
+                       break;
+               case PLATFORM_PROFILE_BALANCED:
+                       dytc_profile_set(NULL, PLATFORM_PROFILE_PERFORMANCE);
+                       break;
+               case PLATFORM_PROFILE_PERFORMANCE:
+                       dytc_profile_set(NULL, PLATFORM_PROFILE_LOW_POWER);
+                       break;
+               default:
+                       pr_warn("Profile HKEY unexpected profile %d", dytc_current_profile);
+               }
+               /* Notify user space the profile changed */
+               platform_profile_notify();
+       }
 }
 
 static void hotkey_driver_event(const unsigned int scancode)
index 975cf24ae359a882974f35762894108d4a117fb8..c6a10ec2c83f666b936247abbc17fe4bf2d87304 100644 (file)
@@ -1217,6 +1217,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"),
                },
        },
+       {
+               /* Chuwi Vi8 dual-boot (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, "CHUWI2.D86JHBNR02"),
+               },
+       },
        {
                /* Chuwi Vi8 Plus (CWI519) */
                .driver_data = (void *)&chuwi_vi8_plus_data,
index 644d2fd889c081bd37299b2343339dd033d80ba0..df6f0ae6e6c7904f97c125297a21166f56d0b1f0 100644 (file)
@@ -94,6 +94,7 @@ static struct wmi_driver wmi_bmof_driver = {
        .probe = wmi_bmof_probe,
        .remove = wmi_bmof_remove,
        .id_table = wmi_bmof_id_table,
+       .no_singleton = true,
 };
 
 module_wmi_driver(wmi_bmof_driver);
index 3c288e8f404beb5d4887235c85654e9ac77cd425..1920e115da893a11335495d8238f260e13c9b9df 100644 (file)
@@ -57,6 +57,8 @@ static_assert(__alignof__(struct guid_block) == 1);
 
 enum { /* wmi_block flags */
        WMI_READ_TAKES_NO_ARGS,
+       WMI_GUID_DUPLICATED,
+       WMI_NO_EVENT_DATA,
 };
 
 struct wmi_block {
@@ -88,16 +90,6 @@ static const struct acpi_device_id wmi_device_ids[] = {
 };
 MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
 
-/* allow duplicate GUIDs as these device drivers use struct wmi_driver */
-static const char * const allow_duplicates[] = {
-       "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */
-       "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */
-       "44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */
-       "86CCFD48-205E-4A77-9C48-2021CBEDE341", /* intel-wmi-thunderbolt */
-       "F1DDEE52-063C-4784-A11E-8A06684B9B01", /* dell-smm-hwmon */
-       NULL
-};
-
 #define dev_to_wblock(__dev)   container_of_const(__dev, struct wmi_block, dev.dev)
 #define dev_to_wdev(__dev)     container_of_const(__dev, struct wmi_device, dev)
 
@@ -132,26 +124,6 @@ static const void *find_guid_context(struct wmi_block *wblock,
        return NULL;
 }
 
-static int get_subobj_info(acpi_handle handle, const char *pathname,
-                          struct acpi_device_info **info)
-{
-       acpi_handle subobj_handle;
-       acpi_status status;
-
-       status = acpi_get_handle(handle, pathname, &subobj_handle);
-       if (status == AE_NOT_FOUND)
-               return -ENOENT;
-
-       if (ACPI_FAILURE(status))
-               return -EIO;
-
-       status = acpi_get_object_info(subobj_handle, info);
-       if (ACPI_FAILURE(status))
-               return -EIO;
-
-       return 0;
-}
-
 static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
 {
        struct guid_block *block;
@@ -215,6 +187,12 @@ static int wmidev_match_guid(struct device *dev, const void *data)
        struct wmi_block *wblock = dev_to_wblock(dev);
        const guid_t *guid = data;
 
+       /* Legacy GUID-based functions are restricted to only see
+        * a single WMI device for each GUID.
+        */
+       if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
+               return 0;
+
        if (guid_equal(guid, &wblock->gblock.guid))
                return 1;
 
@@ -226,13 +204,19 @@ static int wmidev_match_notify_id(struct device *dev, const void *data)
        struct wmi_block *wblock = dev_to_wblock(dev);
        const u32 *notify_id = data;
 
+       /* Legacy GUID-based functions are restricted to only see
+        * a single WMI device for each GUID.
+        */
+       if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
+               return 0;
+
        if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id)
                return 1;
 
        return 0;
 }
 
-static struct bus_type wmi_bus_type;
+static const struct bus_type wmi_bus_type;
 
 static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
 {
@@ -316,7 +300,7 @@ EXPORT_SYMBOL_GPL(wmidev_instance_count);
  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
  * @instance: Instance index
  * @method_id: Method ID to call
- * @in: Buffer containing input for the method call
+ * @in: Mandatory buffer containing input for the method call
  * @out: Empty buffer to return the method results
  *
  * Call an ACPI-WMI method, the caller must free @out.
@@ -346,7 +330,7 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
  * @wdev: A wmi bus device from a driver
  * @instance: Instance index
  * @method_id: Method ID to call
- * @in: Buffer containing input for the method call
+ * @in: Mandatory buffer containing input for the method call
  * @out: Empty buffer to return the method results
  *
  * Call an ACPI-WMI method, the caller must free @out.
@@ -367,26 +351,25 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met
        block = &wblock->gblock;
        handle = wblock->acpi_device->handle;
 
+       if (!in)
+               return AE_BAD_DATA;
+
        if (!(block->flags & ACPI_WMI_METHOD))
                return AE_BAD_DATA;
 
        if (block->instance_count <= instance)
                return AE_BAD_PARAMETER;
 
-       input.count = 2;
+       input.count = 3;
        input.pointer = params;
+
        params[0].type = ACPI_TYPE_INTEGER;
        params[0].integer.value = instance;
        params[1].type = ACPI_TYPE_INTEGER;
        params[1].integer.value = method_id;
-
-       if (in) {
-               input.count = 3;
-
-               params[2].type = get_param_acpi_type(wblock);
-               params[2].buffer.length = in->length;
-               params[2].buffer.pointer = in->pointer;
-       }
+       params[2].type = get_param_acpi_type(wblock);
+       params[2].buffer.length = in->length;
+       params[2].buffer.pointer = in->pointer;
 
        get_acpi_method_name(wblock, 'M', method);
 
@@ -890,6 +873,23 @@ static int wmi_dev_probe(struct device *dev)
        struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
        int ret = 0;
 
+       /* Some older WMI drivers will break if instantiated multiple times,
+        * so they are blocked from probing WMI devices with a duplicated GUID.
+        *
+        * New WMI drivers should support being instantiated multiple times.
+        */
+       if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags) && !wdriver->no_singleton) {
+               dev_warn(dev, "Legacy driver %s cannot be instantiated multiple times\n",
+                        dev->driver->name);
+
+               return -ENODEV;
+       }
+
+       if (wdriver->notify) {
+               if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)
+                       return -ENODEV;
+       }
+
        if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
                dev_warn(dev, "failed to enable device -- probing anyway\n");
 
@@ -931,7 +931,7 @@ static struct class wmi_bus_class = {
        .name = "wmi_bus",
 };
 
-static struct bus_type wmi_bus_type = {
+static const struct bus_type wmi_bus_type = {
        .name = "wmi",
        .dev_groups = wmi_groups,
        .match = wmi_dev_match,
@@ -979,9 +979,10 @@ static int wmi_create_device(struct device *wmi_bus_dev,
                             struct wmi_block *wblock,
                             struct acpi_device *device)
 {
-       struct acpi_device_info *info;
        char method[WMI_ACPI_METHOD_NAME_SIZE];
-       int result;
+       struct acpi_device_info *info;
+       acpi_handle method_handle;
+       acpi_status status;
        uint count;
 
        if (wblock->gblock.flags & ACPI_WMI_EVENT) {
@@ -990,6 +991,15 @@ static int wmi_create_device(struct device *wmi_bus_dev,
        }
 
        if (wblock->gblock.flags & ACPI_WMI_METHOD) {
+               get_acpi_method_name(wblock, 'M', method);
+               if (!acpi_has_method(device->handle, method)) {
+                       dev_warn(wmi_bus_dev,
+                                FW_BUG "%s method block execution control method not found\n",
+                                method);
+
+                       return -ENXIO;
+               }
+
                wblock->dev.dev.type = &wmi_type_method;
                goto out_init;
        }
@@ -1000,15 +1010,19 @@ static int wmi_create_device(struct device *wmi_bus_dev,
         * we ignore this data block.
         */
        get_acpi_method_name(wblock, 'Q', method);
-       result = get_subobj_info(device->handle, method, &info);
-
-       if (result) {
+       status = acpi_get_handle(device->handle, method, &method_handle);
+       if (ACPI_FAILURE(status)) {
                dev_warn(wmi_bus_dev,
-                        "%s data block query control method not found\n",
+                        FW_BUG "%s data block query control method not found\n",
                         method);
-               return result;
+
+               return -ENXIO;
        }
 
+       status = acpi_get_object_info(method_handle, &info);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
        wblock->dev.dev.type = &wmi_type_data;
 
        /*
@@ -1037,10 +1051,12 @@ static int wmi_create_device(struct device *wmi_bus_dev,
        wblock->dev.dev.parent = wmi_bus_dev;
 
        count = guid_count(&wblock->gblock.guid);
-       if (count)
+       if (count) {
                dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count);
-       else
+               set_bit(WMI_GUID_DUPLICATED, &wblock->flags);
+       } else {
                dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
+       }
 
        device_initialize(&wblock->dev.dev);
 
@@ -1067,32 +1083,6 @@ static int wmi_add_device(struct platform_device *pdev, struct wmi_device *wdev)
        return device_add(&wdev->dev);
 }
 
-static bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid)
-{
-       struct wmi_block *wblock;
-
-       list_for_each_entry(wblock, &wmi_block_list, list) {
-               /* skip warning and register if we know the driver will use struct wmi_driver */
-               for (int i = 0; allow_duplicates[i] != NULL; i++) {
-                       if (guid_parse_and_compare(allow_duplicates[i], guid))
-                               return false;
-               }
-               if (guid_equal(&wblock->gblock.guid, guid)) {
-                       /*
-                        * Because we historically didn't track the relationship
-                        * between GUIDs and ACPI nodes, we don't know whether
-                        * we need to suppress GUIDs that are unique on a
-                        * given node but duplicated across nodes.
-                        */
-                       dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
-                                guid, dev_name(&wblock->acpi_device->dev));
-                       return true;
-               }
-       }
-
-       return false;
-}
-
 /*
  * Parse the _WDG method for the GUID data blocks
  */
@@ -1101,6 +1091,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
        struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
        struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
        const struct guid_block *gblock;
+       bool event_data_available;
        struct wmi_block *wblock;
        union acpi_object *obj;
        acpi_status status;
@@ -1120,6 +1111,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
                return -ENXIO;
        }
 
+       event_data_available = acpi_has_method(device->handle, "_WED");
        gblock = (const struct guid_block *)obj->buffer.pointer;
        total = obj->buffer.length / sizeof(struct guid_block);
 
@@ -1129,17 +1121,14 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
                        continue;
                }
 
-               if (guid_already_parsed_for_legacy(device, &gblock[i].guid))
-                       continue;
-
                wblock = kzalloc(sizeof(*wblock), GFP_KERNEL);
-               if (!wblock) {
-                       dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid);
+               if (!wblock)
                        continue;
-               }
 
                wblock->acpi_device = device;
                wblock->gblock = gblock[i];
+               if (gblock[i].flags & ACPI_WMI_EVENT && !event_data_available)
+                       set_bit(WMI_NO_EVENT_DATA, &wblock->flags);
 
                retval = wmi_create_device(wmi_bus_dev, wblock, device);
                if (retval) {
@@ -1205,30 +1194,46 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
        }
 }
 
-static void wmi_notify_driver(struct wmi_block *wblock)
+static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj)
 {
-       struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
        struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
        acpi_status status;
 
-       if (!driver->no_notify_data) {
-               status = get_event_data(wblock, &data);
-               if (ACPI_FAILURE(status)) {
-                       dev_warn(&wblock->dev.dev, "Failed to get event data\n");
-                       return;
-               }
+       if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) {
+               *obj = NULL;
+               return 0;
        }
 
-       if (driver->notify)
-               driver->notify(&wblock->dev, data.pointer);
+       status = get_event_data(wblock, &data);
+       if (ACPI_FAILURE(status)) {
+               dev_warn(&wblock->dev.dev, "Failed to get event data\n");
+               return -EIO;
+       }
+
+       *obj = data.pointer;
 
-       kfree(data.pointer);
+       return 0;
+}
+
+static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
+{
+       struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
+
+       if (!obj && !driver->no_notify_data) {
+               dev_warn(&wblock->dev.dev, "Event contains no event data\n");
+               return;
+       }
+
+       if (driver->notify)
+               driver->notify(&wblock->dev, obj);
 }
 
 static int wmi_notify_device(struct device *dev, void *data)
 {
        struct wmi_block *wblock = dev_to_wblock(dev);
+       union acpi_object *obj;
        u32 *event = data;
+       int ret;
 
        if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
                return 0;
@@ -1238,15 +1243,36 @@ static int wmi_notify_device(struct device *dev, void *data)
         * Because of this the WMI driver notify handler takes precedence.
         */
        if (wblock->dev.dev.driver && wblock->driver_ready) {
-               wmi_notify_driver(wblock);
+               ret = wmi_get_notify_data(wblock, &obj);
+               if (ret >= 0) {
+                       wmi_notify_driver(wblock, obj);
+                       kfree(obj);
+               }
        } else {
-               if (wblock->handler)
+               if (wblock->handler) {
                        wblock->handler(*event, wblock->handler_data);
+               } else {
+                       /* The ACPI WMI specification says that _WED should be
+                        * evaluated every time an notification is received, even
+                        * if no consumers are present.
+                        *
+                        * Some firmware implementations actually depend on this
+                        * by using a queue for events which will fill up if the
+                        * WMI driver core stops evaluating _WED due to missing
+                        * WMI event consumers.
+                        *
+                        * Because of this we need this seemingly useless call to
+                        * wmi_get_notify_data() which in turn evaluates _WED.
+                        */
+                       ret = wmi_get_notify_data(wblock, &obj);
+                       if (ret >= 0)
+                               kfree(obj);
+               }
+
        }
        up_read(&wblock->notify_lock);
 
-       acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class,
-                                       dev_name(&wblock->dev.dev), *event, 0);
+       acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
 
        return -EBUSY;
 }
@@ -1347,7 +1373,7 @@ static int acpi_wmi_probe(struct platform_device *device)
 
        error = parse_wdg(wmi_bus_dev, device);
        if (error) {
-               pr_err("Failed to parse WDG method\n");
+               dev_err(&device->dev, "Failed to parse _WDG method\n");
                return error;
        }
 
index b8a701c77fd00cc2d983ad69cca9ebd5a66acee9..161e4bc1c9ee310a55f3ff723d23085da9e6335d 100644 (file)
                                BIT_ORED_DEDICATED_IRQ_GPSC | \
                                BIT_SHARED_IRQ_GPSS)
 
+/* External clk generator settings */
+#define PMC_CLK_CTL_OFFSET             0x60
+#define PMC_CLK_CTL_SIZE               4
+#define PMC_CLK_NUM                    6
+#define PMC_CLK_CTL_GATED_ON_D3                0x0
+#define PMC_CLK_CTL_FORCE_ON           0x1
+#define PMC_CLK_CTL_FORCE_OFF          0x2
+#define PMC_CLK_CTL_RESERVED           0x3
+#define PMC_MASK_CLK_CTL               GENMASK(1, 0)
+#define PMC_MASK_CLK_FREQ              BIT(2)
+#define PMC_CLK_FREQ_XTAL              (0 << 2)        /* 25 MHz */
+#define PMC_CLK_FREQ_PLL               (1 << 2)        /* 19.2 MHz */
+
 /* The timers accumulate time spent in sleep state */
 #define        PMC_S0IR_TMR            0x80
 #define        PMC_S0I1_TMR            0x84
 #define        BIT_SCC_SDIO            BIT(9)
 #define        BIT_SCC_SDCARD          BIT(10)
 #define        BIT_SCC_MIPI            BIT(11)
-#define        BIT_HDA                 BIT(12)
+#define        BIT_HDA                 BIT(12) /* CHT datasheet: reserved */
 #define        BIT_LPE                 BIT(13)
 #define        BIT_OTG                 BIT(14)
-#define        BIT_USH                 BIT(15)
-#define        BIT_GBE                 BIT(16)
-#define        BIT_SATA                BIT(17)
-#define        BIT_USB_EHCI            BIT(18)
-#define        BIT_SEC                 BIT(19)
+#define        BIT_USH                 BIT(15) /* CHT datasheet: reserved */
+#define        BIT_GBE                 BIT(16) /* CHT datasheet: reserved */
+#define        BIT_SATA                BIT(17) /* CHT datasheet: reserved */
+#define        BIT_USB_EHCI            BIT(18) /* CHT datasheet: XHCI!    */
+#define        BIT_SEC                 BIT(19) /* BYT datasheet: reserved */
 #define        BIT_PCIE_PORT0          BIT(20)
 #define        BIT_PCIE_PORT1          BIT(21)
 #define        BIT_PCIE_PORT2          BIT(22)
index 686291b878525a9cfdd530ab4a76bf8aa83a2cc3..63cca3b58d6dfebfa7d31fa2b952944d5e57b60c 100644 (file)
@@ -48,7 +48,8 @@ u8 wmidev_instance_count(struct wmi_device *wdev);
  * struct wmi_driver - WMI driver structure
  * @driver: Driver model structure
  * @id_table: List of WMI GUIDs supported by this driver
- * @no_notify_data: WMI events provide no event data
+ * @no_notify_data: Driver supports WMI events which provide no event data
+ * @no_singleton: Driver can be instantiated multiple times
  * @probe: Callback for device binding
  * @remove: Callback for device unbinding
  * @notify: Callback for receiving WMI events
@@ -59,6 +60,7 @@ struct wmi_driver {
        struct device_driver driver;
        const struct wmi_device_id *id_table;
        bool no_notify_data;
+       bool no_singleton;
 
        int (*probe)(struct wmi_device *wdev, const void *context);
        void (*remove)(struct wmi_device *wdev);
index af0af3f1d9b7c081fb61ab582482302094409ff3..8ce2de120f2d47c09b4cf7d4985b9ecc939a7c22 100644 (file)
 
 TRACE_EVENT(ifs_status,
 
-       TP_PROTO(int cpu, int start, int stop, u64 status),
+       TP_PROTO(int batch, int start, int stop, u64 status),
 
-       TP_ARGS(cpu, start, stop, status),
+       TP_ARGS(batch, start, stop, status),
 
        TP_STRUCT__entry(
+               __field(        int,    batch   )
                __field(        u64,    status  )
-               __field(        int,    cpu     )
                __field(        u16,    start   )
                __field(        u16,    stop    )
        ),
 
        TP_fast_assign(
-               __entry->cpu    = cpu;
+               __entry->batch  = batch;
                __entry->start  = start;
                __entry->stop   = stop;
                __entry->status = status;
        ),
 
-       TP_printk("cpu: %d, start: %.4x, stop: %.4x, status: %.16llx",
-               __entry->cpu,
+       TP_printk("batch: %.2d, start: %.4x, stop: %.4x, status: %.16llx",
+               __entry->batch,
                __entry->start,
                __entry->stop,
                __entry->status)
index 022a520e31fc23abdb9dbfdcfa06edc0b692a1be..03edf2ccdf6c80c52eca377c3f4066819e88555f 100644 (file)
 
 #define KEY_ALS_TOGGLE         0x230   /* Ambient light sensor */
 #define KEY_ROTATE_LOCK_TOGGLE 0x231   /* Display rotation lock */
+#define KEY_REFRESH_RATE_TOGGLE        0x232   /* Display refresh rate toggle */
 
 #define KEY_BUTTONCONFIG               0x240   /* AL Button Configuration */
 #define KEY_TASKMANAGER                0x241   /* AL Task/Project Manager */