From patchwork Wed Jul 29 21:37:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "David E. Box" X-Patchwork-Id: 1338517 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=linux.intel.com Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BH6L82vLBz9sSd for ; Thu, 30 Jul 2020 07:36:16 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727777AbgG2VgK (ORCPT ); Wed, 29 Jul 2020 17:36:10 -0400 Received: from mga05.intel.com ([192.55.52.43]:20884 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727115AbgG2VgJ (ORCPT ); Wed, 29 Jul 2020 17:36:09 -0400 IronPort-SDR: Emv2NJKikdXR7r3WDb4xc9yiHNMIFjdIWfU1s7oWC790/kL/6PGOaQP+F+3b5fDTaZ2tWPNBlB OC8a7hdo1xLA== X-IronPort-AV: E=McAfee;i="6000,8403,9697"; a="236368347" X-IronPort-AV: E=Sophos;i="5.75,411,1589266800"; d="scan'208";a="236368347" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Jul 2020 14:36:08 -0700 IronPort-SDR: B0BgWXushly6rMK6lmVw+8opEoyBayKc8NiA2qhs4FvBR0xIz2B2LFVyoK71ZPFeFx9yb+jsNy OAQ6f0nyXp/Q== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.75,411,1589266800"; d="scan'208";a="320872926" Received: from linux.intel.com ([10.54.29.200]) by orsmga008.jf.intel.com with ESMTP; 29 Jul 2020 14:36:07 -0700 Received: from debox1-desk2.jf.intel.com (debox1-desk2.jf.intel.com [10.54.75.16]) by linux.intel.com (Postfix) with ESMTP id BDCF95807FF; Wed, 29 Jul 2020 14:36:05 -0700 (PDT) From: "David E. Box" To: lee.jones@linaro.org, david.e.box@linux.intel.com, dvhart@infradead.org, andy@infradead.org, bhelgaas@google.com, alexander.h.duyck@linux.intel.com Cc: linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, linux-pci@vger.kernel.org, Andy Shevchenko Subject: [PATCH V5 1/3] PCI: Add defines for Designated Vendor-Specific Extended Capability Date: Wed, 29 Jul 2020 14:37:17 -0700 Message-Id: <20200729213719.17795-2-david.e.box@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200717190620.29821-1-david.e.box@linux.intel.com> References: <20200717190620.29821-1-david.e.box@linux.intel.com> MIME-Version: 1.0 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org Add PCIe Designated Vendor-Specific Extended Capability (DVSEC) and defines for the header offsets. Defined in PCIe r5.0, sec 7.9.6. Signed-off-by: David E. Box Acked-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- include/uapi/linux/pci_regs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index f9701410d3b5..beafeee39e44 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -720,6 +720,7 @@ #define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */ #define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */ #define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ +#define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ #define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT @@ -1062,6 +1063,10 @@ #define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ #define PCI_L1SS_CTL2 0x0c /* Control 2 Register */ +/* Designated Vendor-Specific (DVSEC, PCI_EXT_CAP_ID_DVSEC) */ +#define PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ +#define PCI_DVSEC_HEADER2 0x8 /* Designated Vendor-Specific Header2 */ + /* Data Link Feature */ #define PCI_DLF_CAP 0x04 /* Capabilities Register */ #define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */ From patchwork Wed Jul 29 21:37:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "David E. Box" X-Patchwork-Id: 1338518 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=linux.intel.com Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BH6LH6WmQz9sRK for ; Thu, 30 Jul 2020 07:36:23 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727803AbgG2VgS (ORCPT ); Wed, 29 Jul 2020 17:36:18 -0400 Received: from mga18.intel.com ([134.134.136.126]:59468 "EHLO mga18.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726581AbgG2VgH (ORCPT ); Wed, 29 Jul 2020 17:36:07 -0400 IronPort-SDR: V5pBrKjo8ViWFks+vZEsihwui+iExQJ29Z99gVE0fpYvm9rqg6w1MNrq4jiyk3KWDkTJcAV819 v3a++2ce/bUw== X-IronPort-AV: E=McAfee;i="6000,8403,9697"; a="139035033" X-IronPort-AV: E=Sophos;i="5.75,411,1589266800"; d="scan'208";a="139035033" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga004.jf.intel.com ([10.7.209.38]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Jul 2020 14:36:06 -0700 IronPort-SDR: 4OJq/7lhhTllP9wG3CjLkHEcL5bbA68kcUCMUORl13K8G2i9TVopWIO7GMZyMqURgq/3nY845O 2xCIMhkxWAfw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.75,411,1589266800"; d="scan'208";a="434820935" Received: from linux.intel.com ([10.54.29.200]) by orsmga004.jf.intel.com with ESMTP; 29 Jul 2020 14:36:05 -0700 Received: from debox1-desk2.jf.intel.com (debox1-desk2.jf.intel.com [10.54.75.16]) by linux.intel.com (Postfix) with ESMTP id C78AA580295; Wed, 29 Jul 2020 14:36:05 -0700 (PDT) From: "David E. Box" To: lee.jones@linaro.org, david.e.box@linux.intel.com, dvhart@infradead.org, andy@infradead.org, bhelgaas@google.com, alexander.h.duyck@linux.intel.com Cc: linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, linux-pci@vger.kernel.org, Andy Shevchenko Subject: [PATCH V5 2/3] mfd: Intel Platform Monitoring Technology support Date: Wed, 29 Jul 2020 14:37:18 -0700 Message-Id: <20200729213719.17795-3-david.e.box@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200717190620.29821-1-david.e.box@linux.intel.com> References: <20200717190620.29821-1-david.e.box@linux.intel.com> MIME-Version: 1.0 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org Intel Platform Monitoring Technology (PMT) is an architecture for enumerating and accessing hardware monitoring facilities. PMT supports multiple types of monitoring capabilities. This driver creates platform devices for each type so that they may be managed by capability specific drivers (to be introduced). Capabilities are discovered using PCIe DVSEC ids. Support is included for the 3 current capability types, Telemetry, Watcher, and Crashlog. The features are available on new Intel platforms starting from Tiger Lake for which support is added. Also add a quirk mechanism for several early hardware differences and bugs. For Tiger Lake, do not support Watcher and Crashlog capabilities since they will not be compatible with future product. Also, fix use a quirk to fix the discovery table offset. Co-developed-by: Alexander Duyck Signed-off-by: Alexander Duyck Signed-off-by: David E. Box Reviewed-by: Andy Shevchenko --- MAINTAINERS | 5 + drivers/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 1 + drivers/mfd/intel_pmt.c | 220 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 drivers/mfd/intel_pmt.c diff --git a/MAINTAINERS b/MAINTAINERS index f0569cf304ca..b69429c70330 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8845,6 +8845,11 @@ F: drivers/mfd/intel_soc_pmic* F: include/linux/mfd/intel_msic.h F: include/linux/mfd/intel_soc_pmic* +INTEL PMT DRIVER +M: "David E. Box" +S: Maintained +F: drivers/mfd/intel_pmt.c + INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT M: Stanislav Yakovlev L: linux-wireless@vger.kernel.org diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index a37d7d171382..5dd05f1b8ce5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -670,6 +670,16 @@ config MFD_INTEL_PMC_BXT Register and P-unit access. In addition this creates devices for iTCO watchdog and telemetry that are part of the PMC. +config MFD_INTEL_PMT + tristate "Intel Platform Monitoring Technology (PMT) support" + depends on PCI + select MFD_CORE + help + The Intel Platform Monitoring Technology (PMT) is an interface that + provides access to hardware monitor registers. This driver supports + Telemetry, Watcher, and Crashlog PMT capabilities/devices for + platforms starting from Tiger Lake. + config MFD_IPAQ_MICRO bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" depends on SA1100_H3100 || SA1100_H3600 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9367a92f795a..1961b4737985 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -216,6 +216,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o +obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o diff --git a/drivers/mfd/intel_pmt.c b/drivers/mfd/intel_pmt.c new file mode 100644 index 000000000000..0e572b105101 --- /dev/null +++ b/drivers/mfd/intel_pmt.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitoring Technology PMT driver + * + * Copyright (c) 2020, Intel Corporation. + * All Rights Reserved. + * + * Author: David E. Box + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Intel DVSEC capability vendor space offsets */ +#define INTEL_DVSEC_ENTRIES 0xA +#define INTEL_DVSEC_SIZE 0xB +#define INTEL_DVSEC_TABLE 0xC +#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) +#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) +#define INTEL_DVSEC_ENTRY_SIZE 4 + +/* PMT capabilities */ +#define DVSEC_INTEL_ID_TELEMETRY 2 +#define DVSEC_INTEL_ID_WATCHER 3 +#define DVSEC_INTEL_ID_CRASHLOG 4 + +struct intel_dvsec_header { + u16 length; + u16 id; + u8 num_entries; + u8 entry_size; + u8 tbir; + u32 offset; +}; + +enum pmt_quirks { + /* Watcher capability not supported */ + PMT_QUIRK_NO_WATCHER = BIT(0), + + /* Crashlog capability not supported */ + PMT_QUIRK_NO_CRASHLOG = BIT(1), + + /* Use shift instead of mask to read discovery table offset */ + PMT_QUIRK_TABLE_SHIFT = BIT(2), +}; + +struct pmt_platform_info { + unsigned long quirks; +}; + +static const struct pmt_platform_info tgl_info = { + .quirks = PMT_QUIRK_NO_WATCHER | PMT_QUIRK_NO_CRASHLOG | + PMT_QUIRK_TABLE_SHIFT, +}; + +static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header, + struct pmt_platform_info *info) +{ + struct device *dev = &pdev->dev; + struct resource *res, *tmp; + struct mfd_cell *cell; + const char *name; + int count = header->num_entries; + int size = header->entry_size; + int id = header->id; + int i; + + switch (id) { + case DVSEC_INTEL_ID_TELEMETRY: + name = "pmt_telemetry"; + break; + case DVSEC_INTEL_ID_WATCHER: + if (info->quirks & PMT_QUIRK_NO_WATCHER) { + dev_info(dev, "Watcher not supported\n"); + return 0; + } + name = "pmt_watcher"; + break; + case DVSEC_INTEL_ID_CRASHLOG: + if (info->quirks & PMT_QUIRK_NO_CRASHLOG) { + dev_info(dev, "Crashlog not supported\n"); + return 0; + } + name = "pmt_crashlog"; + break; + default: + dev_err(dev, "Unrecognized PMT capability: %d\n", id); + return -EINVAL; + } + + if (!header->num_entries || !header->entry_size) { + dev_err(dev, "Invalid count or size for %s header\n", name); + return -EINVAL; + } + + cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL); + if (!cell) + return -ENOMEM; + + res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + if (info->quirks & PMT_QUIRK_TABLE_SHIFT) + header->offset >>= 3; + + /* + * The PMT DVSEC contains the starting offset and count for a block of + * discovery tables, each providing access to monitoring facilities for + * a section of the device. Create a resource list of these tables to + * provide to the driver. + */ + for (i = 0, tmp = res; i < count; i++, tmp++) { + tmp->start = pdev->resource[header->tbir].start + + header->offset + i * (size << 2); + tmp->end = tmp->start + (size << 2) - 1; + tmp->flags = IORESOURCE_MEM; + } + + cell->resources = res; + cell->num_resources = count; + cell->name = name; + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, + NULL); +} + +static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct pmt_platform_info *info; + bool found_devices = false; + int ret, pos = 0; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + do { + struct intel_dvsec_header header; + u32 table; + u16 vid; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); + if (!pos) + break; + + pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid); + if (vid != PCI_VENDOR_ID_INTEL) + continue; + + pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, + &header.id); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, + &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, + &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, + &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + ret = pmt_add_dev(pdev, &header, info); + if (ret) { + dev_warn(&pdev->dev, + "Failed to add device for DVSEC id %d\n", + header.id); + continue; + } + + found_devices = true; + } while (true); + + if (!found_devices) { + dev_err(&pdev->dev, "No supported PMT capabilities found.\n"); + return -ENODEV; + } + + pm_runtime_put(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + return 0; +} + +static void pmt_pci_remove(struct pci_dev *pdev) +{ + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); +} + +#define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d +static const struct pci_device_id pmt_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) }, + { } +}; +MODULE_DEVICE_TABLE(pci, pmt_pci_ids); + +static struct pci_driver pmt_pci_driver = { + .name = "intel-pmt", + .id_table = pmt_pci_ids, + .probe = pmt_pci_probe, + .remove = pmt_pci_remove, +}; +module_pci_driver(pmt_pci_driver); + +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel Platform Monitoring Technology PMT driver"); +MODULE_LICENSE("GPL v2"); From patchwork Wed Jul 29 21:37:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "David E. Box" X-Patchwork-Id: 1338519 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=linux.intel.com Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BH6LJ3S4Xz9sSd for ; Thu, 30 Jul 2020 07:36:24 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727016AbgG2VgH (ORCPT ); Wed, 29 Jul 2020 17:36:07 -0400 Received: from mga02.intel.com ([134.134.136.20]:18831 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726496AbgG2VgH (ORCPT ); Wed, 29 Jul 2020 17:36:07 -0400 IronPort-SDR: 1wpkZT00fG7bDjwSV5kZSKDBscigsLh5oQZjqLq10hgKb5JBo8G3Yc1+7Txn4KAq85Qs21VV6t XV6vOlC4sByg== X-IronPort-AV: E=McAfee;i="6000,8403,9697"; a="139508482" X-IronPort-AV: E=Sophos;i="5.75,411,1589266800"; d="scan'208";a="139508482" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by orsmga101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Jul 2020 14:36:06 -0700 IronPort-SDR: zj0b/QqPwtjYGAeEphSAW9QVcIBbdkSRqWcsJdmTaKo87GNc8bWeku8BEybCEl8VKQD3kOCQOV tFK8U5orY4ZQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.75,411,1589266800"; d="scan'208";a="490897785" Received: from linux.intel.com ([10.54.29.200]) by fmsmga005.fm.intel.com with ESMTP; 29 Jul 2020 14:36:06 -0700 Received: from debox1-desk2.jf.intel.com (debox1-desk2.jf.intel.com [10.54.75.16]) by linux.intel.com (Postfix) with ESMTP id DD1C7580834; Wed, 29 Jul 2020 14:36:05 -0700 (PDT) From: "David E. Box" To: lee.jones@linaro.org, david.e.box@linux.intel.com, dvhart@infradead.org, andy@infradead.org, bhelgaas@google.com, alexander.h.duyck@linux.intel.com Cc: linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, linux-pci@vger.kernel.org, Andy Shevchenko Subject: [PATCH V5 3/3] platform/x86: Intel PMT Telemetry capability driver Date: Wed, 29 Jul 2020 14:37:19 -0700 Message-Id: <20200729213719.17795-4-david.e.box@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200717190620.29821-1-david.e.box@linux.intel.com> References: <20200717190620.29821-1-david.e.box@linux.intel.com> MIME-Version: 1.0 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org PMT Telemetry is a capability of the Intel Platform Monitoring Technology. The Telemetry capability provides access to device telemetry metrics that provide hardware performance data to users from continuous, memory mapped, read-only register spaces. Register mappings are not provided by the driver. Instead, a GUID is read from a header for each endpoint. The GUID identifies the device and is to be used with an XML, provided by the vendor, to discover the available set of metrics and their register mapping. This allows firmware updates to modify the register space without needing to update the driver every time with new mappings. Firmware writes a new GUID in this case to specify the new mapping. Software tools with access to the associated XML file can then interpret the changes. The module manages access to all PMT Telemetry endpoints on a system, independent of the device exporting them. It creates a pmt_telemetry class to manage the devices. For each telemetry endpoint, sysfs files provide GUID and size information as well as a pointer to the parent device the telemetry came from. Software may discover the association between endpoints and devices by iterating through the list in sysfs, or by looking for the existence of the class folder under the device of interest. A device node of the same name allows software to then map the telemetry space for direct access. Also create a PCI device id list for early telemetry hardware that require workarounds for known issues. Co-developed-by: Alexander Duyck Signed-off-by: Alexander Duyck Signed-off-by: David E. Box Reviewed-by: Andy Shevchenko --- .../ABI/testing/sysfs-class-pmt_telemetry | 46 ++ MAINTAINERS | 1 + drivers/platform/x86/Kconfig | 10 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_pmt_telemetry.c | 448 ++++++++++++++++++ 5 files changed, 506 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-pmt_telemetry create mode 100644 drivers/platform/x86/intel_pmt_telemetry.c diff --git a/Documentation/ABI/testing/sysfs-class-pmt_telemetry b/Documentation/ABI/testing/sysfs-class-pmt_telemetry new file mode 100644 index 000000000000..b0b096db9cae --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-pmt_telemetry @@ -0,0 +1,46 @@ +What: /sys/class/pmt_telemetry/ +Date: July 2020 +KernelVersion: 5.9 +Contact: David Box +Description: + The pmt_telemetry/ class directory contains information for + devices that expose hardware telemetry using Intel Platform + Monitoring Technology (PMT) + +What: /sys/class/pmt_telemetry/telem +Date: July 2020 +KernelVersion: 5.9 +Contact: David Box +Description: + The telem directory contains files describing an instance of + a PMT telemetry device that exposes hardware telemetry. Each + telem directory has an associated /dev/telem node. This + node may be opened and mapped to access the telemetry space of + the device. The register layout of the telemetry space is + determined from an XML file that matches the PCI device id and + GUID for the device. + +What: /sys/class/pmt_telemetry/telem/guid +Date: July 2020 +KernelVersion: 5.9 +Contact: David Box +Description: + (RO) The GUID for this telemetry device. The GUID identifies + the version of the XML file for the parent device that is to + be used to get the register layout. + +What: /sys/class/pmt_telemetry/telem/size +Date: July 2020 +KernelVersion: 5.9 +Contact: David Box +Description: + (RO) The size of telemetry region in bytes that corresponds to + the mapping size for the /dev/telem device node. + +What: /sys/class/pmt_telemetry/telem/offset +Date: July 2020 +KernelVersion: 5.9 +Contact: David Box +Description: + (RO) The offset of telemetry region in bytes that corresponds to + the mapping for the /dev/telem device node. diff --git a/MAINTAINERS b/MAINTAINERS index b69429c70330..40794cc721af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8849,6 +8849,7 @@ INTEL PMT DRIVER M: "David E. Box" S: Maintained F: drivers/mfd/intel_pmt.c +F: drivers/platform/x86/intel_pmt_* INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT M: Stanislav Yakovlev diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0581a54cf562..8552b094d005 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1339,6 +1339,16 @@ config INTEL_PMC_CORE - LTR Ignore - MPHY/PLL gating status (Sunrisepoint PCH only) +config INTEL_PMT_TELEMETRY + tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" + help + The Intel Platform Monitory Technology (PMT) Telemetry driver provides + access to hardware telemetry metrics on devices that support the + feature. + + For more information, see + + config INTEL_PUNIT_IPC tristate "Intel P-Unit IPC Driver" help diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 2b85852a1a87..95cd3d0be17f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -139,6 +139,7 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o +obj-$(CONFIG_INTEL_PMT_TELEMETRY) += intel_pmt_telemetry.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o diff --git a/drivers/platform/x86/intel_pmt_telemetry.c b/drivers/platform/x86/intel_pmt_telemetry.c new file mode 100644 index 000000000000..17f814ece30a --- /dev/null +++ b/drivers/platform/x86/intel_pmt_telemetry.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitory Technology Telemetry driver + * + * Copyright (c) 2020, Intel Corporation. + * All Rights Reserved. + * + * Author: "David E. Box" + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TELEM_DEV_NAME "pmt_telemetry" + +/* Telemetry access types */ +#define TELEM_ACCESS_FUTURE 1 +#define TELEM_ACCESS_BARID 2 +#define TELEM_ACCESS_LOCAL 3 + +#define TELEM_GUID_OFFSET 0x4 +#define TELEM_BASE_OFFSET 0x8 +#define TELEM_TBIR_MASK GENMASK(2, 0) +#define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) +#define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) +/* size is in bytes */ +#define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10) + +#define TELEM_XA_START 0 +#define TELEM_XA_MAX INT_MAX +#define TELEM_XA_LIMIT XA_LIMIT(TELEM_XA_START, TELEM_XA_MAX) + +/* Used by client hardware to identify a fixed telemetry entry*/ +#define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 + +static DEFINE_XARRAY_ALLOC(telem_array); + +struct pmt_telem_priv; + +struct telem_header { + u8 access_type; + u8 telem_type; + u16 size; + u32 guid; + u32 base_offset; + u8 tbir; +}; + +struct pmt_telem_entry { + struct pmt_telem_priv *priv; + struct telem_header header; + struct resource *header_res; + unsigned long base_addr; + void __iomem *disc_table; + struct cdev cdev; + dev_t devt; + int devid; +}; + +struct pmt_telem_priv { + struct pmt_telem_entry *entry; + int num_entries; + struct device *dev; +}; + +/* + * devfs + */ +static int pmt_telem_open(struct inode *inode, struct file *filp) +{ + struct pmt_telem_priv *priv; + struct pmt_telem_entry *entry; + struct pci_driver *pci_drv; + struct pci_dev *pci_dev; + + if (!perfmon_capable()) + return -EPERM; + + entry = container_of(inode->i_cdev, struct pmt_telem_entry, cdev); + priv = entry->priv; + pci_dev = to_pci_dev(priv->dev->parent); + + pci_drv = pci_dev_driver(pci_dev); + if (!pci_drv) + return -ENODEV; + + filp->private_data = entry; + get_device(&pci_dev->dev); + + if (!try_module_get(pci_drv->driver.owner)) { + put_device(&pci_dev->dev); + return -ENODEV; + } + + return 0; +} + +static int pmt_telem_release(struct inode *inode, struct file *filp) +{ + struct pmt_telem_entry *entry = filp->private_data; + struct pci_dev *pci_dev = to_pci_dev(entry->priv->dev->parent); + struct pci_driver *pci_drv = pci_dev_driver(pci_dev); + + put_device(&pci_dev->dev); + module_put(pci_drv->driver.owner); + + return 0; +} + +static int pmt_telem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct pmt_telem_entry *entry = filp->private_data; + struct pmt_telem_priv *priv; + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long phys = entry->base_addr; + unsigned long pfn = PFN_DOWN(phys); + unsigned long psize; + + priv = entry->priv; + psize = (PFN_UP(entry->base_addr + entry->header.size) - pfn) * PAGE_SIZE; + if (vsize > psize) { + dev_err(priv->dev, "Requested mmap size is too large\n"); + return -EINVAL; + } + + if ((vma->vm_flags & VM_WRITE) || (vma->vm_flags & VM_MAYWRITE)) + return -EPERM; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, pfn, vsize, + vma->vm_page_prot)) + return -EINVAL; + + return 0; +} + +static const struct file_operations pmt_telem_fops = { + .owner = THIS_MODULE, + .open = pmt_telem_open, + .mmap = pmt_telem_mmap, + .release = pmt_telem_release, +}; + +/* + * sysfs + */ +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pmt_telem_entry *entry = dev_get_drvdata(dev); + + return sprintf(buf, "0x%x\n", entry->header.guid); +} +static DEVICE_ATTR_RO(guid); + +static ssize_t size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pmt_telem_entry *entry = dev_get_drvdata(dev); + + /* Display buffer size in bytes */ + return sprintf(buf, "%u\n", entry->header.size); +} +static DEVICE_ATTR_RO(size); + +static ssize_t offset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pmt_telem_entry *entry = dev_get_drvdata(dev); + + /* Display buffer offset in bytes */ + return sprintf(buf, "%lu\n", offset_in_page(entry->base_addr)); +} +static DEVICE_ATTR_RO(offset); + +static struct attribute *pmt_telem_attrs[] = { + &dev_attr_guid.attr, + &dev_attr_size.attr, + &dev_attr_offset.attr, + NULL +}; +ATTRIBUTE_GROUPS(pmt_telem); + +static struct class pmt_telem_class = { + .owner = THIS_MODULE, + .name = "pmt_telemetry", + .dev_groups = pmt_telem_groups, +}; + +/* + * driver initialization + */ +static const struct pci_device_id pmt_telem_early_client_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */ + { } +}; + +static bool pmt_telem_is_early_client_hw(struct device *dev) +{ + struct pci_dev *parent = to_pci_dev(dev->parent); + + return !!pci_match_id(pmt_telem_early_client_pci_ids, parent); +} + +static int pmt_telem_create_dev(struct pmt_telem_priv *priv, + struct pmt_telem_entry *entry) +{ + struct pci_dev *pci_dev; + struct device *dev; + int ret; + + cdev_init(&entry->cdev, &pmt_telem_fops); + ret = cdev_add(&entry->cdev, entry->devt, 1); + if (ret) { + dev_err(priv->dev, "Could not add char dev\n"); + return ret; + } + + pci_dev = to_pci_dev(priv->dev->parent); + dev = device_create(&pmt_telem_class, &pci_dev->dev, entry->devt, + entry, "telem%d", entry->devid); + if (IS_ERR(dev)) { + dev_err(priv->dev, "Could not create device node\n"); + cdev_del(&entry->cdev); + } + + return PTR_ERR_OR_ZERO(dev); +} + +static void pmt_telem_populate_header(void __iomem *disc_offset, + struct telem_header *header) +{ + header->access_type = TELEM_ACCESS(readb(disc_offset)); + header->telem_type = TELEM_TYPE(readb(disc_offset)); + header->size = TELEM_SIZE(readl(disc_offset)); + header->guid = readl(disc_offset + TELEM_GUID_OFFSET); + header->base_offset = readl(disc_offset + TELEM_BASE_OFFSET); + + /* + * For non-local access types the lower 3 bits of base offset + * contains the index of the base address register where the + * telemetry can be found. + */ + header->tbir = header->base_offset & TELEM_TBIR_MASK; + header->base_offset ^= header->tbir; +} + +static int pmt_telem_add_entry(struct pmt_telem_priv *priv, + struct pmt_telem_entry *entry) +{ + struct resource *res = entry->header_res; + struct pci_dev *pci_dev = to_pci_dev(priv->dev->parent); + int ret; + + pmt_telem_populate_header(entry->disc_table, &entry->header); + + /* Local access and BARID only for now */ + switch (entry->header.access_type) { + case TELEM_ACCESS_LOCAL: + if (entry->header.tbir) { + dev_err(priv->dev, + "Unsupported BAR index %d for access type %d\n", + entry->header.tbir, entry->header.access_type); + return -EINVAL; + } + + /* + * For access_type LOCAL, the base address is as follows: + * base address = header address + header length + base offset + */ + entry->base_addr = res->start + resource_size(res) + + entry->header.base_offset; + break; + + case TELEM_ACCESS_BARID: + entry->base_addr = pci_dev->resource[entry->header.tbir].start + + entry->header.base_offset; + break; + + default: + dev_err(priv->dev, "Unsupported access type %d\n", + entry->header.access_type); + return -EINVAL; + } + + ret = alloc_chrdev_region(&entry->devt, 0, 1, TELEM_DEV_NAME); + if (ret) { + dev_err(priv->dev, + "PMT telemetry chrdev_region error: %d\n", ret); + return ret; + } + + ret = xa_alloc(&telem_array, &entry->devid, entry, TELEM_XA_LIMIT, + GFP_KERNEL); + if (ret) + goto fail_xa_alloc; + + ret = pmt_telem_create_dev(priv, entry); + if (ret) + goto fail_create_dev; + + entry->priv = priv; + priv->num_entries++; + return 0; + +fail_create_dev: + xa_erase(&telem_array, entry->devid); +fail_xa_alloc: + unregister_chrdev_region(entry->devt, 1); + + return ret; +} + +static bool pmt_telem_region_overlaps(struct platform_device *pdev, + void __iomem *disc_table) +{ + u32 guid; + + guid = readl(disc_table + TELEM_GUID_OFFSET); + + return guid == TELEM_CLIENT_FIXED_BLOCK_GUID; +} + +static void pmt_telem_remove_entries(struct pmt_telem_priv *priv) +{ + int i; + + for (i = 0; i < priv->num_entries; i++) { + device_destroy(&pmt_telem_class, priv->entry[i].devt); + cdev_del(&priv->entry[i].cdev); + xa_erase(&telem_array, priv->entry[i].devid); + unregister_chrdev_region(priv->entry[i].devt, 1); + } +} + +static int pmt_telem_probe(struct platform_device *pdev) +{ + struct pmt_telem_priv *priv; + struct pmt_telem_entry *entry; + bool early_hw; + int i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->dev = &pdev->dev; + + priv->entry = devm_kcalloc(&pdev->dev, pdev->num_resources, + sizeof(struct pmt_telem_entry), GFP_KERNEL); + if (!priv->entry) + return -ENOMEM; + + if (pmt_telem_is_early_client_hw(&pdev->dev)) + early_hw = true; + + for (i = 0, entry = priv->entry; i < pdev->num_resources; + i++, entry++) { + int ret; + + entry->header_res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!entry->header_res) { + pmt_telem_remove_entries(priv); + return -ENODEV; + } + + entry->disc_table = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(entry->disc_table)) { + pmt_telem_remove_entries(priv); + return PTR_ERR(entry->disc_table); + } + + if (pmt_telem_region_overlaps(pdev, entry->disc_table) && + early_hw) + continue; + + ret = pmt_telem_add_entry(priv, entry); + if (ret) { + pmt_telem_remove_entries(priv); + return ret; + } + } + + return 0; +} + +static int pmt_telem_remove(struct platform_device *pdev) +{ + struct pmt_telem_priv *priv = platform_get_drvdata(pdev); + + pmt_telem_remove_entries(priv); + + return 0; +} + +static const struct platform_device_id pmt_telem_table[] = { + { + .name = "pmt_telemetry", + }, + {} +}; +MODULE_DEVICE_TABLE(platform, pmt_telem_table); + +static struct platform_driver pmt_telem_driver = { + .driver = { + .name = TELEM_DEV_NAME, + }, + .probe = pmt_telem_probe, + .remove = pmt_telem_remove, + .id_table = pmt_telem_table, +}; + +static int __init pmt_telem_init(void) +{ + int ret = class_register(&pmt_telem_class); + + if (ret) + return ret; + + ret = platform_driver_register(&pmt_telem_driver); + if (ret) + class_unregister(&pmt_telem_class); + + return ret; +} +module_init(pmt_telem_init); + +static void __exit pmt_telem_exit(void) +{ + platform_driver_unregister(&pmt_telem_driver); + class_unregister(&pmt_telem_class); + xa_destroy(&telem_array); +} +module_exit(pmt_telem_exit); + +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel PMT Telemetry driver"); +MODULE_ALIAS("platform:" TELEM_DEV_NAME); +MODULE_LICENSE("GPL v2");