android_kernel_samsung_sm8650/drivers/cpufreq/qcom-cpufreq-hw-debug.c
Kalpak Kawadkar 9cadb11265 cpufreq: qcom-cpufreq-hw: Move CPUFreq HW registers entry to sysfs
Move the logging of CPUFreq HW debug registers from debugfs to
sysfs under /sys/kernel/qcom-cpufreq-hw.

Change-Id: I2d23052d013125ee69de852dcec621c104f0d50a
Signed-off-by: Jagadeesh Kona <quic_jkona@quicinc.com>
Signed-off-by: Kalpak Kawadkar <quic_kkawadka@quicinc.com>
2023-06-01 16:23:33 +05:30

248 lines
6.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "cpufreq_hw_debug: %s: " fmt, __func__
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/panic_notifier.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
enum debug_hw_regs_data {
REG_PERF_STATE,
REG_CYCLE_CNTR,
REG_PSTATE_STATUS,
REG_EPSS_DEBUG_STATUS,
REG_EPSS_DEBUG_SRB,
REG_EPSS_DEBUG_LUT,
REG_ARRAY_SIZE,
};
struct cpufreq_hwregs {
void * __iomem *base;
int domain_cnt;
};
struct cpufreq_register_data {
char *name;
u16 offset;
};
static struct cpufreq_hwregs *hw_regs;
static const u16 *offsets;
static struct kobj_attribute cpufreq_hwregs_attr;
static struct kobject *cpufreqhw_kobj;
static const u16 cpufreq_qcom_std_data[] = {
[REG_PERF_STATE] = 0x920,
[REG_CYCLE_CNTR] = 0x9c0,
[REG_PSTATE_STATUS] = 0x700,
};
static const u16 cpufreq_qcom_std_epss_data[] = {
[REG_PERF_STATE] = 0x320,
[REG_CYCLE_CNTR] = 0x3c4,
[REG_PSTATE_STATUS] = 0x020,
[REG_EPSS_DEBUG_STATUS] = 0x01c,
[REG_EPSS_DEBUG_SRB] = 0x0bc,
[REG_EPSS_DEBUG_LUT] = 0x100,
};
static ssize_t cpufreq_hwregs_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int i, j, size = ARRAY_SIZE(cpufreq_qcom_std_data);
u32 regval, count = 0;
static struct cpufreq_register_data data[] = {
{"PERF_STATE_DESIRED", REG_PERF_STATE},
{"CYCLE_CNTR_VAL", REG_CYCLE_CNTR},
{"PSTATE_STATUS", REG_PSTATE_STATUS},
{"EPSS_DEBUG_STATUS", REG_EPSS_DEBUG_STATUS},
{"EPSS_DEBUG_SRB", REG_EPSS_DEBUG_SRB},
{"EPSS_DEBUG_LUT", REG_EPSS_DEBUG_LUT},
};
if (offsets == cpufreq_qcom_std_epss_data)
size = ARRAY_SIZE(cpufreq_qcom_std_epss_data);
for (i = 0; i < hw_regs->domain_cnt; i++) {
count += scnprintf(buf + count, PAGE_SIZE,
"FREQUENCY DOMAIN %d\n", i);
for (j = 0; j < size; j++) {
regval = readl_relaxed(hw_regs->base[i] +
offsets[data[j].offset]);
count += scnprintf(buf + count, PAGE_SIZE,
"%25s: 0x%.8x\n", data[j].name, regval);
}
}
return count;
}
static int cpufreq_panic_callback(struct notifier_block *nfb,
unsigned long event, void *unused)
{
int i, j, size = ARRAY_SIZE(cpufreq_qcom_std_data);
u32 regval;
static struct cpufreq_register_data data[] = {
{"PERF_STATE_DESIRED", REG_PERF_STATE},
{"CYCLE_CNTR_VAL", REG_CYCLE_CNTR},
{"PSTATE_STATUS", REG_PSTATE_STATUS},
{"EPSS_DEBUG_STATUS", REG_EPSS_DEBUG_STATUS},
{"EPSS_DEBUG_SRB", REG_EPSS_DEBUG_SRB},
{"EPSS_DEBUG_LUT", REG_EPSS_DEBUG_LUT},
};
if (offsets == cpufreq_qcom_std_epss_data)
size = ARRAY_SIZE(cpufreq_qcom_std_epss_data);
for (i = 0; i < hw_regs->domain_cnt; i++) {
pr_err("FREQUENCY DOMAIN %d\n", i);
for (j = 0; j < size; j++) {
regval = readl_relaxed(hw_regs->base[i] +
offsets[data[j].offset]);
pr_err("%25s: 0x%.8x\n", data[j].name, regval);
}
}
return NOTIFY_OK;
}
static struct notifier_block cpufreq_panic_notifier = {
.notifier_call = cpufreq_panic_callback,
.priority = 1,
};
static int cpufreq_get_hwregs(struct platform_device *pdev)
{
struct of_phandle_args args;
struct property *prop;
struct resource res;
void __iomem *base;
int i, ret;
offsets = of_device_get_match_data(&pdev->dev);
if (!offsets)
return -EINVAL;
hw_regs = devm_kzalloc(&pdev->dev, sizeof(*hw_regs), GFP_KERNEL);
if (!hw_regs)
return -ENOMEM;
prop = of_find_property(pdev->dev.of_node, "qcom,freq-hw-domain", NULL);
if (!prop)
return -EINVAL;
hw_regs->domain_cnt = prop->length / (2 * sizeof(prop->length));
hw_regs->base = devm_kzalloc(&pdev->dev,
hw_regs->domain_cnt * sizeof(base), GFP_KERNEL);
if (!hw_regs->base)
return -ENOMEM;
for (i = 0; i < hw_regs->domain_cnt; i++) {
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
"qcom,freq-hw-domain", 1, i, &args);
of_node_put(pdev->dev.of_node);
if (ret)
return ret;
ret = of_address_to_resource(args.np, args.args[0], &res);
if (ret)
return ret;
base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
if (!base)
return -ENOMEM;
hw_regs->base[i] = base;
}
atomic_notifier_chain_register(&panic_notifier_list,
&cpufreq_panic_notifier);
return 0;
}
static int enable_cpufreq_hw_debug(struct platform_device *pdev)
{
int ret;
ret = cpufreq_get_hwregs(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to map cpufreq hw regs\n");
return ret;
}
cpufreqhw_kobj = kobject_create_and_add("qcom-cpufreq-hw",
kernel_kobj);
if (!cpufreqhw_kobj)
return -ENOMEM;
sysfs_attr_init(&cpufreq_hwregs_attr.attr);
cpufreq_hwregs_attr.attr.name = "print_cpufreq_debug_regs";
cpufreq_hwregs_attr.show = cpufreq_hwregs_show;
cpufreq_hwregs_attr.attr.mode = 0444;
ret = sysfs_create_file(cpufreqhw_kobj, &cpufreq_hwregs_attr.attr);
if (ret) {
dev_err(&pdev->dev, "Failed to create sysfs entry\n");
kobject_put(cpufreqhw_kobj);
}
return ret;
}
static int qcom_cpufreq_hw_debug_probe(struct platform_device *pdev)
{
return enable_cpufreq_hw_debug(pdev);
}
static int qcom_cpufreq_hw_debug_remove(struct platform_device *pdev)
{
sysfs_remove_file(kernel_kobj, &cpufreq_hwregs_attr.attr);
kobject_put(cpufreqhw_kobj);
return 0;
}
static const struct of_device_id qcom_cpufreq_hw_debug_match[] = {
{ .compatible = "qcom,cpufreq-hw-debug",
.data = &cpufreq_qcom_std_data },
{ .compatible = "qcom,cpufreq-hw-epss-debug",
.data = &cpufreq_qcom_std_epss_data },
{}
};
static struct platform_driver qcom_cpufreq_hw_debug = {
.probe = qcom_cpufreq_hw_debug_probe,
.remove = qcom_cpufreq_hw_debug_remove,
.driver = {
.name = "qcom-cpufreq-hw-debug",
.of_match_table = qcom_cpufreq_hw_debug_match,
},
};
static int __init qcom_cpufreq_hw_debug_init(void)
{
return platform_driver_register(&qcom_cpufreq_hw_debug);
}
fs_initcall(qcom_cpufreq_hw_debug_init);
static void __exit qcom_cpufreq_hw_debug_exit(void)
{
return platform_driver_unregister(&qcom_cpufreq_hw_debug);
}
module_exit(qcom_cpufreq_hw_debug_exit);
MODULE_DESCRIPTION("QTI clock driver for CPUFREQ HW debug");
MODULE_LICENSE("GPL");