diff options
Diffstat (limited to 'drivers/platform/x86/intel/pmc')
-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/arl.c | 16 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/cnp.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/core.c | 250 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/core.h | 22 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/core_ssram.c | 332 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/mtl.c | 10 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/ssram_telemetry.c | 204 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmc/ssram_telemetry.h | 24 |
10 files changed, 500 insertions, 377 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 b148b40d09f5..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 core_ssram.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/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 320993bd6d31..9d66d65e7577 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -10,7 +10,6 @@ #include <linux/pci.h> #include "core.h" -#include "../pmt/telemetry.h" /* PMC SSRAM PMT Telemetry GUID */ #define IOEP_LPM_REQ_GUID 0x5077612 @@ -651,29 +650,25 @@ static const struct pmc_reg_map arl_pchs_reg_map = { .etr3_offset = ETR3_OFFSET, }; -#define PMC_DEVID_SOCM 0x777f -#define PMC_DEVID_SOCS 0xae7f -#define PMC_DEVID_IOEP 0x7ecf -#define PMC_DEVID_PCHS 0x7f27 static struct pmc_info arl_pmc_info_list[] = { { .guid = IOEP_LPM_REQ_GUID, - .devid = PMC_DEVID_IOEP, + .devid = PMC_DEVID_ARL_IOEP, .map = &mtl_ioep_reg_map, }, { .guid = SOCS_LPM_REQ_GUID, - .devid = PMC_DEVID_SOCS, + .devid = PMC_DEVID_ARL_SOCS, .map = &arl_socs_reg_map, }, { .guid = PCHS_LPM_REQ_GUID, - .devid = PMC_DEVID_PCHS, + .devid = PMC_DEVID_ARL_PCHS, .map = &arl_pchs_reg_map, }, { .guid = SOCM_LPM_REQ_GUID, - .devid = PMC_DEVID_SOCM, + .devid = PMC_DEVID_ARL_SOCM, .map = &mtl_socm_reg_map, }, {} @@ -681,6 +676,7 @@ static struct pmc_info arl_pmc_info_list[] = { #define ARL_NPU_PCI_DEV 0xad1d #define ARL_GNA_PCI_DEV 0xae4c +#define ARL_H_NPU_PCI_DEV 0x7d1d #define ARL_H_GNA_PCI_DEV 0x774c /* * Set power state of select devices that do not have drivers to D3 @@ -694,7 +690,7 @@ static void arl_d3_fixup(void) static void arl_h_d3_fixup(void) { - pmc_core_set_device_d3(ARL_NPU_PCI_DEV); + pmc_core_set_device_d3(ARL_H_NPU_PCI_DEV); pmc_core_set_device_d3(ARL_H_GNA_PCI_DEV); } diff --git a/drivers/platform/x86/intel/pmc/cnp.c b/drivers/platform/x86/intel/pmc/cnp.c index 2c5af158bbe2..efea4e1ba52b 100644 --- a/drivers/platform/x86/intel/pmc/cnp.c +++ b/drivers/platform/x86/intel/pmc/cnp.c @@ -10,6 +10,7 @@ #include <linux/smp.h> #include <linux/suspend.h> +#include <asm/msr.h> #include "core.h" /* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */ @@ -227,10 +228,10 @@ static void disable_c1_auto_demote(void *unused) int cpunum = smp_processor_id(); u64 val; - rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, val); + rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, val); per_cpu(pkg_cst_config, cpunum) = val; val &= ~NHM_C1_AUTO_DEMOTE; - wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, val); + wrmsrq(MSR_PKG_CST_CONFIG_CONTROL, val); pr_debug("%s: cpu:%d cst %llx\n", __func__, cpunum, val); } @@ -239,7 +240,7 @@ static void restore_c1_auto_demote(void *unused) { int cpunum = smp_processor_id(); - wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, per_cpu(pkg_cst_config, cpunum)); + wrmsrq(MSR_PKG_CST_CONFIG_CONTROL, per_cpu(pkg_cst_config, cpunum)); pr_debug("%s: cpu:%d cst %llx\n", __func__, cpunum, per_cpu(pkg_cst_config, cpunum)); diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 7a1d11f2914f..540cd2fb0673 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -22,13 +22,14 @@ #include <linux/suspend.h> #include <linux/units.h> -#include <asm/cpuid.h> +#include <asm/cpuid/api.h> #include <asm/cpu_device_id.h> #include <asm/intel-family.h> #include <asm/msr.h> #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 */ @@ -1082,7 +1083,7 @@ static int pmc_core_pkgc_show(struct seq_file *s, void *unused) unsigned int index; for (index = 0; map[index].name ; index++) { - if (rdmsrl_safe(map[index].bit_mask, &pcstate_count)) + if (rdmsrq_safe(map[index].bit_mask, &pcstate_count)) continue; pcstate_count *= 1000; @@ -1345,6 +1346,198 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } +static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) +{ + for (; list->map; ++list) + if (list->map == map) + return list->guid; + + return 0; +} + +/* + * This function retrieves low power mode requirement data from PMC Low + * Power Mode (LPM) table. + * + * In telemetry space, the LPM table contains a 4 byte header followed + * by 8 consecutive mode blocks (one for each LPM mode). Each block + * has a 4 byte header followed by a set of registers that describe the + * IP state requirements for the given mode. The IP mapping is platform + * specific but the same for each block, making for easy analysis. + * Platforms only use a subset of the space to track the requirements + * for their IPs. Callers provide the requirement registers they use as + * a list of indices. Each requirement register is associated with an + * IP map that's maintained by the caller. + * + * Header + * +----+----------------------------+----------------------------+ + * | 0 | REVISION | ENABLED MODES | + * +----+--------------+-------------+-------------+--------------+ + * + * Low Power Mode 0 Block + * +----+--------------+-------------+-------------+--------------+ + * | 1 | SUB ID | SIZE | MAJOR | MINOR | + * +----+--------------+-------------+-------------+--------------+ + * | 2 | LPM0 Requirements 0 | + * +----+---------------------------------------------------------+ + * | | ... | + * +----+---------------------------------------------------------+ + * | 29 | LPM0 Requirements 27 | + * +----+---------------------------------------------------------+ + * + * ... + * + * Low Power Mode 7 Block + * +----+--------------+-------------+-------------+--------------+ + * | | SUB ID | SIZE | MAJOR | MINOR | + * +----+--------------+-------------+-------------+--------------+ + * | 60 | LPM7 Requirements 0 | + * +----+---------------------------------------------------------+ + * | | ... | + * +----+---------------------------------------------------------+ + * | 87 | LPM7 Requirements 27 | + * +----+---------------------------------------------------------+ + * + */ +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; + int num_maps, mode_offset = 0; + int ret, mode; + int lpm_size; + u32 guid; + + lpm_indices = pmc->map->lpm_reg_index; + num_maps = pmc->map->lpm_num_maps; + lpm_size = LPM_MAX_NUM_MODES * num_maps; + + guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); + if (!guid) + return -ENXIO; + + ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); + if (IS_ERR(ep)) { + dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); + return -EPROBE_DEFER; + } + + pmc->lpm_req_regs = devm_kzalloc(&pmcdev->pdev->dev, + lpm_size * sizeof(u32), + GFP_KERNEL); + if (!pmc->lpm_req_regs) { + ret = -ENOMEM; + goto unregister_ep; + } + + mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET; + pmc_for_each_mode(mode, pmcdev) { + u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps); + int m; + + for (m = 0; m < num_maps; m++) { + u8 sample_id = lpm_indices[m] + mode_offset; + + ret = pmt_telem_read32(ep, sample_id, req_offset, 1); + if (ret) { + dev_err(&pmcdev->pdev->dev, + "couldn't read Low Power Mode requirements: %d\n", ret); + goto unregister_ep; + } + ++req_offset; + } + mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET; + } + +unregister_ep: + pmt_telem_unregister_endpoint(ep); + + return ret; +} + +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; + + 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], pcidev); + if (ret) + return ret; + } + + return 0; +} + +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 int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int 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; + + map = pmc_core_find_regmap(pmcdev->regmap_list, pmc_ssram_telemetry.devid); + if (!map) + return -ENODEV; + + 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 = map; + pmc->base_addr = pmc_ssram_telemetry.base_addr; + pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length); + + if (!pmc->regbase) { + devm_kfree(&pmcdev->pdev->dev, pmc); + return -ENOMEM; + } + + pmcdev->pmcs[pmc_index] = pmc; + + 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 @@ -1362,10 +1555,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; } } @@ -1381,10 +1582,26 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) if (pmc_dev_info->dmu_guid) pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid); - if (ssram) - return pmc_core_ssram_get_lpm_reqs(pmcdev); + if (ssram) { + ret = pmc_core_ssram_get_lpm_reqs(pmcdev, pmc_dev_info->pci_func); + if (ret) + goto unmap_regbase; + } return 0; + +unmap_regbase: + for (unsigned int i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { + struct pmc *pmc = pmcdev->pmcs[i]; + + if (pmc && pmc->regbase) + iounmap(pmc->regbase); + } + + if (pmcdev->punit_ep) + pmt_telem_unregister_endpoint(pmcdev->punit_ep); + + return ret; } static const struct x86_cpu_id intel_pmc_core_ids[] = { @@ -1471,20 +1688,14 @@ 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); platform_set_drvdata(pdev, NULL); - mutex_destroy(&pmcdev->lock); } static int pmc_core_probe(struct platform_device *pdev) @@ -1529,7 +1740,9 @@ static int pmc_core_probe(struct platform_device *pdev) if (!pmcdev->pkgc_res_cnt) return -ENOMEM; - mutex_init(&pmcdev->lock); + ret = devm_mutex_init(&pdev->dev, &pmcdev->lock); + if (ret) + return ret; if (pmc_dev_info->init) ret = pmc_dev_info->init(pmcdev, pmc_dev_info); @@ -1537,7 +1750,7 @@ static int pmc_core_probe(struct platform_device *pdev) ret = generic_core_init(pmcdev, pmc_dev_info); if (ret) { - pmc_core_clean_structure(pdev); + platform_set_drvdata(pdev, NULL); return ret; } @@ -1587,7 +1800,7 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) /* Save PKGC residency for checking later */ for (i = 0; i < pmcdev->num_of_pkgc; i++) { - if (rdmsrl_safe(msr_map[i].bit_mask, &pmcdev->pkgc_res_cnt[i])) + if (rdmsrq_safe(msr_map[i].bit_mask, &pmcdev->pkgc_res_cnt[i])) return -EIO; } @@ -1603,7 +1816,7 @@ static inline bool pmc_core_is_deepest_pkgc_failed(struct pmc_dev *pmcdev) u32 deepest_pkgc_msr = msr_map[pmcdev->num_of_pkgc - 1].bit_mask; u64 deepest_pkgc_residency; - if (rdmsrl_safe(deepest_pkgc_msr, &deepest_pkgc_residency)) + if (rdmsrq_safe(deepest_pkgc_msr, &deepest_pkgc_residency)) return false; if (deepest_pkgc_residency == pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1]) @@ -1655,7 +1868,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) for (i = 0; i < pmcdev->num_of_pkgc; i++) { u64 pc_cnt; - if (!rdmsrl_safe(msr_map[i].bit_mask, &pc_cnt)) { + if (!rdmsrq_safe(msr_map[i].bit_mask, &pc_cnt)) { dev_info(dev, "Prev %s cnt = 0x%llx, Current %s cnt = 0x%llx\n", msr_map[i].name, pmcdev->pkgc_res_cnt[i], msr_map[i].name, pc_cnt); @@ -1719,5 +1932,6 @@ static struct platform_driver pmc_core_driver = { module_platform_driver(pmc_core_driver); +MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel PMC Core Driver"); diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 945a1c440cca..e136d18b1d38 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -24,6 +24,11 @@ struct telem_endpoint; #define MAX_NUM_PMC 3 #define S0IX_BLK_SIZE 4 +/* PCH query */ +#define LPM_HEADER_OFFSET 1 +#define LPM_REG_COUNT 28 +#define LPM_MODE_OFFSET 1 + /* Sunrise Point Power Management Controller PCI Device ID */ #define SPT_PMC_PCI_DEVICE_ID 0x9d21 #define SPT_PMC_BASE_ADDR_OFFSET 0x48 @@ -293,6 +298,18 @@ enum ppfear_regs { #define PTL_PMC_LTR_CUR_PLT 0x1C2C #define PTL_PCD_PMC_MMIO_REG_LEN 0x31A8 +/* SSRAM PMC Device ID */ +/* ARL */ +#define PMC_DEVID_ARL_SOCM 0x777f +#define PMC_DEVID_ARL_SOCS 0xae7f +#define PMC_DEVID_ARL_IOEP 0x7ecf +#define PMC_DEVID_ARL_PCHS 0x7f27 + +/* MTL */ +#define PMC_DEVID_MTL_SOCM 0x7e7f +#define PMC_DEVID_MTL_IOEP 0x7ecf +#define PMC_DEVID_MTL_IOEM 0x7ebf + extern const char *pmc_lpm_modes[]; struct pmc_bit_map { @@ -396,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 @@ -416,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 */ @@ -485,7 +500,6 @@ extern const struct pmc_reg_map mtl_socm_reg_map; extern const struct pmc_reg_map mtl_ioep_reg_map; void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); -int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev); int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); int pmc_core_resume_common(struct pmc_dev *pmcdev); @@ -494,8 +508,6 @@ 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); extern struct pmc_dev_info spt_pmc_dev; diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c deleted file mode 100644 index 739569803017..000000000000 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ /dev/null @@ -1,332 +0,0 @@ -// 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/cleanup.h> -#include <linux/intel_vsec.h> -#include <linux/pci.h> -#include <linux/io-64-nonatomic-lo-hi.h> - -#include "core.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 -#define SSRAM_DEVID_OFFSET 0x70 - -/* PCH query */ -#define LPM_HEADER_OFFSET 1 -#define LPM_REG_COUNT 28 -#define LPM_MODE_OFFSET 1 - -DEFINE_FREE(pmc_core_iounmap, void __iomem *, if (_T) iounmap(_T)) - -static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) -{ - for (; list->map; ++list) - if (list->map == map) - return list->guid; - - return 0; -} - -static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) -{ - struct telem_endpoint *ep; - const u8 *lpm_indices; - int num_maps, mode_offset = 0; - int ret, mode; - int lpm_size; - u32 guid; - - lpm_indices = pmc->map->lpm_reg_index; - num_maps = pmc->map->lpm_num_maps; - lpm_size = LPM_MAX_NUM_MODES * num_maps; - - guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); - if (!guid) - return -ENXIO; - - ep = pmt_telem_find_and_register_endpoint(pmcdev->ssram_pcidev, guid, 0); - if (IS_ERR(ep)) { - dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %ld", - PTR_ERR(ep)); - return -EPROBE_DEFER; - } - - pmc->lpm_req_regs = devm_kzalloc(&pmcdev->pdev->dev, - lpm_size * sizeof(u32), - GFP_KERNEL); - if (!pmc->lpm_req_regs) { - ret = -ENOMEM; - goto unregister_ep; - } - - /* - * PMC Low Power Mode (LPM) table - * - * In telemetry space, the LPM table contains a 4 byte header followed - * by 8 consecutive mode blocks (one for each LPM mode). Each block - * has a 4 byte header followed by a set of registers that describe the - * IP state requirements for the given mode. The IP mapping is platform - * specific but the same for each block, making for easy analysis. - * Platforms only use a subset of the space to track the requirements - * for their IPs. Callers provide the requirement registers they use as - * a list of indices. Each requirement register is associated with an - * IP map that's maintained by the caller. - * - * Header - * +----+----------------------------+----------------------------+ - * | 0 | REVISION | ENABLED MODES | - * +----+--------------+-------------+-------------+--------------+ - * - * Low Power Mode 0 Block - * +----+--------------+-------------+-------------+--------------+ - * | 1 | SUB ID | SIZE | MAJOR | MINOR | - * +----+--------------+-------------+-------------+--------------+ - * | 2 | LPM0 Requirements 0 | - * +----+---------------------------------------------------------+ - * | | ... | - * +----+---------------------------------------------------------+ - * | 29 | LPM0 Requirements 27 | - * +----+---------------------------------------------------------+ - * - * ... - * - * Low Power Mode 7 Block - * +----+--------------+-------------+-------------+--------------+ - * | | SUB ID | SIZE | MAJOR | MINOR | - * +----+--------------+-------------+-------------+--------------+ - * | 60 | LPM7 Requirements 0 | - * +----+---------------------------------------------------------+ - * | | ... | - * +----+---------------------------------------------------------+ - * | 87 | LPM7 Requirements 27 | - * +----+---------------------------------------------------------+ - * - */ - mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET; - pmc_for_each_mode(mode, pmcdev) { - u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps); - int m; - - for (m = 0; m < num_maps; m++) { - u8 sample_id = lpm_indices[m] + mode_offset; - - ret = pmt_telem_read32(ep, sample_id, req_offset, 1); - if (ret) { - dev_err(&pmcdev->pdev->dev, - "couldn't read Low Power Mode requirements: %d\n", ret); - devm_kfree(&pmcdev->pdev->dev, pmc->lpm_req_regs); - goto unregister_ep; - } - ++req_offset; - } - mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET; - } - -unregister_ep: - pmt_telem_unregister_endpoint(ep); - - return ret; -} - -int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev) -{ - int ret, i; - - if (!pmcdev->ssram_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]); - if (ret) - return ret; - } - - return 0; -} - -static void -pmc_add_pmt(struct pmc_dev *pmcdev, 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; - void __iomem *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; - info.parent = &pmcdev->pdev->dev; - - intel_vsec_register(pcidev, &info); -} - -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 int -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 -ENODEV; - - /* 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 -ENOMEM; - } - - 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 -ENOMEM; - } - - pmcdev->pmcs[pmc_index] = pmc; - - return 0; -} - -static int -pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, 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; - u64 ssram_base, pwrm_base; - u16 devid; - - if (!pmcdev->regmap_list) - return -ENOENT; - - ssram_base = ssram_pcidev->resource[0].start; - tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); - if (!tmp_ssram) - return -ENOMEM; - - if (pmc_idx != PMC_IDX_MAIN) { - /* - * The secondary PMC BARS (which are behind hidden PCI devices) - * are read from fixed offsets in MMIO of the primary PMC BAR. - * If a device is not present, the value will be 0. - */ - ssram_base = get_base(tmp_ssram, offset); - if (!ssram_base) - return 0; - - ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); - if (!ssram) - return -ENOMEM; - - } else { - ssram = no_free_ptr(tmp_ssram); - } - - pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); - devid = readw(ssram + SSRAM_DEVID_OFFSET); - - /* Find and register and PMC telemetry entries */ - pmc_add_pmt(pmcdev, ssram_base, ssram); - - map = pmc_core_find_regmap(pmcdev->regmap_list, devid); - if (!map) - return -ENODEV; - - return pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx); -} - -int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func) -{ - struct pci_dev *pcidev; - int ret; - - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func)); - if (!pcidev) - return -ENODEV; - - ret = pcim_enable_device(pcidev); - if (ret) - goto release_dev; - - pmcdev->ssram_pcidev = pcidev; - - ret = pmc_core_ssram_get_pmc(pmcdev, 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); - - return ret; -} -MODULE_IMPORT_NS("INTEL_VSEC"); -MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY"); diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 8862829694a7..faa13a7ee688 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -10,7 +10,6 @@ #include <linux/pci.h> #include "core.h" -#include "../pmt/telemetry.h" /* PMC SSRAM PMT Telemetry GUIDS */ #define SOCP_LPM_REQ_GUID 0x2625030 @@ -947,23 +946,20 @@ static const struct pmc_reg_map mtl_ioem_reg_map = { .lpm_reg_index = MTL_LPM_REG_INDEX, }; -#define PMC_DEVID_SOCM 0x7e7f -#define PMC_DEVID_IOEP 0x7ecf -#define PMC_DEVID_IOEM 0x7ebf static struct pmc_info mtl_pmc_info_list[] = { { .guid = SOCP_LPM_REQ_GUID, - .devid = PMC_DEVID_SOCM, + .devid = PMC_DEVID_MTL_SOCM, .map = &mtl_socm_reg_map, }, { .guid = IOEP_LPM_REQ_GUID, - .devid = PMC_DEVID_IOEP, + .devid = PMC_DEVID_MTL_IOEP, .map = &mtl_ioep_reg_map, }, { .guid = IOEM_LPM_REQ_GUID, - .devid = PMC_DEVID_IOEM, + .devid = PMC_DEVID_MTL_IOEM, .map = &mtl_ioem_reg_map }, {} diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c new file mode 100644 index 000000000000..b207247eb5dd --- /dev/null +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel PMC SSRAM TELEMETRY PCI Driver + * + * Copyright (c) 2023, Intel Corporation. + */ + +#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 +#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 + +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 int +pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram) +{ + struct intel_vsec_platform_info info = {}; + struct intel_vsec_header *headers[2] = {}; + struct intel_vsec_header header; + void __iomem *dvsec; + u32 dvsec_offset; + u32 table, hdr; + + dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET); + dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE); + if (!dvsec) + return -ENOMEM; + + 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; + info.parent = &pcidev->dev; + + return intel_vsec_register(pcidev, &info); +} + +static inline u64 get_base(void __iomem *addr, u32 offset) +{ + return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3); +} + +static int +pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) +{ + 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; + + ssram_base = pci_resource_start(pcidev, 0); + tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); + if (!tmp_ssram) + return -ENOMEM; + + if (pmc_idx != PMC_IDX_MAIN) { + /* + * The secondary PMC BARS (which are behind hidden PCI devices) + * are read from fixed offsets in MMIO of the primary PMC BAR. + * If a device is not present, the value will be 0. + */ + ssram_base = get_base(tmp_ssram, offset); + if (!ssram_base) + return 0; + + ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); + if (!ssram) + return -ENOMEM; + + } else { + ssram = no_free_ptr(tmp_ssram); + } + + 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 */ + return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); +} + +/** + * 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; + + 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); + +static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) +{ + int ret; + + 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) { + dev_dbg(&pcidev->dev, "failed to enable PMC SSRAM device\n"); + goto probe_finish; + } + + ret = pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_MAIN, 0); + if (ret) + 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 */ |