mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 13:53:33 +00:00
soc: mediatek: SVS: add debug commands
The purpose of SVS is to help find the suitable voltages for DVFS. Therefore, if SVS bank voltages are concerned to be wrong, we can show/disable SVS bank voltages by this patch. Signed-off-by: Roger Lu <roger.lu@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Reviewed-by: Kevin Hilman <khilman@baylibre.com> Link: https://lore.kernel.org/r/20220516004311.18358-6-roger.lu@mediatek.com Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
This commit is contained in:
parent
6c69271926
commit
13f1bbcfb5
1 changed files with 275 additions and 0 deletions
|
@ -7,6 +7,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -23,6 +24,7 @@
|
|||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/thermal.h>
|
||||
|
@ -69,6 +71,39 @@
|
|||
|
||||
static DEFINE_SPINLOCK(svs_lock);
|
||||
|
||||
#define debug_fops_ro(name) \
|
||||
static int svs_##name##_debug_open(struct inode *inode, \
|
||||
struct file *filp) \
|
||||
{ \
|
||||
return single_open(filp, svs_##name##_debug_show, \
|
||||
inode->i_private); \
|
||||
} \
|
||||
static const struct file_operations svs_##name##_debug_fops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = svs_##name##_debug_open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
#define debug_fops_rw(name) \
|
||||
static int svs_##name##_debug_open(struct inode *inode, \
|
||||
struct file *filp) \
|
||||
{ \
|
||||
return single_open(filp, svs_##name##_debug_show, \
|
||||
inode->i_private); \
|
||||
} \
|
||||
static const struct file_operations svs_##name##_debug_fops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = svs_##name##_debug_open, \
|
||||
.read = seq_read, \
|
||||
.write = svs_##name##_debug_write, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
#define svs_dentry_data(name) {__stringify(name), &svs_##name##_debug_fops}
|
||||
|
||||
/**
|
||||
* enum svsb_phase - svs bank phase enumeration
|
||||
* @SVSB_PHASE_ERROR: svs bank encounters unexpected condition
|
||||
|
@ -268,6 +303,7 @@ struct svs_platform_data {
|
|||
* @tzone_name: thermal zone name
|
||||
* @phase: bank current phase
|
||||
* @volt_od: bank voltage overdrive
|
||||
* @reg_data: bank register data in different phase for debug purpose
|
||||
* @pm_runtime_enabled_count: bank pm runtime enabled count
|
||||
* @mode_support: bank mode support.
|
||||
* @freq_base: reference frequency for bank init
|
||||
|
@ -326,6 +362,7 @@ struct svs_bank {
|
|||
char *tzone_name;
|
||||
enum svsb_phase phase;
|
||||
s32 volt_od;
|
||||
u32 reg_data[SVSB_PHASE_MAX][SVS_REG_MAX];
|
||||
u32 pm_runtime_enabled_count;
|
||||
u32 mode_support;
|
||||
u32 freq_base;
|
||||
|
@ -467,6 +504,220 @@ static int svs_adjust_pm_opp_volts(struct svs_bank *svsb)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int svs_dump_debug_show(struct seq_file *m, void *p)
|
||||
{
|
||||
struct svs_platform *svsp = (struct svs_platform *)m->private;
|
||||
struct svs_bank *svsb;
|
||||
unsigned long svs_reg_addr;
|
||||
u32 idx, i, j, bank_id;
|
||||
|
||||
for (i = 0; i < svsp->efuse_max; i++)
|
||||
if (svsp->efuse && svsp->efuse[i])
|
||||
seq_printf(m, "M_HW_RES%d = 0x%08x\n",
|
||||
i, svsp->efuse[i]);
|
||||
|
||||
for (i = 0; i < svsp->tefuse_max; i++)
|
||||
if (svsp->tefuse)
|
||||
seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n",
|
||||
i, svsp->tefuse[i]);
|
||||
|
||||
for (bank_id = 0, idx = 0; idx < svsp->bank_max; idx++, bank_id++) {
|
||||
svsb = &svsp->banks[idx];
|
||||
|
||||
for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) {
|
||||
seq_printf(m, "Bank_number = %u\n", bank_id);
|
||||
|
||||
if (i == SVSB_PHASE_INIT01 || i == SVSB_PHASE_INIT02)
|
||||
seq_printf(m, "mode = init%d\n", i);
|
||||
else if (i == SVSB_PHASE_MON)
|
||||
seq_puts(m, "mode = mon\n");
|
||||
else
|
||||
seq_puts(m, "mode = error\n");
|
||||
|
||||
for (j = DESCHAR; j < SVS_REG_MAX; j++) {
|
||||
svs_reg_addr = (unsigned long)(svsp->base +
|
||||
svsp->regs[j]);
|
||||
seq_printf(m, "0x%08lx = 0x%08x\n",
|
||||
svs_reg_addr, svsb->reg_data[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug_fops_ro(dump);
|
||||
|
||||
static int svs_enable_debug_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct svs_bank *svsb = (struct svs_bank *)m->private;
|
||||
|
||||
switch (svsb->phase) {
|
||||
case SVSB_PHASE_ERROR:
|
||||
seq_puts(m, "disabled\n");
|
||||
break;
|
||||
case SVSB_PHASE_INIT01:
|
||||
seq_puts(m, "init1\n");
|
||||
break;
|
||||
case SVSB_PHASE_INIT02:
|
||||
seq_puts(m, "init2\n");
|
||||
break;
|
||||
case SVSB_PHASE_MON:
|
||||
seq_puts(m, "mon mode\n");
|
||||
break;
|
||||
default:
|
||||
seq_puts(m, "unknown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t svs_enable_debug_write(struct file *filp,
|
||||
const char __user *buffer,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct svs_bank *svsb = file_inode(filp)->i_private;
|
||||
struct svs_platform *svsp = dev_get_drvdata(svsb->dev);
|
||||
unsigned long flags;
|
||||
int enabled, ret;
|
||||
char *buf = NULL;
|
||||
|
||||
if (count >= PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
buf = (char *)memdup_user_nul(buffer, count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
ret = kstrtoint(buf, 10, &enabled);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!enabled) {
|
||||
spin_lock_irqsave(&svs_lock, flags);
|
||||
svsp->pbank = svsb;
|
||||
svsb->mode_support = SVSB_MODE_ALL_DISABLE;
|
||||
svs_switch_bank(svsp);
|
||||
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
|
||||
svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
|
||||
spin_unlock_irqrestore(&svs_lock, flags);
|
||||
|
||||
svsb->phase = SVSB_PHASE_ERROR;
|
||||
svs_adjust_pm_opp_volts(svsb);
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
debug_fops_rw(enable);
|
||||
|
||||
static int svs_status_debug_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct svs_bank *svsb = (struct svs_bank *)m->private;
|
||||
struct dev_pm_opp *opp;
|
||||
int tzone_temp = 0, ret;
|
||||
u32 i;
|
||||
|
||||
ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp);
|
||||
if (ret)
|
||||
seq_printf(m, "%s: temperature ignore\n", svsb->name);
|
||||
else
|
||||
seq_printf(m, "%s: temperature = %d\n", svsb->name, tzone_temp);
|
||||
|
||||
for (i = 0; i < svsb->opp_count; i++) {
|
||||
opp = dev_pm_opp_find_freq_exact(svsb->opp_dev,
|
||||
svsb->opp_dfreq[i], true);
|
||||
if (IS_ERR(opp)) {
|
||||
seq_printf(m, "%s: cannot find freq = %u (%ld)\n",
|
||||
svsb->name, svsb->opp_dfreq[i],
|
||||
PTR_ERR(opp));
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
|
||||
seq_printf(m, "opp_freq[%02u]: %u, opp_volt[%02u]: %lu, ",
|
||||
i, svsb->opp_dfreq[i], i,
|
||||
dev_pm_opp_get_voltage(opp));
|
||||
seq_printf(m, "svsb_volt[%02u]: 0x%x, freq_pct[%02u]: %u\n",
|
||||
i, svsb->volt[i], i, svsb->freq_pct[i]);
|
||||
dev_pm_opp_put(opp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug_fops_ro(status);
|
||||
|
||||
static int svs_create_debug_cmds(struct svs_platform *svsp)
|
||||
{
|
||||
struct svs_bank *svsb;
|
||||
struct dentry *svs_dir, *svsb_dir, *file_entry;
|
||||
const char *d = "/sys/kernel/debug/svs";
|
||||
u32 i, idx;
|
||||
|
||||
struct svs_dentry {
|
||||
const char *name;
|
||||
const struct file_operations *fops;
|
||||
};
|
||||
|
||||
struct svs_dentry svs_entries[] = {
|
||||
svs_dentry_data(dump),
|
||||
};
|
||||
|
||||
struct svs_dentry svsb_entries[] = {
|
||||
svs_dentry_data(enable),
|
||||
svs_dentry_data(status),
|
||||
};
|
||||
|
||||
svs_dir = debugfs_create_dir("svs", NULL);
|
||||
if (IS_ERR(svs_dir)) {
|
||||
dev_err(svsp->dev, "cannot create %s: %ld\n",
|
||||
d, PTR_ERR(svs_dir));
|
||||
return PTR_ERR(svs_dir);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(svs_entries); i++) {
|
||||
file_entry = debugfs_create_file(svs_entries[i].name, 0664,
|
||||
svs_dir, svsp,
|
||||
svs_entries[i].fops);
|
||||
if (IS_ERR(file_entry)) {
|
||||
dev_err(svsp->dev, "cannot create %s/%s: %ld\n",
|
||||
d, svs_entries[i].name, PTR_ERR(file_entry));
|
||||
return PTR_ERR(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
for (idx = 0; idx < svsp->bank_max; idx++) {
|
||||
svsb = &svsp->banks[idx];
|
||||
|
||||
if (svsb->mode_support == SVSB_MODE_ALL_DISABLE)
|
||||
continue;
|
||||
|
||||
svsb_dir = debugfs_create_dir(svsb->name, svs_dir);
|
||||
if (IS_ERR(svsb_dir)) {
|
||||
dev_err(svsp->dev, "cannot create %s/%s: %ld\n",
|
||||
d, svsb->name, PTR_ERR(svsb_dir));
|
||||
return PTR_ERR(svsb_dir);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(svsb_entries); i++) {
|
||||
file_entry = debugfs_create_file(svsb_entries[i].name,
|
||||
0664, svsb_dir, svsb,
|
||||
svsb_entries[i].fops);
|
||||
if (IS_ERR(file_entry)) {
|
||||
dev_err(svsp->dev, "no %s/%s/%s?: %ld\n",
|
||||
d, svsb->name, svsb_entries[i].name,
|
||||
PTR_ERR(file_entry));
|
||||
return PTR_ERR(file_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx)
|
||||
{
|
||||
u32 vx;
|
||||
|
@ -591,6 +842,16 @@ static void svs_set_bank_phase(struct svs_platform *svsp,
|
|||
}
|
||||
}
|
||||
|
||||
static inline void svs_save_bank_register_data(struct svs_platform *svsp,
|
||||
enum svsb_phase phase)
|
||||
{
|
||||
struct svs_bank *svsb = svsp->pbank;
|
||||
enum svs_reg_index rg_i;
|
||||
|
||||
for (rg_i = DESCHAR; rg_i < SVS_REG_MAX; rg_i++)
|
||||
svsb->reg_data[phase][rg_i] = svs_readl_relaxed(svsp, rg_i);
|
||||
}
|
||||
|
||||
static inline void svs_error_isr_handler(struct svs_platform *svsp)
|
||||
{
|
||||
struct svs_bank *svsb = svsp->pbank;
|
||||
|
@ -605,6 +866,8 @@ static inline void svs_error_isr_handler(struct svs_platform *svsp)
|
|||
svs_readl_relaxed(svsp, SMSTATE1));
|
||||
dev_err(svsb->dev, "TEMP = 0x%08x\n", svs_readl_relaxed(svsp, TEMP));
|
||||
|
||||
svs_save_bank_register_data(svsp, SVSB_PHASE_ERROR);
|
||||
|
||||
svsb->phase = SVSB_PHASE_ERROR;
|
||||
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
|
||||
svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
|
||||
|
@ -619,6 +882,8 @@ static inline void svs_init01_isr_handler(struct svs_platform *svsp)
|
|||
svs_readl_relaxed(svsp, VDESIGN30),
|
||||
svs_readl_relaxed(svsp, DCVALUES));
|
||||
|
||||
svs_save_bank_register_data(svsp, SVSB_PHASE_INIT01);
|
||||
|
||||
svsb->phase = SVSB_PHASE_INIT01;
|
||||
svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) &
|
||||
GENMASK(15, 0)) + 1;
|
||||
|
@ -644,6 +909,8 @@ static inline void svs_init02_isr_handler(struct svs_platform *svsp)
|
|||
svs_readl_relaxed(svsp, VOP30),
|
||||
svs_readl_relaxed(svsp, DCVALUES));
|
||||
|
||||
svs_save_bank_register_data(svsp, SVSB_PHASE_INIT02);
|
||||
|
||||
svsb->phase = SVSB_PHASE_INIT02;
|
||||
svsb->get_volts(svsp);
|
||||
|
||||
|
@ -655,6 +922,8 @@ static inline void svs_mon_mode_isr_handler(struct svs_platform *svsp)
|
|||
{
|
||||
struct svs_bank *svsb = svsp->pbank;
|
||||
|
||||
svs_save_bank_register_data(svsp, SVSB_PHASE_MON);
|
||||
|
||||
svsb->phase = SVSB_PHASE_MON;
|
||||
svsb->get_volts(svsp);
|
||||
|
||||
|
@ -1626,6 +1895,12 @@ static int svs_probe(struct platform_device *pdev)
|
|||
goto svs_probe_iounmap;
|
||||
}
|
||||
|
||||
ret = svs_create_debug_cmds(svsp);
|
||||
if (ret) {
|
||||
dev_err(svsp->dev, "svs create debug cmds fail: %d\n", ret);
|
||||
goto svs_probe_iounmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
svs_probe_iounmap:
|
||||
|
|
Loading…
Reference in a new issue