platform/x86:intel/pmc: Move PMC Core related functions

Move functions that implements PMC Core feature from core_ssram.c
to core.c. This patch is a preparation step to introduce a new
SSRAM Telemetry driver for the SSRAM device.

Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20250409191056.15434-2-xi.pardee@linux.intel.com
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Xi Pardee 2025-04-09 12:10:43 -07:00 committed by Ilpo Järvinen
parent 88f67f2a99
commit 78a7491d5c
No known key found for this signature in database
GPG Key ID: 59AC4F6153E5CE31
3 changed files with 176 additions and 176 deletions

View File

@ -1345,6 +1345,173 @@ 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;
}
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;
}
static 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;
}
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;
}
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;
}
/*
* 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
@ -1719,5 +1886,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");

View File

@ -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
@ -485,7 +490,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);
@ -497,6 +501,9 @@ 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, int pmc_index);
extern struct pmc_dev_info spt_pmc_dev;
extern struct pmc_dev_info cnp_pmc_dev;

View File

@ -14,7 +14,6 @@
#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
@ -24,142 +23,8 @@
#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)
{
@ -203,50 +68,11 @@ pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram)
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)
{
@ -329,4 +155,3 @@ int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func)
return ret;
}
MODULE_IMPORT_NS("INTEL_VSEC");
MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY");