The PMC SSRAM device contains PMC metrics that are structured in Intel
Platform Monitoring Technology (PMT) telemetry regions. Register these
telemetry regions from the driver so that they may be read using the
Intel PMT API.
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---
drivers/platform/x86/intel/pmc/Kconfig | 1 +
drivers/platform/x86/intel/pmc/core.c | 6 ++
drivers/platform/x86/intel/pmc/core.h | 2 +
drivers/platform/x86/intel/pmc/mtl.c | 106 +++++++++++++++++++++++++
4 files changed, 115 insertions(+)
@@ -7,6 +7,7 @@ config INTEL_PMC_CORE
tristate "Intel PMC Core driver"
depends on PCI
depends on ACPI
+ depends on INTEL_PMT_TELEMETRY
help
The Intel Platform Controller Hub for Intel Core SoCs provides access
to Power Management Controller registers via various interfaces. This
@@ -1174,6 +1174,11 @@ static void pmc_core_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
mutex_destroy(&pmcdev->lock);
iounmap(pmcdev->regbase);
+
+ if (pmcdev->ssram_pcidev) {
+ pci_dev_put(pmcdev->ssram_pcidev);
+ pci_disable_device(pmcdev->ssram_pcidev);
+ }
}
static bool warn_on_s0ix_failures;
@@ -1287,3 +1292,4 @@ module_platform_driver(pmc_core_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel PMC Core Driver");
+MODULE_IMPORT_NS(INTEL_VSEC);
@@ -315,6 +315,7 @@ struct pmc_reg_map {
* @map: pointer to pmc_reg_map struct that contains platform
* specific attributes
* @pdev: pointer to platform_device struct
+ * @ssram_pcidev: pointer to pci device struct for the PMC SSRAM
* @dbgfs_dir: path to debugfs interface
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available
@@ -334,6 +335,7 @@ struct pmc_dev {
const struct pmc_reg_map *map;
struct dentry *dbgfs_dir;
struct platform_device *pdev;
+ struct pci_dev *ssram_pcidev;
int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */
@@ -8,7 +8,18 @@
*
*/
+#include <linux/pci.h>
+
#include "core.h"
+#include "../vsec.h"
+#include "../pmt/telemetry.h"
+
+#define SSRAM_HDR_SIZE 0x100
+#define SSRAM_PWRM_OFFSET 0x14
+#define SSRAM_DVSEC_OFFSET 0x1C
+#define SSRAM_DVSEC_SIZE 0x10
+#define SSRAM_PCH_OFFSET 0x60
+#define SSRAM_IOE_OFFSET 0x68
const struct pmc_reg_map mtl_reg_map = {
.pfear_sts = ext_tgl_pfear_map,
@@ -36,6 +47,99 @@ const struct pmc_reg_map mtl_reg_map = {
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
};
+static void
+mtl_pmc_add_pmt(struct pmc_dev *pmcdev, struct pci_dev *pdev, u64 ssram_base)
+{
+ struct intel_vsec_platform_info info = {};
+ struct intel_vsec_header *headers[2] = {};
+ struct intel_vsec_header header;
+ void __iomem *ssram, *dvsec;
+ u32 dvsec_offset;
+ u32 table, hdr;
+
+ ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
+ if (!ssram)
+ return;
+
+ dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET);
+ iounmap(ssram);
+
+ dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE);
+ if (!dvsec)
+ return;
+
+ hdr = readl(dvsec + PCI_DVSEC_HEADER1);
+ header.id = readw(dvsec + PCI_DVSEC_HEADER2);
+ header.rev = PCI_DVSEC_HEADER1_REV(hdr);
+ header.length = PCI_DVSEC_HEADER1_LEN(hdr);
+ header.num_entries = readb(dvsec + INTEL_DVSEC_ENTRIES);
+ header.entry_size = readb(dvsec + INTEL_DVSEC_SIZE);
+
+ table = readl(dvsec + INTEL_DVSEC_TABLE);
+ header.tbir = INTEL_DVSEC_TABLE_BAR(table);
+ header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
+ iounmap(dvsec);
+
+ headers[0] = &header;
+ info.caps = VSEC_CAP_TELEMETRY;
+ info.headers = headers;
+ info.base_addr = ssram_base;
+
+ intel_vsec_register(pdev, &info);
+}
+
+static inline u64 get_ssram_base(void __iomem *addr, u32 offset)
+{
+ u64 low, high;
+
+ low = readl(addr + offset) & GENMASK(31, 3);
+ high = readl(addr + offset + 4);
+
+ return (high << 32) + low;
+}
+
+static void mtl_pmc_ssram_init(struct pmc_dev *pmcdev)
+{
+ void __iomem *ssram;
+ struct pci_dev *pcidev;
+ u64 socs_ssram_base;
+ u64 ioe_ssram_base;
+ u64 pch_ssram_base;
+ int ret;
+
+ pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2));
+ if (!pcidev) {
+ dev_err(&pmcdev->pdev->dev, "pci_dev is not found.");
+ return;
+ }
+
+ ret = pcim_enable_device(pcidev);
+ if (ret) {
+ pci_dev_put(pcidev);
+ return;
+ }
+
+ socs_ssram_base = pcidev->resource[0].start;
+ ssram = ioremap(socs_ssram_base, SSRAM_HDR_SIZE);
+ if (!ssram) {
+ pci_dev_put(pcidev);
+ pci_disable_device(pcidev);
+ return;
+ }
+
+ pmcdev->ssram_pcidev = pcidev;
+
+ ioe_ssram_base = get_ssram_base(ssram, SSRAM_IOE_OFFSET);
+ pch_ssram_base = get_ssram_base(ssram, SSRAM_PCH_OFFSET);
+ iounmap(ssram);
+
+ mtl_pmc_add_pmt(pmcdev, pcidev, socs_ssram_base);
+ if (ioe_ssram_base)
+ mtl_pmc_add_pmt(pmcdev, pcidev, ioe_ssram_base);
+ if (pch_ssram_base)
+ mtl_pmc_add_pmt(pmcdev, pcidev, pch_ssram_base);
+}
+
int mtl_core_init(struct pmc_dev *pmcdev)
{
int ret;
@@ -45,6 +149,8 @@ int mtl_core_init(struct pmc_dev *pmcdev)
if (ret)
return ret;
+ mtl_pmc_ssram_init(pmcdev);
+
/* Due to a hardware limitation, the GBE LTR blocks PC10
* when a cable is attached. Tell the PMC to ignore it.
*/