platform/x86: Add PMC Driver for Intel Core SoC
authorRajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
Thu, 26 May 2016 09:11:19 +0000 (14:41 +0530)
committerDarren Hart <dvhart@linux.intel.com>
Fri, 27 May 2016 18:47:56 +0000 (11:47 -0700)
This patch adds the Power Management Controller driver as a PCI driver
for Intel Core SoC architecture.

This driver can utilize debugging capabilities and supported features
as exposed by the Power Management Controller.

Please refer to the below specification for more details on PMC features.
http://www.intel.in/content/www/in/en/chipsets/100-series-chipset-datasheet-vol-2.html

The current version of this driver exposes SLP_S0_RESIDENCY counter.
This counter can be used for detecting fragile SLP_S0 signal related
failures and take corrective actions when PCH SLP_S0 signal is not
asserted after kernel freeze as part of suspend to idle flow
(echo freeze > /sys/power/state).

Intel Platform Controller Hub (PCH) asserts SLP_S0 signal when it
detects favorable conditions to enter its low power mode. As a
pre-requisite the SoC should be in deepest possible Package C-State
and devices should be in low power mode. For example, on Skylake SoC
the deepest Package C-State is Package C10 or PC10. Suspend to idle
flow generally leads to PC10 state but PC10 state may not be sufficient
for realizing the platform wide power potential which SLP_S0 signal
assertion can provide.

SLP_S0 signal is often connected to the Embedded Controller (EC) and the
Power Management IC (PMIC) for other platform power management related
optimizations.

In general, SLP_S0 assertion == PC10 + PCH low power mode + ModPhy Lanes
power gated + PLL Idle.

As part of this driver, a mechanism to read the SLP_S0_RESIDENCY is exposed
as an API and also debugfs features are added to indicate SLP_S0 signal
assertion residency in microseconds.

echo freeze > /sys/power/state
wake the system
cat /sys/kernel/debug/pmc_core/slp_s0_residency_usec

Signed-off-by: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
Signed-off-by: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
MAINTAINERS
arch/x86/include/asm/pmc_core.h [new file with mode: 0644]
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/intel_pmc_core.c [new file with mode: 0644]
drivers/platform/x86/intel_pmc_core.h [new file with mode: 0644]

index 1c32f8a3d6c4a707b87da2394a4712d2fe499444..32a57926cd04770a366133c4ceaf59d362c5b3b9 100644 (file)
@@ -5879,6 +5879,14 @@ S:       Maintained
 F:     arch/x86/include/asm/intel_telemetry.h
 F:     drivers/platform/x86/intel_telemetry*
 
+INTEL PMC CORE DRIVER
+M:     Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+M:     Vishwanath Somayaji <vishwanath.somayaji@intel.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     arch/x86/include/asm/pmc_core.h
+F:     drivers/platform/x86/intel_pmc_core*
+
 IOC3 ETHERNET DRIVER
 M:     Ralf Baechle <ralf@linux-mips.org>
 L:     linux-mips@linux-mips.org
diff --git a/arch/x86/include/asm/pmc_core.h b/arch/x86/include/asm/pmc_core.h
new file mode 100644 (file)
index 0000000..d4855f1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Intel Core SoC Power Management Controller Header File
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+ *          Vishwanath Somayaji <vishwanath.somayaji@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _ASM_PMC_CORE_H
+#define _ASM_PMC_CORE_H
+
+/* API to read SLP_S0_RESIDENCY counter */
+int intel_pmc_slp_s0_counter_read(u32 *data);
+
+#endif /* _ASM_PMC_CORE_H */
index ed2004be13cfcc58a6c9598a234433d9c76ba078..c06bb85c283912590d306e3336e28419f32a0c7c 100644 (file)
@@ -846,6 +846,18 @@ config INTEL_IMR
 
          If you are running on a Galileo/Quark say Y here.
 
+config INTEL_PMC_CORE
+       bool "Intel PMC Core driver"
+       depends on X86 && PCI
+       ---help---
+         The Intel Platform Controller Hub for Intel Core SoCs provides access
+         to Power Management Controller registers via a PCI interface. This
+         driver can utilize debugging capabilities and supported features as
+         exposed by the Power Management Controller.
+
+         Supported features:
+               - SLP_S0_RESIDENCY counter.
+
 config IBM_RTL
        tristate "Device driver to enable PRTL support"
        depends on X86 && PCI
index 448443c3baba9652e4c0edaa2d8295b8a1c3e0f1..9b11b4073e033f66b59d05d9dce01442aeee3775 100644 (file)
@@ -69,3 +69,4 @@ obj-$(CONFIG_INTEL_PUNIT_IPC)  += intel_punit_ipc.o
 obj-$(CONFIG_INTEL_TELEMETRY)  += intel_telemetry_core.o \
                                   intel_telemetry_pltdrv.o \
                                   intel_telemetry_debugfs.o
+obj-$(CONFIG_INTEL_PMC_CORE)    += intel_pmc_core.o
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
new file mode 100644 (file)
index 0000000..2776bec
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Intel Core SoC Power Management Controller Driver
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+ *          Vishwanath Somayaji <vishwanath.somayaji@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/pmc_core.h>
+
+#include "intel_pmc_core.h"
+
+static struct pmc_dev pmc;
+
+static const struct pci_device_id pmc_pci_ids[] = {
+       { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), (kernel_ulong_t)NULL },
+       { 0, },
+};
+
+static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
+{
+       return readl(pmcdev->regbase + reg_offset);
+}
+
+static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
+{
+       return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
+}
+
+/**
+ * intel_pmc_slp_s0_counter_read() - Read SLP_S0 residency.
+ * @data: Out param that contains current SLP_S0 count.
+ *
+ * This API currently supports Intel Skylake SoC and Sunrise
+ * Point Platform Controller Hub. Future platform support
+ * should be added for platforms that support low power modes
+ * beyond Package C10 state.
+ *
+ * SLP_S0_RESIDENCY counter counts in 100 us granularity per
+ * step hence function populates the multiplied value in out
+ * parameter @data.
+ *
+ * Return: an error code or 0 on success.
+ */
+int intel_pmc_slp_s0_counter_read(u32 *data)
+{
+       struct pmc_dev *pmcdev = &pmc;
+       u32 value;
+
+       if (!pmcdev->has_slp_s0_res)
+               return -EACCES;
+
+       value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
+       *data = pmc_core_adjust_slp_s0_step(value);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int pmc_core_dev_state_show(struct seq_file *s, void *unused)
+{
+       struct pmc_dev *pmcdev = s->private;
+       u32 counter_val;
+
+       counter_val = pmc_core_reg_read(pmcdev,
+                                       SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
+       seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val));
+
+       return 0;
+}
+
+static int pmc_core_dev_state_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, pmc_core_dev_state_show, inode->i_private);
+}
+
+static const struct file_operations pmc_core_dev_state_ops = {
+       .open           = pmc_core_dev_state_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
+{
+       debugfs_remove_recursive(pmcdev->dbgfs_dir);
+}
+
+static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
+{
+       struct dentry *dir, *file;
+
+       dir = debugfs_create_dir("pmc_core", NULL);
+       if (!dir)
+               return -ENOMEM;
+
+       pmcdev->dbgfs_dir = dir;
+       file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
+                                  dir, pmcdev, &pmc_core_dev_state_ops);
+
+       if (!file) {
+               pmc_core_dbgfs_unregister(pmcdev);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+#else
+static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
+{
+       return 0;
+}
+
+static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static const struct x86_cpu_id intel_pmc_core_ids[] = {
+       { X86_VENDOR_INTEL, 6, 0x4e, X86_FEATURE_MWAIT,
+               (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */
+       { X86_VENDOR_INTEL, 6, 0x5e, X86_FEATURE_MWAIT,
+               (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */
+       {}
+};
+
+static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       struct device *ptr_dev = &dev->dev;
+       struct pmc_dev *pmcdev = &pmc;
+       const struct x86_cpu_id *cpu_id;
+       int err;
+
+       cpu_id = x86_match_cpu(intel_pmc_core_ids);
+       if (!cpu_id) {
+               dev_dbg(&dev->dev, "PMC Core: cpuid mismatch.\n");
+               return -EINVAL;
+       }
+
+       err = pcim_enable_device(dev);
+       if (err < 0) {
+               dev_dbg(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n");
+               return err;
+       }
+
+       err = pci_read_config_dword(dev,
+                                   SPT_PMC_BASE_ADDR_OFFSET,
+                                   &pmcdev->base_addr);
+       if (err < 0) {
+               dev_dbg(&dev->dev, "PMC Core: failed to read PCI config space.\n");
+               return err;
+       }
+       dev_dbg(&dev->dev, "PMC Core: PWRMBASE is %#x\n", pmcdev->base_addr);
+
+       pmcdev->regbase = devm_ioremap_nocache(ptr_dev,
+                                             pmcdev->base_addr,
+                                             SPT_PMC_MMIO_REG_LEN);
+       if (!pmcdev->regbase) {
+               dev_dbg(&dev->dev, "PMC Core: ioremap failed.\n");
+               return -ENOMEM;
+       }
+
+       err = pmc_core_dbgfs_register(pmcdev);
+       if (err < 0) {
+               dev_err(&dev->dev, "PMC Core: debugfs register failed.\n");
+               return err;
+       }
+
+       pmc.has_slp_s0_res = true;
+       return 0;
+}
+
+static struct pci_driver intel_pmc_core_driver = {
+       .name = "intel_pmc_core",
+       .id_table = pmc_pci_ids,
+       .probe = pmc_core_probe,
+};
+
+builtin_pci_driver(intel_pmc_core_driver);
diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h
new file mode 100644 (file)
index 0000000..a9dadaf
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Intel Core SoC Power Management Controller Header File
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+ *          Vishwanath Somayaji <vishwanath.somayaji@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef PMC_CORE_H
+#define PMC_CORE_H
+
+/* Sunrise Point Power Management Controller PCI Device ID */
+#define SPT_PMC_PCI_DEVICE_ID                  0x9d21
+#define SPT_PMC_BASE_ADDR_OFFSET               0x48
+#define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET      0x13c
+#define SPT_PMC_MMIO_REG_LEN                   0x100
+#define SPT_PMC_SLP_S0_RES_COUNTER_STEP                0x64
+
+/**
+ * struct pmc_dev - pmc device structure
+ * @base_addr:         comtains pmc base address
+ * @regbase:           pointer to io-remapped memory location
+ * @dbgfs_dir:         path to debug fs interface
+ * @feature_available: flag to indicate whether
+ *                     the feature is available
+ *                     on a particular platform or not.
+ *
+ * pmc_dev contains info about power management controller device.
+ */
+struct pmc_dev {
+       u32 base_addr;
+       void __iomem *regbase;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+       struct dentry *dbgfs_dir;
+#endif /* CONFIG_DEBUG_FS */
+       bool has_slp_s0_res;
+};
+
+#endif /* PMC_CORE_H */