summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel/pmc/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/intel/pmc/core.c')
-rw-r--r--drivers/platform/x86/intel/pmc/core.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index 9d3a1b6ef622..983e3a8f4910 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -20,6 +20,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/suspend.h>
+#include <linux/units.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
@@ -27,6 +28,7 @@
#include <asm/tsc.h>
#include "core.h"
+#include "../pmt/telemetry.h"
/* Maximum number of modes supported by platfoms that has low power mode capability */
const char *pmc_lpm_modes[] = {
@@ -822,6 +824,47 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs);
+static unsigned int pmc_core_get_crystal_freq(void)
+{
+ unsigned int eax_denominator, ebx_numerator, ecx_hz, edx;
+
+ if (boot_cpu_data.cpuid_level < 0x15)
+ return 0;
+
+ eax_denominator = ebx_numerator = ecx_hz = edx = 0;
+
+ /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */
+ cpuid(0x15, &eax_denominator, &ebx_numerator, &ecx_hz, &edx);
+
+ if (ebx_numerator == 0 || eax_denominator == 0)
+ return 0;
+
+ return ecx_hz;
+}
+
+static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ u64 die_c6_res, count;
+ int ret;
+
+ if (!pmcdev->crystal_freq) {
+ dev_warn_once(&pmcdev->pdev->dev, "Crystal frequency unavailable\n");
+ return -ENXIO;
+ }
+
+ ret = pmt_telem_read(pmcdev->punit_ep, pmcdev->die_c6_offset,
+ &count, 1);
+ if (ret)
+ return ret;
+
+ die_c6_res = div64_u64(count * HZ_PER_MHZ, pmcdev->crystal_freq);
+ seq_printf(s, "%llu\n", die_c6_res);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us);
+
static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
@@ -1118,6 +1161,12 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
pmcdev->dbgfs_dir, pmcdev,
&pmc_core_substate_req_regs_fops);
}
+
+ if (pmcdev->has_die_c6) {
+ debugfs_create_file("die_c6_us_show", 0444,
+ pmcdev->dbgfs_dir, pmcdev,
+ &pmc_core_die_c6_us_fops);
+ }
}
static const struct x86_cpu_id intel_pmc_core_ids[] = {
@@ -1212,6 +1261,10 @@ static void pmc_core_clean_structure(struct platform_device *pdev)
pci_dev_put(pmcdev->ssram_pcidev);
pci_disable_device(pmcdev->ssram_pcidev);
}
+
+ if (pmcdev->punit_ep)
+ pmt_telem_unregister_endpoint(pmcdev->punit_ep);
+
platform_set_drvdata(pdev, NULL);
mutex_destroy(&pmcdev->lock);
}
@@ -1232,6 +1285,8 @@ static int pmc_core_probe(struct platform_device *pdev)
if (!pmcdev)
return -ENOMEM;
+ pmcdev->crystal_freq = pmc_core_get_crystal_freq();
+
platform_set_drvdata(pdev, pmcdev);
pmcdev->pdev = pdev;