blob: 451d55465b175927c89b61ba78c093fa544d7ab0 [file] [log] [blame]
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <soc/samsung/cal-if.h>
#include "cmucal.h"
#include "vclk.h"
#include "ra.h"
/*** debugfs support ***/
#ifdef CONFIG_DEBUG_FS
#define MAX_NAME_SIZE 50
static struct dentry *rootdir;
static struct cmucal_clk *clk_info;
static struct vclk *dvfs_domain;
static unsigned int margin;
extern unsigned int dbg_offset;
void print_clk_on_blk(void)
{
struct cmucal_clk *clk;
int size, reg;
int i;
size = cmucal_get_list_size(PLL_TYPE);
for (i = 0; i < size ; i++) {
clk = cmucal_get_node(i | PLL_TYPE);
if (!clk || (clk->paddr & 0xFFFF0000) != 0x15a80000)
continue;
reg = readl((clk->pll_con0 - 0xE0) + dbg_offset);
if (((reg >> 4) & 0x7) != 0x3)
printk("name %s : [0x%x] active\n", clk->name, reg);
else
printk("name %s : [0x%x] idle\n", clk->name, reg);
}
size = cmucal_get_list_size(GATE_TYPE);
for (i = 0; i < size ; i++) {
clk = cmucal_get_node(i | GATE_TYPE);
if (!clk || (clk->paddr & 0xFFFF0000) != 0x15a80000)
continue;
reg = readl(clk->offset + dbg_offset);
if ((reg & 0x7) != 0x3)
printk("name %s : [0x%x] active\n", clk->name, reg);
else
printk("name %s : [0x%x] idle\n", clk->name, reg);
}
}
static int vclk_table_dump(struct seq_file *s, void *p)
{
struct vclk *vclk = s->private;
struct cmucal_clk *clk;
int i, j;
seq_puts(s, "-----------------------------------------------------\n");
seq_printf(s, "%s <%x> rate = %lu\n",
vclk->name, vclk->id, vclk_recalc_rate(vclk->id));
for (i = 0; i < vclk->num_list; i++) {
clk = cmucal_get_node(vclk->list[i]);
if (!clk)
continue;
seq_printf(s, " [%s] value : %u rate : %u\n",
clk->name,
ra_get_value(clk->id),
vclk_debug_clk_get_rate(clk->id));
}
if (!vclk->lut)
return 0;
for (i = 0; i < vclk->num_rates; i++) {
seq_printf(s, "[%2d]%7d :", i + 1, vclk->lut[i].rate);
for (j = 0; j < vclk->num_list; j++)
seq_printf(s, "%7d ", vclk->lut[i].params[j]);
seq_puts(s, "\n");
}
return 0;
}
static int vclk_table_open(struct inode *inode, struct file *file)
{
return single_open(file, vclk_table_dump, inode->i_private);
}
static const struct file_operations vclk_table_fops = {
.open = vclk_table_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int vclk_clk_info(struct seq_file *s, void *p)
{
return 0;
}
static int vclk_clk_info_open(struct inode *inode, struct file *file)
{
return single_open(file, vclk_clk_info, inode->i_private);
}
static ssize_t
vclk_read_clk_info(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{
struct cmucal_clk *clk;
struct cmucal_clk *parent;
char buf[512];
int r;
clk = clk_info;
if (clk == NULL) {
r = sprintf(buf, "echo \"clk_name\" > clk_info\n");
} else {
r = sprintf(buf, "clk name : %s\n"
" id : 0x%x\n"
" rate : %u\n"
" value : %u\n"
" path :\n", clk->name, clk->id,
vclk_debug_clk_get_rate(clk->id),
ra_get_value(clk->id));
parent = ra_get_parent(clk->id);
while (parent != NULL) {
r += sprintf(buf + r, "<- %s ", parent->name);
parent = ra_get_parent(parent->id);
}
r += sprintf(buf + r, "\n");
}
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
static ssize_t
vclk_write_clk_info(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char buf[MAX_NAME_SIZE + 1];
unsigned int id;
size_t ret;
ret = cnt;
if (cnt == 0)
return cnt;
if (cnt > MAX_NAME_SIZE)
cnt = MAX_NAME_SIZE;
if (copy_from_user(buf, ubuf, cnt))
return -EVCLKFAULT;
if (buf[cnt-1] == '\n')
buf[cnt-1] = 0;
else
buf[cnt] = 0;
if (!strcmp(buf, "hwacg")) {
print_clk_on_blk();
} else {
id = cmucal_get_id(buf);
clk_info = cmucal_get_node(id);
}
*ppos += ret;
return cnt;
}
static ssize_t
vclk_read_dvfs_domain(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{
struct vclk *vclk;
char buf[512];
int i, size;
int r;
vclk = dvfs_domain;
if (vclk == NULL)
r = sprintf(buf, "echo id > dvfs_domain\n");
else
r = sprintf(buf, "%s : 0x%x\n", dvfs_domain->name, dvfs_domain->id);
r += sprintf(buf + r, "- dvfs list\n");
size = cmucal_get_list_size(ACPM_VCLK_TYPE);
for (i = 0; i < size ; i++) {
vclk = cmucal_get_node(ACPM_VCLK_TYPE | i);
if (vclk == NULL)
continue;
r += sprintf(buf + r, " %s : 0x%x\n", vclk->name, vclk->id);
}
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
static ssize_t
vclk_write_dvfs_domain(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char buf[16];
ssize_t len;
u32 id;
len = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, cnt);
if (len < 0)
return len;
buf[len] = '\0';
if (!kstrtouint(buf, 0, &id)) {
dvfs_domain = cmucal_get_node(id);
if (!dvfs_domain || !IS_ACPM_VCLK(dvfs_domain->id))
dvfs_domain = NULL;
}
return len;
}
static ssize_t
vclk_read_set_margin(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char buf[512];
int r;
r = sprintf(buf, "margin : %u\n", margin);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
static ssize_t
vclk_write_set_margin(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char buf[16];
ssize_t len;
u32 volt;
len = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, cnt);
if (len < 0)
return len;
buf[len] = '\0';
if (!kstrtoint(buf, 0, &volt)) {
margin = volt;
cal_dfs_set_volt_margin(dvfs_domain->id, volt);
}
return len;
}
static const struct file_operations clk_info_fops = {
.open = vclk_clk_info_open,
.read = vclk_read_clk_info,
.write = vclk_write_clk_info,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations dvfs_domain_fops = {
.open = simple_open,
.read = vclk_read_dvfs_domain,
.write = vclk_write_dvfs_domain,
.llseek = seq_lseek,
};
static const struct file_operations set_margin_fops = {
.open = simple_open,
.read = vclk_read_set_margin,
.write = vclk_write_set_margin,
.llseek = seq_lseek,
};
/* caller must hold prepare_lock */
static int vclk_debug_create_one(struct vclk *vclk, struct dentry *pdentry)
{
struct dentry *d;
int ret = -ENOMEM;
if (!vclk || !pdentry) {
ret = -EINVAL;
goto out;
}
d = debugfs_create_dir(vclk->name, pdentry);
if (!d)
goto out;
vclk->dentry = d;
d = debugfs_create_x32("vclk_id", S_IRUSR, vclk->dentry,
(u32 *)&vclk->id);
if (!d)
goto err_out;
d = debugfs_create_u32("vclk_rate", S_IRUSR, vclk->dentry,
(u32 *)&vclk->vrate);
if (!d)
goto err_out;
d = debugfs_create_u32("vclk_num_rates", S_IRUSR, vclk->dentry,
(u32 *)&vclk->num_rates);
if (!d)
goto err_out;
d = debugfs_create_u32("vclk_num_list", S_IRUSR, vclk->dentry,
(u32 *)&vclk->num_list);
if (!d)
goto err_out;
d = debugfs_create_file("vclk_table", S_IRUSR, vclk->dentry, vclk,
&vclk_table_fops);
if (!d)
return -ENOMEM;
ret = 0;
goto out;
err_out:
debugfs_remove_recursive(vclk->dentry);
vclk->dentry = NULL;
out:
return ret;
}
unsigned int vclk_debug_clk_get_rate(unsigned int id)
{
unsigned long rate;
rate = ra_recalc_rate(id);
return rate;
}
EXPORT_SYMBOL_GPL(vclk_debug_clk_get_rate);
unsigned int vclk_debug_clk_get_value(unsigned int id)
{
unsigned int val;
val = ra_get_value(id);
return val;
}
EXPORT_SYMBOL_GPL(vclk_debug_clk_get_value);
int vclk_debug_clk_set_value(unsigned int id, unsigned int params)
{
int ret;
ret = ra_set_value(id, params);
return ret;
}
EXPORT_SYMBOL_GPL(vclk_debug_clk_set_value);
/**
* vclk_debug_init - lazily create the debugfs clk tree visualization
*/
static int __init vclk_debug_init(void)
{
struct vclk *vclk;
struct dentry *d;
int i;
rootdir = debugfs_create_dir("vclk", NULL);
if (!rootdir)
return -ENOMEM;
for (i = 0; i < cmucal_get_list_size(VCLK_TYPE); i++) {
vclk = cmucal_get_node(i | VCLK_TYPE);
if (!vclk)
continue;
vclk_debug_create_one(vclk, rootdir);
}
d = debugfs_create_file("clk_info", 0600, rootdir, NULL,
&clk_info_fops);
if (!d)
return -ENOMEM;
d = debugfs_create_file("dvfs_domain", 0600, rootdir, NULL,
&dvfs_domain_fops);
if (!d)
return -ENOMEM;
d = debugfs_create_file("set_margin", 0600, rootdir, NULL,
&set_margin_fops);
if (!d)
return -ENOMEM;
return 0;
}
late_initcall(vclk_debug_init);
#endif