a208221c8c
Add the new compatible to allow the debugfs to be created and list the required cpufreq hardware registers. Change-Id: I87073bb4a9c334d8c8077ddfd807c08325ee3394 Signed-off-by: Taniya Das <tdas@codeaurora.org>
634 lines
15 KiB
C
634 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "cpufreq_hw_debug: %s: " fmt, __func__
|
|
|
|
#include <linux/debugfs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/uaccess.h>
|
|
#include <soc/qcom/scm.h>
|
|
|
|
enum trace_type {
|
|
XOR_PACKET,
|
|
PERIODIC_PACKET,
|
|
};
|
|
|
|
enum clock_domain {
|
|
CLKDOM0,
|
|
CLKDOM1,
|
|
CLKDOM2,
|
|
CLKDOM3,
|
|
|
|
CLK_DOMAIN_SIZE,
|
|
};
|
|
|
|
enum debug_trace_regs_data {
|
|
GLOBAL_CTRL_OFFSET,
|
|
CLKDOM_CTRL_OFFSET,
|
|
PERIODIC_TIMER_CTRL_OFFSET,
|
|
|
|
PERIODIC_TRACE_ENABLE_BIT,
|
|
CPUFREQ_HW_TRACE_ENABLE_BIT,
|
|
|
|
REG_PERF_STATE,
|
|
REG_CYCLE_CNTR,
|
|
REG_PSTATE_STATUS,
|
|
|
|
REG_ARRAY_SIZE,
|
|
};
|
|
|
|
struct cpufreq_hwregs {
|
|
void __iomem *base[REG_ARRAY_SIZE];
|
|
int domain_cnt;
|
|
struct dentry *debugfs_base;
|
|
};
|
|
|
|
struct cpufreq_register_data {
|
|
char *name;
|
|
u16 offset;
|
|
};
|
|
|
|
#define CLKDOM0_TRACE_PACKET_SHIFT 0
|
|
#define CLKDOM1_TRACE_PACKET_SHIFT 3
|
|
#define CLKDOM2_TRACE_PACKET_SHIFT 6
|
|
#define CLKDOM3_TRACE_PACKET_SHIFT 9
|
|
#define CLKDOM_TRACE_PACKET_WIDTH 2
|
|
#define MAX_DEBUG_BUF_LEN 50
|
|
#define MAX_PKT_SIZE 5
|
|
#define CLKDOMAIN_SET_VAL(val, packet_sel, shift) \
|
|
((val & ~GENMASK(shift + CLKDOM_TRACE_PACKET_WIDTH, shift)) \
|
|
| (packet_sel << shift))
|
|
#define CLKDOMAIN_CLEAR_VAL(val, packet_sel, shift) \
|
|
((val & ~GENMASK(shift + CLKDOM_TRACE_PACKET_WIDTH, shift)))
|
|
|
|
static struct cpufreq_hwregs *hw_regs;
|
|
static char debug_buf[MAX_DEBUG_BUF_LEN];
|
|
static const u16 *offsets;
|
|
|
|
static const u16 cpufreq_qcom_std_data[REG_ARRAY_SIZE] = {
|
|
[GLOBAL_CTRL_OFFSET] = 0x10,
|
|
[CLKDOM_CTRL_OFFSET] = 0x14,
|
|
[PERIODIC_TIMER_CTRL_OFFSET] = 0x1C,
|
|
[REG_PERF_STATE] = 0x920,
|
|
[REG_CYCLE_CNTR] = 0x9c0,
|
|
[REG_PSTATE_STATUS] = 0x700,
|
|
[CPUFREQ_HW_TRACE_ENABLE_BIT] = 16,
|
|
[PERIODIC_TRACE_ENABLE_BIT] = 17,
|
|
};
|
|
|
|
static const u16 cpufreq_qcom_std_epss_data[REG_ARRAY_SIZE] = {
|
|
[REG_PERF_STATE] = 0x320,
|
|
[REG_CYCLE_CNTR] = 0x3c4,
|
|
[REG_PSTATE_STATUS] = 0x020,
|
|
};
|
|
|
|
static int clock_timer_set(void *data, u64 val)
|
|
{
|
|
u32 base;
|
|
int ret;
|
|
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)data);
|
|
|
|
ret = scm_io_write(base + offsets[PERIODIC_TIMER_CTRL_OFFSET], val);
|
|
if (ret)
|
|
pr_err("failed(0x%x) to set clk timer\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int clock_timer_get(void *data, u64 *val)
|
|
{
|
|
u32 base;
|
|
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)data);
|
|
|
|
*val = scm_io_read(base + offsets[PERIODIC_TIMER_CTRL_OFFSET]);
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(clock_timer_fops, clock_timer_get, clock_timer_set,
|
|
"%llu\n");
|
|
|
|
static ssize_t trace_type_set(struct file *file, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
u32 regval, base;
|
|
int ret;
|
|
|
|
if (IS_ERR(file) || file == NULL) {
|
|
pr_err("input error %ld\n", PTR_ERR(file));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!file->private_data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)file->private_data);
|
|
|
|
if (count < MAX_DEBUG_BUF_LEN) {
|
|
if (copy_from_user(debug_buf, (void __user *) buf,
|
|
MAX_DEBUG_BUF_LEN))
|
|
return -EFAULT;
|
|
}
|
|
debug_buf[count] = '\0';
|
|
|
|
regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
|
|
if (!strcmp(debug_buf, "periodic\n")) {
|
|
regval |= BIT(offsets[PERIODIC_TRACE_ENABLE_BIT]);
|
|
} else if (!strcmp(debug_buf, "xor\n")) {
|
|
regval &= ~BIT(offsets[PERIODIC_TRACE_ENABLE_BIT]);
|
|
} else {
|
|
pr_err("error, supported trace mode types: 'periodic' or 'xor'\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = scm_io_write(base + offsets[GLOBAL_CTRL_OFFSET], regval);
|
|
if (ret)
|
|
pr_err("failed(0x%x) to set cpufreq clk timer\n", ret);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t trace_type_get(struct file *file, char __user *buf, size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
int len;
|
|
u32 regval, base;
|
|
|
|
if (IS_ERR(file) || file == NULL) {
|
|
pr_err("input error %ld\n", PTR_ERR(file));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!file->private_data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)file->private_data);
|
|
|
|
regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
|
|
if (regval && offsets[PERIODIC_TRACE_ENABLE_BIT])
|
|
len = snprintf(debug_buf, sizeof(debug_buf), "periodic\n");
|
|
else
|
|
len = snprintf(debug_buf, sizeof(debug_buf), "xor\n");
|
|
|
|
return simple_read_from_buffer((void __user *) buf, count, ppos,
|
|
(void *) debug_buf, len);
|
|
}
|
|
|
|
static int trace_type_open(struct inode *inode, struct file *file)
|
|
{
|
|
if (IS_ERR(file) || file == NULL) {
|
|
pr_err("input error %ld\n", PTR_ERR(file));
|
|
return -EINVAL;
|
|
}
|
|
|
|
file->private_data = inode->i_private;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations clk_trace_type_fops = {
|
|
.read = trace_type_get,
|
|
.open = trace_type_open,
|
|
.write = trace_type_set,
|
|
};
|
|
|
|
void __domain_packet_set(u32 *clkdom_regval, u32 *pktsel_regval, int clkdomain,
|
|
int pktsel, int packet_sel_shift, int enable)
|
|
{
|
|
if (!!enable) {
|
|
*clkdom_regval |= BIT(clkdomain);
|
|
*pktsel_regval = CLKDOMAIN_SET_VAL(*pktsel_regval, pktsel,
|
|
packet_sel_shift);
|
|
} else {
|
|
*clkdom_regval &= ~BIT(clkdomain);
|
|
*pktsel_regval &= CLKDOMAIN_CLEAR_VAL(*pktsel_regval, pktsel,
|
|
packet_sel_shift);
|
|
}
|
|
}
|
|
|
|
static ssize_t domain_packet_set(struct file *file,
|
|
const char __user *ubuf, size_t count, loff_t *ppos)
|
|
{
|
|
unsigned int filled, clk_domain, packet_sel, enable = 1;
|
|
u32 clkdom_regval, pktsel_regval, base;
|
|
char buf[MAX_DEBUG_BUF_LEN];
|
|
int ret;
|
|
|
|
if (IS_ERR(file) || file == NULL) {
|
|
pr_err("input error %ld\n", PTR_ERR(file));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!file->private_data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)file->private_data);
|
|
|
|
if (count < MAX_DEBUG_BUF_LEN) {
|
|
if (copy_from_user(buf, ubuf, count))
|
|
return -EFAULT;
|
|
}
|
|
|
|
buf[count] = '\0';
|
|
filled = sscanf(buf, "%u %u %u", &clk_domain, &packet_sel, &enable);
|
|
if (clk_domain > CLK_DOMAIN_SIZE || packet_sel > MAX_PKT_SIZE) {
|
|
pr_err("Clock domain and source selection not in range\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
clkdom_regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
|
|
pktsel_regval = scm_io_read(base + offsets[CLKDOM_CTRL_OFFSET]);
|
|
|
|
|
|
switch (clk_domain) {
|
|
case CLKDOM0:
|
|
__domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM0,
|
|
packet_sel, CLKDOM0_TRACE_PACKET_SHIFT, enable);
|
|
break;
|
|
case CLKDOM1:
|
|
__domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM1,
|
|
packet_sel, CLKDOM1_TRACE_PACKET_SHIFT, enable);
|
|
break;
|
|
case CLKDOM2:
|
|
__domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM2,
|
|
packet_sel, CLKDOM2_TRACE_PACKET_SHIFT, enable);
|
|
break;
|
|
case CLKDOM3:
|
|
__domain_packet_set(&clkdom_regval, &pktsel_regval, CLKDOM3,
|
|
packet_sel, CLKDOM3_TRACE_PACKET_SHIFT, enable);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = scm_io_write(base + offsets[GLOBAL_CTRL_OFFSET], clkdom_regval);
|
|
if (ret)
|
|
pr_err("failed(0x%x) to set cpufreq domain\n", ret);
|
|
|
|
ret = scm_io_write(base + offsets[CLKDOM_CTRL_OFFSET], pktsel_regval);
|
|
if (ret)
|
|
pr_err("failed(0x%x) to set cpufreq trace packet\n", ret);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t domain_packet_get(struct file *file, char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
u32 base, clkdom_regval, pktsel_regval;
|
|
int len;
|
|
|
|
if (IS_ERR(file) || file == NULL) {
|
|
pr_err("input error %ld\n", PTR_ERR(file));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!file->private_data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)file->private_data);
|
|
|
|
clkdom_regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
|
|
pktsel_regval = scm_io_read(base + offsets[CLKDOM_CTRL_OFFSET]);
|
|
|
|
len = snprintf(debug_buf, sizeof(debug_buf),
|
|
"GLOBAL_TRACE_CTRL = 0x%x\nCLKDOM_TRACE_CTRL = 0x%x\n",
|
|
clkdom_regval, pktsel_regval);
|
|
|
|
return simple_read_from_buffer((void __user *) buf, count, ppos,
|
|
(void *) debug_buf, len);
|
|
}
|
|
|
|
static int domain_packet_open(struct inode *inode, struct file *file)
|
|
{
|
|
if (IS_ERR(file) || file == NULL) {
|
|
pr_err("input error %ld\n", PTR_ERR(file));
|
|
return -EINVAL;
|
|
}
|
|
|
|
file->private_data = inode->i_private;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations clock_domian_packet_set = {
|
|
.write = domain_packet_set,
|
|
.open = domain_packet_open,
|
|
.read = domain_packet_get,
|
|
};
|
|
|
|
static int cpufreq_hw_trace_enable_set(void *data, u64 val)
|
|
{
|
|
u32 regval, base;
|
|
int ret;
|
|
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)data);
|
|
|
|
regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
|
|
if (!!val)
|
|
regval |= BIT(offsets[CPUFREQ_HW_TRACE_ENABLE_BIT]);
|
|
else
|
|
regval &= ~BIT(offsets[CPUFREQ_HW_TRACE_ENABLE_BIT]);
|
|
|
|
ret = scm_io_write(base + offsets[GLOBAL_CTRL_OFFSET], regval);
|
|
if (ret)
|
|
pr_err("failed(0x%x) to set cpufreq hw trace\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int cpufreq_hw_trace_enable_get(void *data, u64 *val)
|
|
{
|
|
u32 regval, base;
|
|
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)data);
|
|
|
|
regval = scm_io_read(base + offsets[GLOBAL_CTRL_OFFSET]);
|
|
*val = (regval & BIT(offsets[CPUFREQ_HW_TRACE_ENABLE_BIT])) >>
|
|
offsets[CPUFREQ_HW_TRACE_ENABLE_BIT];
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(clock_trace_enable, cpufreq_hw_trace_enable_get,
|
|
cpufreq_hw_trace_enable_set, "%llu\n");
|
|
|
|
static int print_cpufreq_hw_trace_regs(struct seq_file *s, void *unused)
|
|
{
|
|
u32 base;
|
|
int i;
|
|
|
|
static struct cpufreq_register_data data[] = {
|
|
{"GLOBAL_TRACE_CTRL", GLOBAL_CTRL_OFFSET},
|
|
{"CLKDOM_TRACE_CTRL", CLKDOM_CTRL_OFFSET},
|
|
{"PERIODIC_TIMER_TIMER", PERIODIC_TIMER_CTRL_OFFSET},
|
|
};
|
|
|
|
if (!s->private)
|
|
return -EINVAL;
|
|
|
|
base = *((u32 *)s->private);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
|
seq_printf(s, "%25s: 0x%.8x\n", data[i].name,
|
|
scm_io_read(base + offsets[data[i].offset]));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int print_trace_reg_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, print_cpufreq_hw_trace_regs, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations cpufreq_trace_register_fops = {
|
|
.open = print_trace_reg_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
static int print_cpufreq_hw_debug_regs(struct seq_file *s, void *unused)
|
|
{
|
|
int i, j;
|
|
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},
|
|
};
|
|
|
|
for (i = 0; i < hw_regs->domain_cnt; i++) {
|
|
seq_printf(s, "FREQUENCY DOMAIN %d\n", i);
|
|
for (j = 0; j < ARRAY_SIZE(data); j++) {
|
|
regval = readl_relaxed(hw_regs->base[i] +
|
|
offsets[data[j].offset]);
|
|
seq_printf(s, "%25s: 0x%.8x\n", data[j].name, regval);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int print_cpufreq_hw_reg_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, print_cpufreq_hw_debug_regs, NULL);
|
|
}
|
|
|
|
static const struct file_operations cpufreq_debug_register_fops = {
|
|
.open = print_cpufreq_hw_reg_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
static int cpufreq_panic_callback(struct notifier_block *nfb,
|
|
unsigned long event, void *unused)
|
|
{
|
|
int i, j;
|
|
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},
|
|
};
|
|
|
|
for (i = 0; i < hw_regs->domain_cnt; i++) {
|
|
pr_err("FREQUENCY DOMAIN %d\n", i);
|
|
for (j = 0; j < ARRAY_SIZE(data); 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));
|
|
|
|
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_trace_debug(struct platform_device *pdev,
|
|
bool is_secure)
|
|
{
|
|
struct resource *res;
|
|
void *base;
|
|
int ret, debug_only, epss_debug_only;
|
|
|
|
ret = cpufreq_get_hwregs(pdev);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "Failed to map cpufreq hw regs\n");
|
|
return ret;
|
|
}
|
|
|
|
hw_regs->debugfs_base = debugfs_create_dir("qcom-cpufreq-hw", NULL);
|
|
if (!hw_regs->debugfs_base) {
|
|
dev_err(&pdev->dev, "Failed to create debugfs entry\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (!debugfs_create_file("print_cpufreq_debug_regs", 0444,
|
|
hw_regs->debugfs_base, NULL, &cpufreq_debug_register_fops))
|
|
goto debugfs_fail;
|
|
|
|
debug_only = of_device_is_compatible(pdev->dev.of_node,
|
|
"qcom,cpufreq-hw-debug");
|
|
epss_debug_only = of_device_is_compatible(pdev->dev.of_node,
|
|
"qcom,cpufreq-hw-epss-debug");
|
|
|
|
if (!is_secure || epss_debug_only || debug_only)
|
|
return 0;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "domain-top");
|
|
if (!res) {
|
|
dev_err(&pdev->dev, "Failed to get domain-top register base\n");
|
|
return 0;
|
|
}
|
|
|
|
base = &(res->start);
|
|
|
|
if (!debugfs_create_file("print_cpufreq_trace_regs", 0444,
|
|
hw_regs->debugfs_base, base, &cpufreq_trace_register_fops))
|
|
goto debugfs_fail;
|
|
|
|
if (!debugfs_create_file("clock_domain_packet_sel", 0444,
|
|
hw_regs->debugfs_base, base, &clock_domian_packet_set))
|
|
goto debugfs_fail;
|
|
|
|
if (!debugfs_create_file("trace_enable", 0444, hw_regs->debugfs_base,
|
|
base, &clock_trace_enable))
|
|
goto debugfs_fail;
|
|
|
|
if (!debugfs_create_file("clock_timer", 0444,
|
|
hw_regs->debugfs_base, base, &clock_timer_fops))
|
|
goto debugfs_fail;
|
|
|
|
if (!debugfs_create_file("trace_type", 0444,
|
|
hw_regs->debugfs_base, base, &clk_trace_type_fops))
|
|
goto debugfs_fail;
|
|
|
|
return 0;
|
|
|
|
debugfs_fail:
|
|
dev_err(&pdev->dev, "Failed to create debugfs entry so cleaning up\n");
|
|
debugfs_remove_recursive(hw_regs->debugfs_base);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int qcom_cpufreq_hw_debug_probe(struct platform_device *pdev)
|
|
{
|
|
bool is_secure = scm_is_secure_device();
|
|
|
|
return enable_cpufreq_hw_trace_debug(pdev, is_secure);
|
|
}
|
|
|
|
static int qcom_cpufreq_hw_debug_remove(struct platform_device *pdev)
|
|
{
|
|
debugfs_remove_recursive(hw_regs->debugfs_base);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id qcom_cpufreq_hw_debug_trace_match[] = {
|
|
{ .compatible = "qcom,cpufreq-hw-debug-trace",
|
|
.data = &cpufreq_qcom_std_data },
|
|
{ .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_trace_match,
|
|
},
|
|
};
|
|
|
|
static int __init qcom_cpufreq_hw_debug_trace_init(void)
|
|
{
|
|
return platform_driver_register(&qcom_cpufreq_hw_debug);
|
|
}
|
|
fs_initcall(qcom_cpufreq_hw_debug_trace_init);
|
|
|
|
static void __exit qcom_cpufreq_hw_debug_trace_exit(void)
|
|
{
|
|
return platform_driver_unregister(&qcom_cpufreq_hw_debug);
|
|
}
|
|
module_exit(qcom_cpufreq_hw_debug_trace_exit);
|
|
|
|
MODULE_DESCRIPTION("QTI clock driver for CPUFREQ HW debug");
|
|
MODULE_LICENSE("GPL v2");
|