}
}
+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;
}
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)
{
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;
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)) {
* +----+---------------------------------------------------------+
*
*/
-static int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc,
- struct telem_endpoint *ep)
+int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep)
{
const u8 *lpm_indices;
int num_maps, mode_offset = 0;
return ret;
}
-static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, int func)
+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;
+
+ num_blocker = pmc->map->num_s0ix_blocker;
+ sample_offset = pmc->map->blocker_req_offset;
+
+ 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_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;
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;
return -EPROBE_DEFER;
}
- ret = pmc_core_pmt_get_lpm_req(pmcdev, pmc, ep);
+ ret = pmc_dev_info->sub_req(pmcdev, pmc, ep);
pmt_telem_unregister_endpoint(ep);
if (ret)
return ret;
pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid);
if (ssram) {
- ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info->pci_func);
+ ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info);
if (ret)
goto unmap_regbase;
}
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));
* @pm_read_disable_bit: Bit index to read PMC_READ_DISABLE
* @slps0_dbg_offset: PWRMBASE offset to SLP_S0_DEBUG_REG*
* @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter
+ * @num_s0ix_blocker: Number of S0ix blockers
+ * @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table
*
* Each PCH has unique set of register offsets and bit indexes. This structure
* captures them to have a common implementation.
const u32 ltr_ignore_max;
const u32 pm_vric1_offset;
const u32 s0ix_blocker_offset;
+ const u32 num_s0ix_blocker;
+ const u32 blocker_req_offset;
/* Low Power Mode registers */
const int lpm_num_maps;
const int lpm_num_modes;
* SSRAM support.
* @map: Pointer to a pmc_reg_map struct that contains platform
* specific attributes of the primary PMC
+ * @sub_req_show: File operations to show substate requirements
* @suspend: Function to perform platform specific suspend
* @resume: Function to perform platform specific resume
* @init: Function to perform platform specific init action
+ * @sub_req: Function to achieve low power mode substate requirements
*/
struct pmc_dev_info {
u8 pci_func;
u32 dmu_guid;
struct pmc_info *regmap_list;
const struct pmc_reg_map *map;
+ const struct file_operations *sub_req_show;
void (*suspend)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
+ int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
};
extern const struct pmc_bit_map msr_map[];
void cnl_suspend(struct pmc_dev *pmcdev);
int cnl_resume(struct pmc_dev *pmcdev);
+int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
+int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
+ struct telem_endpoint *ep);
+
+extern const struct file_operations pmc_core_substate_req_regs_fops;
+extern const struct file_operations pmc_core_substate_blk_req_fops;
#define pmc_for_each_mode(mode, pmcdev) \
for (unsigned int __i = 0, __cond; \