summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel/pmc/core_ssram.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/intel/pmc/core_ssram.c')
-rw-r--r--drivers/platform/x86/intel/pmc/core_ssram.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c
new file mode 100644
index 000000000000..13fa16f0d52e
--- /dev/null
+++ b/drivers/platform/x86/intel/pmc/core_ssram.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains functions to handle discovery of PMC metrics located
+ * in the PMC SSRAM PCI device.
+ *
+ * Copyright (c) 2023, Intel Corporation.
+ * All Rights Reserved.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+#include "core.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
+#define SSRAM_DEVID_OFFSET 0x70
+
+static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid)
+{
+ for (; list->map; ++list)
+ if (devid == list->devid)
+ return list->map;
+
+ return NULL;
+}
+
+static inline u64 get_base(void __iomem *addr, u32 offset)
+{
+ return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3);
+}
+
+static void
+pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base,
+ const struct pmc_reg_map *reg_map, int pmc_index)
+{
+ struct pmc *pmc = pmcdev->pmcs[pmc_index];
+
+ if (!pwrm_base)
+ return;
+
+ /* Memory for primary PMC has been allocated in core.c */
+ if (!pmc) {
+ pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL);
+ if (!pmc)
+ return;
+ }
+
+ pmc->map = reg_map;
+ pmc->base_addr = pwrm_base;
+ pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length);
+
+ if (!pmc->regbase) {
+ devm_kfree(&pmcdev->pdev->dev, pmc);
+ return;
+ }
+
+ pmcdev->pmcs[pmc_index] = pmc;
+}
+
+static void
+pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, void __iomem *ssram, u32 offset,
+ int pmc_idx)
+{
+ u64 pwrm_base;
+ u16 devid;
+
+ if (pmc_idx != PMC_IDX_SOC) {
+ u64 ssram_base = get_base(ssram, offset);
+
+ if (!ssram_base)
+ return;
+
+ ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
+ if (!ssram)
+ return;
+ }
+
+ pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
+ devid = readw(ssram + SSRAM_DEVID_OFFSET);
+
+ if (pmcdev->regmap_list) {
+ const struct pmc_reg_map *map;
+
+ map = pmc_core_find_regmap(pmcdev->regmap_list, devid);
+ if (map)
+ pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx);
+ }
+
+ if (pmc_idx != PMC_IDX_SOC)
+ iounmap(ssram);
+}
+
+void pmc_core_ssram_init(struct pmc_dev *pmcdev)
+{
+ void __iomem *ssram;
+ struct pci_dev *pcidev;
+ u64 ssram_base;
+ int ret;
+
+ pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2));
+ if (!pcidev)
+ goto out;
+
+ ret = pcim_enable_device(pcidev);
+ if (ret)
+ goto release_dev;
+
+ ssram_base = pcidev->resource[0].start;
+ ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
+ if (!ssram)
+ goto disable_dev;
+
+ pmcdev->ssram_pcidev = pcidev;
+
+ pmc_core_ssram_get_pmc(pmcdev, ssram, 0, PMC_IDX_SOC);
+ pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_IOE_OFFSET, PMC_IDX_IOE);
+ pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_PCH_OFFSET, PMC_IDX_PCH);
+
+ iounmap(ssram);
+out:
+ return;
+
+disable_dev:
+ pci_disable_device(pcidev);
+release_dev:
+ pci_dev_put(pcidev);
+}