diff options
Diffstat (limited to 'drivers/platform/x86/intel/pmc/core_ssram.c')
-rw-r--r-- | drivers/platform/x86/intel/pmc/core_ssram.c | 133 |
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 0000000000..13fa16f0d5 --- /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); +} |