diff options
-rw-r--r-- | drivers/platform/x86/intel/pmc/Kconfig | 4 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/Makefile | 8 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/core.c | 73 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/core.h | 7 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/ssram_telemetry.c | 147 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/ssram_telemetry.h | 24 |
6 files changed, 185 insertions, 78 deletions
diff --git a/drivers/platform/x86/intel/pmc/Kconfig b/drivers/platform/x86/intel/pmc/Kconfig index d2f651fbec2c..c6ef0bcf76af 100644 --- a/drivers/platform/x86/intel/pmc/Kconfig +++ b/drivers/platform/x86/intel/pmc/Kconfig @@ -8,6 +8,7 @@ config INTEL_PMC_CORE depends on PCI depends on ACPI depends on INTEL_PMT_TELEMETRY + select INTEL_PMC_SSRAM_TELEMETRY help The Intel Platform Controller Hub for Intel Core SoCs provides access to Power Management Controller registers via various interfaces. This @@ -24,3 +25,6 @@ config INTEL_PMC_CORE - SLPS0 Debug registers (Cannonlake/Icelake PCH) - Low Power Mode registers (Tigerlake and beyond) - PMC quirks as needed to enable SLPS0/S0ix + +config INTEL_PMC_SSRAM_TELEMETRY + tristate diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index e842647d3ced..5f68c8503a56 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -3,8 +3,12 @@ # Intel x86 Platform-Specific Drivers # -intel_pmc_core-y := core.o ssram_telemetry.o spt.o cnp.o \ - icl.o tgl.o adl.o mtl.o arl.o lnl.o ptl.o +intel_pmc_core-y := core.o spt.o cnp.o icl.o \ + tgl.o adl.o mtl.o arl.o lnl.o ptl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o + +# Intel PMC SSRAM driver +intel_pmc_ssram_telemetry-y += ssram_telemetry.o +obj-$(CONFIG_INTEL_PMC_SSRAM_TELEMETRY) += intel_pmc_ssram_telemetry.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index a53a7677122c..2028a769cddb 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -29,6 +29,7 @@ #include <asm/tsc.h> #include "core.h" +#include "ssram_telemetry.h" #include "../pmt/telemetry.h" /* Maximum number of modes supported by platfoms that has low power mode capability */ @@ -1354,7 +1355,7 @@ static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *m return 0; } -static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) +static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct pci_dev *pcidev) { struct telem_endpoint *ep; const u8 *lpm_indices; @@ -1371,7 +1372,7 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) if (!guid) return -ENXIO; - ep = pmt_telem_find_and_register_endpoint(pmcdev->ssram_pcidev, guid, 0); + ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); if (IS_ERR(ep)) { dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %ld", PTR_ERR(ep)); @@ -1455,19 +1456,21 @@ unregister_ep: return ret; } -static int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev) +static int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev, int func) { + struct pci_dev *pcidev __free(pci_dev_put) = NULL; unsigned int i; int ret; - if (!pmcdev->ssram_pcidev) + pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func)); + if (!pcidev) return -ENODEV; for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { if (!pmcdev->pmcs[i]) continue; - ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i]); + ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i], pcidev); if (ret) return ret; } @@ -1475,7 +1478,7 @@ static int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev) return 0; } -const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid) +static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid) { for (; list->map; ++list) if (devid == list->devid) @@ -1484,23 +1487,32 @@ const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid) return NULL; } -int pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base, - const struct pmc_reg_map *reg_map, unsigned int pmc_index) +static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + { - struct pmc *pmc = pmcdev->pmcs[pmc_index]; + struct pmc_ssram_telemetry pmc_ssram_telemetry; + const struct pmc_reg_map *map; + struct pmc *pmc; + int ret; + + ret = pmc_ssram_telemetry_get_pmc_info(pmc_index, &pmc_ssram_telemetry); + if (ret) + return ret; - if (!pwrm_base) + map = pmc_core_find_regmap(pmcdev->regmap_list, pmc_ssram_telemetry.devid); + if (!map) return -ENODEV; - /* Memory for primary PMC has been allocated in core.c */ + pmc = pmcdev->pmcs[pmc_index]; + /* Memory for primary PMC has been allocated */ if (!pmc) { pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL); if (!pmc) return -ENOMEM; } - pmc->map = reg_map; - pmc->base_addr = pwrm_base; + pmc->map = map; + pmc->base_addr = pmc_ssram_telemetry.base_addr; pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length); if (!pmc->regbase) { @@ -1513,6 +1525,20 @@ int pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base, return 0; } +static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev) +{ + int ret; + + ret = pmc_core_pmc_add(pmcdev, PMC_IDX_MAIN); + if (ret) + return ret; + + pmc_core_pmc_add(pmcdev, PMC_IDX_IOE); + pmc_core_pmc_add(pmcdev, PMC_IDX_PCH); + + return 0; +} + /* * When supported, ssram init is used to achieve all available PMCs. * If ssram init fails, this function uses legacy method to at least get the @@ -1530,10 +1556,18 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) ssram = pmc_dev_info->regmap_list != NULL; if (ssram) { pmcdev->regmap_list = pmc_dev_info->regmap_list; - ret = pmc_core_ssram_init(pmcdev, pmc_dev_info->pci_func); + ret = pmc_core_ssram_get_reg_base(pmcdev); + /* + * EAGAIN error code indicates Intel PMC SSRAM Telemetry driver + * has not finished probe and PMC info is not available yet. Try + * again later. + */ + if (ret == -EAGAIN) + return -EPROBE_DEFER; + if (ret) { dev_warn(&pmcdev->pdev->dev, - "ssram init failed, %d, using legacy init\n", ret); + "Failed to get PMC info from SSRAM, %d, using legacy init\n", ret); ssram = false; } } @@ -1550,7 +1584,7 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid); if (ssram) - return pmc_core_ssram_get_lpm_reqs(pmcdev); + return pmc_core_ssram_get_lpm_reqs(pmcdev, pmc_dev_info->pci_func); return 0; } @@ -1639,15 +1673,10 @@ static void pmc_core_clean_structure(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { struct pmc *pmc = pmcdev->pmcs[i]; - if (pmc) + if (pmc && pmc->regbase) iounmap(pmc->regbase); } - if (pmcdev->ssram_pcidev) { - pci_dev_put(pmcdev->ssram_pcidev); - pci_disable_device(pmcdev->ssram_pcidev); - } - if (pmcdev->punit_ep) pmt_telem_unregister_endpoint(pmcdev->punit_ep); diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index c3b07075d017..e136d18b1d38 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -413,7 +413,6 @@ struct pmc { * struct pmc_dev - pmc device structure * @devs: pointer to an array of pmc pointers * @pdev: pointer to platform_device struct - * @ssram_pcidev: pointer to pci device struct for the PMC SSRAM * @crystal_freq: crystal frequency from cpuid * @dbgfs_dir: path to debugfs interface * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers @@ -433,7 +432,6 @@ struct pmc_dev { struct pmc *pmcs[MAX_NUM_PMC]; struct dentry *dbgfs_dir; struct platform_device *pdev; - struct pci_dev *ssram_pcidev; unsigned int crystal_freq; int pmc_xram_read_bit; struct mutex lock; /* generic mutex lock for PMC Core */ @@ -510,12 +508,7 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid); void pmc_core_set_device_d3(unsigned int device); -int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func); - int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); -const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid); -int pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base, - const struct pmc_reg_map *reg_map, unsigned int pmc_index); extern struct pmc_dev_info spt_pmc_dev; extern struct pmc_dev_info cnp_pmc_dev; diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 7b8443092b20..b207247eb5dd 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -1,19 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 /* - * This file contains functions to handle discovery of PMC metrics located - * in the PMC SSRAM PCI device. + * Intel PMC SSRAM TELEMETRY PCI Driver * * Copyright (c) 2023, Intel Corporation. - * All Rights Reserved. - * */ #include <linux/cleanup.h> #include <linux/intel_vsec.h> #include <linux/pci.h> +#include <linux/types.h> #include <linux/io-64-nonatomic-lo-hi.h> #include "core.h" +#include "ssram_telemetry.h" #define SSRAM_HDR_SIZE 0x100 #define SSRAM_PWRM_OFFSET 0x14 @@ -23,12 +22,14 @@ #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 -DEFINE_FREE(pmc_core_iounmap, void __iomem *, if (_T) iounmap(_T)) +DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) + +static struct pmc_ssram_telemetry *pmc_ssram_telems; +static bool device_probed; -static void -pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram) +static int +pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram) { - struct pci_dev *pcidev = pmcdev->ssram_pcidev; struct intel_vsec_platform_info info = {}; struct intel_vsec_header *headers[2] = {}; struct intel_vsec_header header; @@ -39,7 +40,7 @@ pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram) dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET); dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE); if (!dvsec) - return; + return -ENOMEM; hdr = readl(dvsec + PCI_DVSEC_HEADER1); header.id = readw(dvsec + PCI_DVSEC_HEADER2); @@ -57,9 +58,9 @@ pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram) info.caps = VSEC_CAP_TELEMETRY; info.headers = headers; info.base_addr = ssram_base; - info.parent = &pmcdev->pdev->dev; + info.parent = &pcidev->dev; - intel_vsec_register(pcidev, &info); + return intel_vsec_register(pcidev, &info); } static inline u64 get_base(void __iomem *addr, u32 offset) @@ -68,19 +69,14 @@ static inline u64 get_base(void __iomem *addr, u32 offset) } static int -pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, unsigned int pmc_idx, u32 offset) +pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) { - struct pci_dev *ssram_pcidev = pmcdev->ssram_pcidev; - void __iomem __free(pmc_core_iounmap) *tmp_ssram = NULL; - void __iomem __free(pmc_core_iounmap) *ssram = NULL; - const struct pmc_reg_map *map; + void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; + void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; u64 ssram_base, pwrm_base; u16 devid; - if (!pmcdev->regmap_list) - return -ENOENT; - - ssram_base = ssram_pcidev->resource[0].start; + ssram_base = pci_resource_start(pcidev, 0); tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); if (!tmp_ssram) return -ENOMEM; @@ -106,46 +102,103 @@ pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, unsigned int pmc_idx, u32 offset) pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); devid = readw(ssram + SSRAM_DEVID_OFFSET); + pmc_ssram_telems[pmc_idx].devid = devid; + pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; + /* Find and register and PMC telemetry entries */ - pmc_add_pmt(pmcdev, ssram_base, ssram); + return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); +} - map = pmc_core_find_regmap(pmcdev->regmap_list, devid); - if (!map) +/** + * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information + * @pmc_idx: Index of the PMC + * @pmc_ssram_telemetry: pmc_ssram_telemetry structure to store the PMC information + * + * Return: + * * 0 - Success + * * -EAGAIN - Probe function has not finished yet. Try again. + * * -EINVAL - Invalid pmc_idx + * * -ENODEV - PMC device is not available + */ +int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, + struct pmc_ssram_telemetry *pmc_ssram_telemetry) +{ + /* + * PMCs are discovered in probe function. If this function is called before + * probe function complete, the result would be invalid. Use device_probed + * variable to avoid this case. Return -EAGAIN to inform the consumer to call + * again later. + */ + if (!device_probed) + return -EAGAIN; + + /* + * Memory barrier is used to ensure the correct read order between + * device_probed variable and PMC info. + */ + smp_rmb(); + if (pmc_idx >= MAX_NUM_PMC) + return -EINVAL; + + if (!pmc_ssram_telems || !pmc_ssram_telems[pmc_idx].devid) return -ENODEV; - return pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx); + pmc_ssram_telemetry->devid = pmc_ssram_telems[pmc_idx].devid; + pmc_ssram_telemetry->base_addr = pmc_ssram_telems[pmc_idx].base_addr; + return 0; } +EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); -int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func) +static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { - struct pci_dev *pcidev; int ret; - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func)); - if (!pcidev) - return -ENODEV; + pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC, + GFP_KERNEL); + if (!pmc_ssram_telems) { + ret = -ENOMEM; + goto probe_finish; + } ret = pcim_enable_device(pcidev); - if (ret) - goto release_dev; - - pmcdev->ssram_pcidev = pcidev; + if (ret) { + dev_dbg(&pcidev->dev, "failed to enable PMC SSRAM device\n"); + goto probe_finish; + } - ret = pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_MAIN, 0); + ret = pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_MAIN, 0); if (ret) - goto disable_dev; - - pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); - pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); - - return 0; - -disable_dev: - pmcdev->ssram_pcidev = NULL; - pci_disable_device(pcidev); -release_dev: - pci_dev_put(pcidev); - + goto probe_finish; + + pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); + pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + +probe_finish: + /* + * Memory barrier is used to ensure the correct write order between PMC info + * and device_probed variable. + */ + smp_wmb(); + device_probed = true; return ret; } + +static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) }, + { } +}; +MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids); + +static struct pci_driver intel_pmc_ssram_telemetry_driver = { + .name = "intel_pmc_ssram_telemetry", + .id_table = intel_pmc_ssram_telemetry_pci_ids, + .probe = intel_pmc_ssram_telemetry_probe, +}; +module_pci_driver(intel_pmc_ssram_telemetry_driver); + MODULE_IMPORT_NS("INTEL_VSEC"); +MODULE_AUTHOR("Xi Pardee <xi.pardee@intel.com>"); +MODULE_DESCRIPTION("Intel PMC SSRAM Telemetry driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.h b/drivers/platform/x86/intel/pmc/ssram_telemetry.h new file mode 100644 index 000000000000..daf8aeeb2275 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Intel PMC SSRAM Telemetry PCI Driver Header File + * + * Copyright (c) 2024, Intel Corporation. + */ + +#ifndef PMC_SSRAM_H +#define PMC_SSRAM_H + +/** + * struct pmc_ssram_telemetry - Structure to keep pmc info in ssram device + * @devid: device id of the pmc device + * @base_addr: contains PWRM base address + */ +struct pmc_ssram_telemetry { + u16 devid; + u64 base_addr; +}; + +int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, + struct pmc_ssram_telemetry *pmc_ssram_telemetry); + +#endif /* PMC_SSRAM_H */ |