From patchwork Thu Apr 21 11:03:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rajneesh Bhardwaj X-Patchwork-Id: 8898861 Return-Path: X-Original-To: patchwork-platform-driver-x86@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 33FF29F1C1 for ; Thu, 21 Apr 2016 11:03:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 1D32620340 for ; Thu, 21 Apr 2016 11:03:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CA01F20303 for ; Thu, 21 Apr 2016 11:03:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751824AbcDULDi (ORCPT ); Thu, 21 Apr 2016 07:03:38 -0400 Received: from mga04.intel.com ([192.55.52.120]:2752 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751582AbcDULDh (ORCPT ); Thu, 21 Apr 2016 07:03:37 -0400 Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga104.fm.intel.com with ESMTP; 21 Apr 2016 04:03:37 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.24,512,1455004800"; d="scan'208";a="89246852" Received: from rajaneesh-optiplex-9010.iind.intel.com ([10.223.135.11]) by fmsmga004.fm.intel.com with ESMTP; 21 Apr 2016 04:03:26 -0700 From: Rajneesh Bhardwaj To: dvhart@infradead.org Cc: platform-driver-x86@vger.kernel.org, vishwanath.somayaji@intel.com, rajneesh.bhardwaj@intel.com Subject: [PATCH] platform:x86: Add PMC Driver for Intel Core SOC Date: Thu, 21 Apr 2016 16:33:25 +0530 Message-Id: <1461236605-27245-1-git-send-email-rajneesh.bhardwaj@intel.com> X-Mailer: git-send-email 1.9.1 Sender: platform-driver-x86-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: platform-driver-x86@vger.kernel.org X-Spam-Status: No, score=-7.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds the Power Management Controller driver as a pci driver for Intel Core SOC architecture. This driver can be utilized for debugging as well as for detecting fragile SLP_S0 failures and take corrective actions for scenarios when PCH SLP_S0 signal is not asserted after kernel freeze as part of suspend to idle flow i.e. 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 PC10 so the suspend to idle results in package C-10 state but PC10 is not sufficient for realizing the best possible power potential which SLP_S0 signal assertion can provide. In general, SLP_S0 assertion == PC10 + PCH low power mode + ModPhy Lanes power gated + PLL Idle. SLP_S0 signal is often connected to Embedded Controller and Power Management IC for other platform power management related optimizations. 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 Signed-off-by: Vishwanath Somayaji --- arch/x86/include/asm/pmc_core.h | 53 +++++++++ drivers/platform/x86/Kconfig | 10 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_pmc_core.c | 200 ++++++++++++++++++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 arch/x86/include/asm/pmc_core.h create mode 100644 drivers/platform/x86/intel_pmc_core.c diff --git a/arch/x86/include/asm/pmc_core.h b/arch/x86/include/asm/pmc_core.h new file mode 100644 index 0000000..3ea61bf --- /dev/null +++ b/arch/x86/include/asm/pmc_core.h @@ -0,0 +1,53 @@ +/* + * Intel Core SOC Power Management Controller Header File + * + * Copyright (c) 2016, Intel Corporation. + * Author: Rajneesh Bhardwaj (rajneesh.bhardwaj@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 + +/* Skylake Power Management Controller PCI Device ID */ +#define PCI_DEVICE_ID_SKL_PMC 0x9d21 +#define PMC_BASE_ADDR_OFFSET 0x48 +#define PMC_SLP_S0_RESIDENCY_COUNTER 0x13c +#define PMC_MMIO_REG_LEN 0x100 +#define PMC_REG_BIT_WIDTH 0x20 +#define SLP_S0_RESIDENCY_COUNTER_GRANULARITY 0x64 + +/** + * struct pmc_dev - pmc device structure + * @base_addr: comtains pmc base address + * @regmap: 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 *regmap; +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ + int feature_available; +}; + +int intel_pmc_slp_s0_counter_read(u64 *data); + +#endif + +/* PMC_CORE_H */ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ed2004b..5db364c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -821,6 +821,16 @@ config INTEL_IPS functionality. If in doubt, say Y here; it will only load on supported platforms. +config INTEL_PMC_CORE + bool "Intel PMC Core driver" + depends on X86 && PCI + default y + ---help--- + This driver exposes the features provided by Power Management + Controller for Intel Core SOC. Intel Platform Controller Hub + for Intel Core SOCs provides access to Power Management Controller + registers via pci interface. + config INTEL_IMR bool "Intel Isolated Memory Region support" default n diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 448443c..9b11b40 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -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 index 0000000..42cee87 --- /dev/null +++ b/drivers/platform/x86/intel_pmc_core.c @@ -0,0 +1,200 @@ +/* + * Intel Core SOC Power Management Controller Driver + * + * Copyright (c) 2016, Intel Corporation. + * Author: Rajneesh Bhardwaj (rajneesh.bhardwaj@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 +#include +#include +#include +#include +#include +#include +#include +#include + +static struct pmc_dev pmc; + +static const struct pci_device_id pmc_pci_ids[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_SKL_PMC), (kernel_ulong_t)NULL }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, pmc_pci_ids); + +/** + * 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(u64 *data) +{ + int ret = 0; + + if (pmc.feature_available) { + *data = readl(pmc.regmap + PMC_SLP_S0_RESIDENCY_COUNTER); + *data *= SLP_S0_RESIDENCY_COUNTER_GRANULARITY; + } else { + ret = -EACCES; + } + + return ret; +} +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) +{ + u64 counter_val; + int err; + + err = intel_pmc_slp_s0_counter_read(&counter_val); + if (err) { + counter_val = 0; + seq_printf(s, "%lld\n", counter_val); + } else { + seq_printf(s, "%lld\n", 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 *pmc) +{ + debugfs_remove_recursive(pmc->dbgfs_dir); +} + +static int pmc_core_dbgfs_register(struct pmc_dev *pmc) +{ + struct dentry *dir, *file; + int ret = 0; + + dir = debugfs_create_dir("pmc_core", NULL); + if (!dir) + return -ENOMEM; + + pmc->dbgfs_dir = dir; + file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO, + dir, pmc, &pmc_core_dev_state_ops); + + if (!file) { + pmc_core_dbgfs_unregister(pmc); + ret = -ENODEV; + } + return ret; +} + +#else + +static int pmc_core_dbgfs_register(struct pmc_dev *pmc) +{ + return 0; /* nothing to register */ +} + +#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}, /* SKL CPU*/ + { X86_VENDOR_INTEL, 6, 0x5e, X86_FEATURE_MWAIT, + (kernel_ulong_t)NULL}, /* SKL CPU*/ + {} +}; +MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_ids); + +static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int err; + const struct x86_cpu_id *cpu_id; + + cpu_id = x86_match_cpu(intel_pmc_core_ids); + if (!cpu_id) + return -EINVAL; + + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n"); + goto exit; + } + + pci_read_config_dword(dev, PMC_BASE_ADDR_OFFSET, &pmc.base_addr); + dev_dbg(&dev->dev, "PMC Core: PWRMBASE is 0x%x\n", pmc.base_addr); + + pmc.regmap = ioremap_nocache(pmc.base_addr, PMC_MMIO_REG_LEN); + if (!pmc.regmap) { + dev_err(&dev->dev, "PMC Core: ioremap failed\n"); + err = -ENOMEM; + goto disable; + } + + err = pmc_core_dbgfs_register(&pmc); + if (err) { + dev_err(&dev->dev, "PMC Core: debugfs register failed\n"); + iounmap(pmc.regmap); + goto disable; + } + + pmc.feature_available = 1; + return 0; + +disable: + pci_disable_device(dev); +exit: + return err; +} + +static void intel_pmc_core_remove(struct pci_dev *pdev) +{ +#if IS_ENABLED(CONFIG_DEBUG_FS) + pmc_core_dbgfs_unregister(&pmc); +#endif + pci_disable_device(pdev); + iounmap(pmc.regmap); +} + +static struct pci_driver intel_pmc_core_driver = { + .name = "intel_pmc_core", + .id_table = pmc_pci_ids, + .probe = pmc_core_probe, + .remove = intel_pmc_core_remove, +}; +module_pci_driver(intel_pmc_core_driver); + +MODULE_AUTHOR("Rajneesh Bhardwaj "); +MODULE_AUTHOR("Vishwanath Somayaji "); +MODULE_DESCRIPTION("Intel CORE SOC Power Management Controller Interface"); +MODULE_LICENSE("GPL v2");