aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/intel/pmc/core.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-10-04 15:28:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-10-04 15:28:18 -0700
commitb66451723c45b791fd2824d1b8f62fe498989e23 (patch)
treef178b8b9a4d8b20786378c98854bf4a62a0ee380 /drivers/platform/x86/intel/pmc/core.c
parentMerge tag 'keys-next-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/j... (diff)
parentplatform/x86: pcengines-apuv2: Use static device properties (diff)
downloadlinux-b66451723c45b791fd2824d1b8f62fe498989e23.tar.gz
linux-b66451723c45b791fd2824d1b8f62fe498989e23.zip
Merge tag 'platform-drivers-x86-v6.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86
Pull x86 platform driver updates from Ilpo Järvinen: - amd/pmf: - Add support for adjusting PMF PPT and PPT APU thresholds - Extend custom BIOS inputs for more policies - Update ta_pmf_action structure to the latest PMF TA - arm64: - thinkpad-t14s-ec: Add EC driver for ThinkPad T14s Gen6 Snapdragon - int3472: - Increase handshake GPIO delay - intel/pmc: - SSRAM support for Lunar Lake and Panther Lake - Support reading substate requirements data from S0ix blockers (for platforms starting from Panther Lake) - Wildcat Lake support - intel-uncore-freq: - Solve duplicate sysfs entry warnings - Present unique domain ID per package - portwell-ec: - Support suspend/resume - Add hwmon support for voltage and temperature - redmi-wmi: - Add WMI driver for Redmibook keyboard - think-lmi: - Certificate support for ThinkCenter - x86-android-tables + others: - Convert away from legacy GPIO APIs - x86-android-tables: - Add support for Acer A1-840 tablet - Fix modules list for Lenovo devices - Stop using EPROBE_DEFER - Miscellaneous cleanups / refactoring / improvements * tag 'platform-drivers-x86-v6.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (63 commits) platform/x86: pcengines-apuv2: Use static device properties platform/x86: meraki-mx100: Use static device properties platform/x86: barco-p50-gpio: use software nodes for gpio-leds/keys platform/x86: x86-android-tablets: Stop using EPROBE_DEFER platform/x86: x86-android-tablets: Fix modules lists for Lenovo devices platform/x86: x86-android-tablets: Simplify lenovo_yoga_tab2_830_1050_exit() platform/x86: x86-android-tablets: Add support for Acer A1-840 tablet platform/x86: x86-android-tablets: Move Acer info to its own file platform/x86: x86-android-tablets: Update my email address platform/x86: x86-android-tablets: Simplify node-group [un]registration platform/x86: x86-android-tablets: use swnode_group instead of manual registering platform/x86: x86-android-tablets: replace bat_swnode with swnode_group platform/x86: x86-android-tablets: convert gpio_keys devices to GPIO references platform/x86: x86-android-tablets: remove support for GPIO lookup tables platform/x86: x86-android-tablets: convert Yoga Tab2 fast charger to GPIO references platform/x86: x86-android-tablets: convert HID-I2C devices to GPIO references platform/x86: x86-android-tablets: convert wm1502 devices to GPIO references platform/x86: x86-android-tablets: convert int3496 devices to GPIO references platform/x86: x86-android-tablets: convert EDT devices to GPIO references platform/x86: x86-android-tablets: convert Novatek devices to GPIO references ...
Diffstat (limited to 'drivers/platform/x86/intel/pmc/core.c')
-rw-r--r--drivers/platform/x86/intel/pmc/core.c185
1 files changed, 148 insertions, 37 deletions
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index d040290e80ff..ac3d19ae8c56 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -11,6 +11,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+enum header_type {
+ HEADER_STATUS,
+ HEADER_VALUE,
+};
+
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
@@ -828,19 +833,86 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs);
-static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index)
+static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index,
+ enum header_type type)
{
struct pmc_dev *pmcdev = s->private;
int mode;
- seq_printf(s, "%30s |", "Element");
+ seq_printf(s, "%40s |", "Element");
pmc_for_each_mode(mode, pmcdev)
seq_printf(s, " %9s |", pmc_lpm_modes[mode]);
- seq_printf(s, " %9s |", "Status");
- seq_printf(s, " %11s |\n", "Live Status");
+ if (type == HEADER_STATUS) {
+ seq_printf(s, " %9s |", "Status");
+ seq_printf(s, " %11s |\n", "Live Status");
+ } else {
+ seq_printf(s, " %9s |\n", "Value");
+ }
}
+static int pmc_core_substate_blk_req_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ unsigned int pmc_idx;
+
+ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) {
+ const struct pmc_bit_map **maps;
+ unsigned int arr_size, r_idx;
+ u32 offset, counter;
+ u32 *lpm_req_regs;
+ struct pmc *pmc;
+
+ pmc = pmcdev->pmcs[pmc_idx];
+ if (!pmc || !pmc->lpm_req_regs)
+ continue;
+
+ lpm_req_regs = pmc->lpm_req_regs;
+ maps = pmc->map->s0ix_blocker_maps;
+ offset = pmc->map->s0ix_blocker_offset;
+ arr_size = pmc_core_lpm_get_arr_size(maps);
+
+ /* Display the header */
+ pmc_core_substate_req_header_show(s, pmc_idx, HEADER_VALUE);
+
+ for (r_idx = 0; r_idx < arr_size; r_idx++) {
+ const struct pmc_bit_map *map;
+
+ for (map = maps[r_idx]; map->name; map++) {
+ int mode;
+
+ if (!map->blk)
+ continue;
+
+ counter = pmc_core_reg_read(pmc, offset);
+ seq_printf(s, "pmc%u: %34s |", pmc_idx, map->name);
+ pmc_for_each_mode(mode, pmcdev) {
+ bool required = *lpm_req_regs & BIT(mode);
+
+ seq_printf(s, " %9s |", required ? "Required" : " ");
+ }
+ seq_printf(s, " %9u |\n", counter);
+ offset += map->blk * S0IX_BLK_SIZE;
+ lpm_req_regs++;
+ }
+ }
+ }
+ return 0;
+}
+
+static int pmc_core_substate_blk_req_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmc_core_substate_blk_req_show, inode->i_private);
+}
+
+const struct file_operations pmc_core_substate_blk_req_fops = {
+ .owner = THIS_MODULE,
+ .open = pmc_core_substate_blk_req_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
@@ -872,7 +944,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
continue;
/* Display the header */
- pmc_core_substate_req_header_show(s, pmc_index);
+ pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS);
/* Loop over maps */
for (mp = 0; mp < num_maps; mp++) {
@@ -910,7 +982,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
}
/* Display the element name in the first column */
- seq_printf(s, "pmc%d: %26s |", pmc_index, map[i].name);
+ seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name);
/* Loop over the enabled states and display if required */
pmc_for_each_mode(mode, pmcdev) {
@@ -931,7 +1003,19 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
}
return 0;
}
-DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs);
+
+static int pmc_core_substate_req_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmc_core_substate_req_regs_show, inode->i_private);
+}
+
+const struct file_operations pmc_core_substate_req_regs_fops = {
+ .owner = THIS_MODULE,
+ .open = pmc_core_substate_req_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
static unsigned int pmc_core_get_crystal_freq(void)
{
@@ -1160,7 +1244,7 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev)
for (mode = 0; mode < LPM_MAX_NUM_MODES; mode++)
pri_order[mode_order[mode]] = mode;
else
- dev_warn(&pmcdev->pdev->dev,
+ dev_dbg(&pmcdev->pdev->dev,
"Assuming a default substate order for this platform\n");
/*
@@ -1264,7 +1348,7 @@ static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
debugfs_remove_recursive(pmcdev->dbgfs_dir);
}
-static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
+static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
struct pmc *primary_pmc = pmcdev->pmcs[PMC_IDX_MAIN];
struct dentry *dir;
@@ -1331,7 +1415,7 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
if (primary_pmc->lpm_req_regs) {
debugfs_create_file("substate_requirements", 0444,
pmcdev->dbgfs_dir, pmcdev,
- &pmc_core_substate_req_regs_fops);
+ pmc_dev_info->sub_req_show);
}
if (primary_pmc->map->pson_residency_offset && pmc_core_is_pson_residency_enabled(pmcdev)) {
@@ -1399,36 +1483,22 @@ static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *m
* +----+---------------------------------------------------------+
*
*/
-static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct pci_dev *pcidev)
+int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep)
{
- 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;
- }
+ if (!pmc->lpm_req_regs)
+ return -ENOMEM;
mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET;
pmc_for_each_mode(mode, pmcdev) {
@@ -1442,34 +1512,74 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct
if (ret) {
dev_err(&pmcdev->pdev->dev,
"couldn't read Low Power Mode requirements: %d\n", ret);
- goto unregister_ep;
+ return ret;
}
++req_offset;
}
mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET;
}
+ return ret;
+}
+
+int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
+ struct telem_endpoint *ep)
+{
+ u32 num_blocker, sample_offset;
+ unsigned int index;
+ u32 *req_offset;
+ int ret;
-unregister_ep:
- pmt_telem_unregister_endpoint(ep);
+ num_blocker = pmc->map->num_s0ix_blocker;
+ sample_offset = pmc->map->blocker_req_offset;
- return ret;
+ pmc->lpm_req_regs = devm_kcalloc(&pmcdev->pdev->dev, num_blocker,
+ sizeof(u32), GFP_KERNEL);
+ if (!pmc->lpm_req_regs)
+ return -ENOMEM;
+
+ req_offset = pmc->lpm_req_regs;
+ for (index = 0; index < num_blocker; index++, req_offset++) {
+ ret = pmt_telem_read32(ep, index + sample_offset, req_offset, 1);
+ if (ret) {
+ dev_err(&pmcdev->pdev->dev,
+ "couldn't read Low Power Mode requirements: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
}
-static int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev, int func)
+static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
struct pci_dev *pcidev __free(pci_dev_put) = NULL;
+ struct telem_endpoint *ep;
unsigned int i;
+ u32 guid;
int ret;
- pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func));
+ pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func));
if (!pcidev)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
- if (!pmcdev->pmcs[i])
+ struct pmc *pmc;
+
+ pmc = pmcdev->pmcs[i];
+ if (!pmc)
continue;
- ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i], pcidev);
+ 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;
+ }
+
+ ret = pmc_dev_info->sub_req(pmcdev, pmc, ep);
+ pmt_telem_unregister_endpoint(ep);
if (ret)
return ret;
}
@@ -1583,7 +1693,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) {
- ret = pmc_core_ssram_get_lpm_reqs(pmcdev, pmc_dev_info->pci_func);
+ ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info);
if (ret)
goto unmap_regbase;
}
@@ -1632,6 +1742,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
X86_MATCH_VFM(INTEL_ARROWLAKE_U, &arl_h_pmc_dev),
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev),
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev),
+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev),
{}
};
@@ -1758,7 +1869,7 @@ static int pmc_core_probe(struct platform_device *pdev)
pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(primary_pmc);
pmc_core_do_dmi_quirks(primary_pmc);
- pmc_core_dbgfs_register(pmcdev);
+ pmc_core_dbgfs_register(pmcdev, pmc_dev_info);
pm_report_max_hw_sleep(FIELD_MAX(SLP_S0_RES_COUNTER_MASK) *
pmc_core_adjust_slp_s0_step(primary_pmc, 1));