platform/x86: intel_pmc_core: Add option to set/clear LPM mode
[sfrench/cifs-2.6.git] / drivers / platform / x86 / intel_pmc_core.c
index 56823f17e113e5106c9cc375896672ee343ff4b3..5240eefe8e6e426ca8da3ddc317f637b48751f88 100644 (file)
@@ -586,6 +586,7 @@ static const struct pmc_reg_map tgl_reg_map = {
        .ltr_ignore_max = TGL_NUM_IP_IGN_ALLOWED,
        .lpm_num_maps = TGL_LPM_NUM_MAPS,
        .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
+       .lpm_sts_latch_en_offset = TGL_LPM_STS_LATCH_EN_OFFSET,
        .lpm_en_offset = TGL_LPM_EN_OFFSET,
        .lpm_priority_offset = TGL_LPM_PRI_OFFSET,
        .lpm_residency_offset = TGL_LPM_RESIDENCY_OFFSET,
@@ -1321,6 +1322,114 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
 }
 DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs);
 
+static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
+{
+       struct pmc_dev *pmcdev = s->private;
+       bool c10;
+       u32 reg;
+       int idx, mode;
+
+       reg = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_sts_latch_en_offset);
+       if (reg & LPM_STS_LATCH_MODE) {
+               seq_puts(s, "c10");
+               c10 = false;
+       } else {
+               seq_puts(s, "[c10]");
+               c10 = true;
+       }
+
+       pmc_for_each_mode(idx, mode, pmcdev) {
+               if ((BIT(mode) & reg) && !c10)
+                       seq_printf(s, " [%s]", pmc_lpm_modes[mode]);
+               else
+                       seq_printf(s, " %s", pmc_lpm_modes[mode]);
+       }
+
+       seq_puts(s, " clear\n");
+
+       return 0;
+}
+
+static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
+                                            const char __user *userbuf,
+                                            size_t count, loff_t *ppos)
+{
+       struct seq_file *s = file->private_data;
+       struct pmc_dev *pmcdev = s->private;
+       bool clear = false, c10 = false;
+       unsigned char buf[8];
+       size_t ret;
+       int idx, m, mode;
+       u32 reg;
+
+       if (count > sizeof(buf) - 1)
+               return -EINVAL;
+
+       ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count);
+       if (ret < 0)
+               return ret;
+
+       buf[count] = '\0';
+
+       /*
+        * Allowed strings are:
+        *      Any enabled substate, e.g. 'S0i2.0'
+        *      'c10'
+        *      'clear'
+        */
+       mode = sysfs_match_string(pmc_lpm_modes, buf);
+
+       /* Check string matches enabled mode */
+       pmc_for_each_mode(idx, m, pmcdev)
+               if (mode == m)
+                       break;
+
+       if (mode != m || mode < 0) {
+               if (sysfs_streq(buf, "clear"))
+                       clear = true;
+               else if (sysfs_streq(buf, "c10"))
+                       c10 = true;
+               else
+                       return -EINVAL;
+       }
+
+       if (clear) {
+               mutex_lock(&pmcdev->lock);
+
+               reg = pmc_core_reg_read(pmcdev, pmcdev->map->etr3_offset);
+               reg |= ETR3_CLEAR_LPM_EVENTS;
+               pmc_core_reg_write(pmcdev, pmcdev->map->etr3_offset, reg);
+
+               mutex_unlock(&pmcdev->lock);
+
+               return count;
+       }
+
+       if (c10) {
+               mutex_lock(&pmcdev->lock);
+
+               reg = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_sts_latch_en_offset);
+               reg &= ~LPM_STS_LATCH_MODE;
+               pmc_core_reg_write(pmcdev, pmcdev->map->lpm_sts_latch_en_offset, reg);
+
+               mutex_unlock(&pmcdev->lock);
+
+               return count;
+       }
+
+       /*
+        * For LPM mode latching we set the latch enable bit and selected mode
+        * and clear everything else.
+        */
+       reg = LPM_STS_LATCH_MODE | BIT(mode);
+       mutex_lock(&pmcdev->lock);
+       pmc_core_reg_write(pmcdev, pmcdev->map->lpm_sts_latch_en_offset, reg);
+       mutex_unlock(&pmcdev->lock);
+
+       return count;
+}
+DEFINE_PMC_CORE_ATTR_WRITE(pmc_core_lpm_latch_mode);
+
 static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
 {
        struct pmc_dev *pmcdev = s->private;
@@ -1439,6 +1548,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
                debugfs_create_file("substate_live_status_registers", 0444,
                                    pmcdev->dbgfs_dir, pmcdev,
                                    &pmc_core_substate_l_sts_regs_fops);
+               debugfs_create_file("lpm_latch_mode", 0644,
+                                   pmcdev->dbgfs_dir, pmcdev,
+                                   &pmc_core_lpm_latch_mode_fops);
        }
 
        if (pmcdev->lpm_req_regs) {