Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86/intel/pmc: Retrieve LPM information using Intel PMT

On supported platforms, the low power mode (LPM) requirements for entering
each idle substate are described in Platform Monitoring Technology (PMT)
telemetry entries. Provide a function for platform code to attempt to find
and read the requirements from the telemetry entries.

Signed-off-by: Xi Pardee <xi.pardee@intel.com>
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20231129222132.2331261-18-david.e.box@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Xi Pardee and committed by
Hans de Goede
4d621c3f 0f601dec

+138
+3
drivers/platform/x86/intel/pmc/core.h
··· 320 320 const u32 lpm_status_offset; 321 321 const u32 lpm_live_status_offset; 322 322 const u32 etr3_offset; 323 + const u8 *lpm_reg_index; 323 324 }; 324 325 325 326 /** ··· 330 329 * specific attributes 331 330 */ 332 331 struct pmc_info { 332 + u32 guid; 333 333 u16 devid; 334 334 const struct pmc_reg_map *map; 335 335 }; ··· 488 486 extern const struct pmc_reg_map mtl_ioem_reg_map; 489 487 490 488 extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); 489 + extern int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev); 491 490 extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); 492 491 493 492 int pmc_core_resume_common(struct pmc_dev *pmcdev);
+135
drivers/platform/x86/intel/pmc/core_ssram.c
··· 24 24 #define SSRAM_IOE_OFFSET 0x68 25 25 #define SSRAM_DEVID_OFFSET 0x70 26 26 27 + /* PCH query */ 28 + #define LPM_HEADER_OFFSET 1 29 + #define LPM_REG_COUNT 28 30 + #define LPM_MODE_OFFSET 1 31 + 27 32 DEFINE_FREE(pmc_core_iounmap, void __iomem *, iounmap(_T)); 33 + 34 + static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) 35 + { 36 + for (; list->map; ++list) 37 + if (list->map == map) 38 + return list->guid; 39 + 40 + return 0; 41 + } 42 + 43 + static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) 44 + { 45 + struct telem_endpoint *ep; 46 + const u8 *lpm_indices; 47 + int num_maps, mode_offset = 0; 48 + int ret, mode, i; 49 + int lpm_size; 50 + u32 guid; 51 + 52 + lpm_indices = pmc->map->lpm_reg_index; 53 + num_maps = pmc->map->lpm_num_maps; 54 + lpm_size = LPM_MAX_NUM_MODES * num_maps; 55 + 56 + guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); 57 + if (!guid) 58 + return -ENXIO; 59 + 60 + ep = pmt_telem_find_and_register_endpoint(pmcdev->ssram_pcidev, guid, 0); 61 + if (IS_ERR(ep)) { 62 + dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %ld", 63 + PTR_ERR(ep)); 64 + return -EPROBE_DEFER; 65 + } 66 + 67 + pmc->lpm_req_regs = devm_kzalloc(&pmcdev->pdev->dev, 68 + lpm_size * sizeof(u32), 69 + GFP_KERNEL); 70 + if (!pmc->lpm_req_regs) { 71 + ret = -ENOMEM; 72 + goto unregister_ep; 73 + } 74 + 75 + /* 76 + * PMC Low Power Mode (LPM) table 77 + * 78 + * In telemetry space, the LPM table contains a 4 byte header followed 79 + * by 8 consecutive mode blocks (one for each LPM mode). Each block 80 + * has a 4 byte header followed by a set of registers that describe the 81 + * IP state requirements for the given mode. The IP mapping is platform 82 + * specific but the same for each block, making for easy analysis. 83 + * Platforms only use a subset of the space to track the requirements 84 + * for their IPs. Callers provide the requirement registers they use as 85 + * a list of indices. Each requirement register is associated with an 86 + * IP map that's maintained by the caller. 87 + * 88 + * Header 89 + * +----+----------------------------+----------------------------+ 90 + * | 0 | REVISION | ENABLED MODES | 91 + * +----+--------------+-------------+-------------+--------------+ 92 + * 93 + * Low Power Mode 0 Block 94 + * +----+--------------+-------------+-------------+--------------+ 95 + * | 1 | SUB ID | SIZE | MAJOR | MINOR | 96 + * +----+--------------+-------------+-------------+--------------+ 97 + * | 2 | LPM0 Requirements 0 | 98 + * +----+---------------------------------------------------------+ 99 + * | | ... | 100 + * +----+---------------------------------------------------------+ 101 + * | 29 | LPM0 Requirements 27 | 102 + * +----+---------------------------------------------------------+ 103 + * 104 + * ... 105 + * 106 + * Low Power Mode 7 Block 107 + * +----+--------------+-------------+-------------+--------------+ 108 + * | | SUB ID | SIZE | MAJOR | MINOR | 109 + * +----+--------------+-------------+-------------+--------------+ 110 + * | 60 | LPM7 Requirements 0 | 111 + * +----+---------------------------------------------------------+ 112 + * | | ... | 113 + * +----+---------------------------------------------------------+ 114 + * | 87 | LPM7 Requirements 27 | 115 + * +----+---------------------------------------------------------+ 116 + * 117 + */ 118 + mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET; 119 + pmc_for_each_mode(i, mode, pmcdev) { 120 + u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps); 121 + int m; 122 + 123 + for (m = 0; m < num_maps; m++) { 124 + u8 sample_id = lpm_indices[m] + mode_offset; 125 + 126 + ret = pmt_telem_read32(ep, sample_id, req_offset, 1); 127 + if (ret) { 128 + dev_err(&pmcdev->pdev->dev, 129 + "couldn't read Low Power Mode requirements: %d\n", ret); 130 + devm_kfree(&pmcdev->pdev->dev, pmc->lpm_req_regs); 131 + goto unregister_ep; 132 + } 133 + ++req_offset; 134 + } 135 + mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET; 136 + } 137 + 138 + unregister_ep: 139 + pmt_telem_unregister_endpoint(ep); 140 + 141 + return ret; 142 + } 143 + 144 + int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev) 145 + { 146 + int ret, i; 147 + 148 + if (!pmcdev->ssram_pcidev) 149 + return -ENODEV; 150 + 151 + for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 152 + if (!pmcdev->pmcs[i]) 153 + continue; 154 + 155 + ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i]); 156 + if (ret) 157 + return ret; 158 + } 159 + 160 + return 0; 161 + } 28 162 29 163 static void 30 164 pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram) ··· 323 189 return ret; 324 190 } 325 191 MODULE_IMPORT_NS(INTEL_VSEC); 192 + MODULE_IMPORT_NS(INTEL_PMT_TELEMETRY);