Commit 13f1bbcf authored by Roger Lu's avatar Roger Lu Committed by Matthias Brugger
Browse files

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: default avatarRoger Lu <roger.lu@mediatek.com>
Reviewed-by: default avatarAngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: default avatarKevin Hilman <khilman@baylibre.com>
Link: https://lore.kernel.org/r/20220516004311.18358-6-roger.lu@mediatek.com


Signed-off-by: default avatarMatthias Brugger <matthias.bgg@gmail.com>
parent 6c692719
Loading
Loading
Loading
Loading
+275 −0
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/completion.h>
#include <linux/cpuidle.h>
#include <linux/cpuidle.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
@@ -23,6 +24,7 @@
#include <linux/pm_opp.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/spinlock.h>
#include <linux/thermal.h>
#include <linux/thermal.h>
@@ -69,6 +71,39 @@


static DEFINE_SPINLOCK(svs_lock);
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
 * enum svsb_phase - svs bank phase enumeration
 * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition
 * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition
@@ -268,6 +303,7 @@ struct svs_platform_data {
 * @tzone_name: thermal zone name
 * @tzone_name: thermal zone name
 * @phase: bank current phase
 * @phase: bank current phase
 * @volt_od: bank voltage overdrive
 * @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
 * @pm_runtime_enabled_count: bank pm runtime enabled count
 * @mode_support: bank mode support.
 * @mode_support: bank mode support.
 * @freq_base: reference frequency for bank init
 * @freq_base: reference frequency for bank init
@@ -326,6 +362,7 @@ struct svs_bank {
	char *tzone_name;
	char *tzone_name;
	enum svsb_phase phase;
	enum svsb_phase phase;
	s32 volt_od;
	s32 volt_od;
	u32 reg_data[SVSB_PHASE_MAX][SVS_REG_MAX];
	u32 pm_runtime_enabled_count;
	u32 pm_runtime_enabled_count;
	u32 mode_support;
	u32 mode_support;
	u32 freq_base;
	u32 freq_base;
@@ -467,6 +504,220 @@ static int svs_adjust_pm_opp_volts(struct svs_bank *svsb)
	return ret;
	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)
static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx)
{
{
	u32 vx;
	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)
static inline void svs_error_isr_handler(struct svs_platform *svsp)
{
{
	struct svs_bank *svsb = svsp->pbank;
	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));
		svs_readl_relaxed(svsp, SMSTATE1));
	dev_err(svsb->dev, "TEMP = 0x%08x\n", svs_readl_relaxed(svsp, TEMP));
	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;
	svsb->phase = SVSB_PHASE_ERROR;
	svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
	svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
	svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
	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, VDESIGN30),
		 svs_readl_relaxed(svsp, DCVALUES));
		 svs_readl_relaxed(svsp, DCVALUES));


	svs_save_bank_register_data(svsp, SVSB_PHASE_INIT01);

	svsb->phase = SVSB_PHASE_INIT01;
	svsb->phase = SVSB_PHASE_INIT01;
	svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) &
	svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) &
				GENMASK(15, 0)) + 1;
				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, VOP30),
		 svs_readl_relaxed(svsp, DCVALUES));
		 svs_readl_relaxed(svsp, DCVALUES));


	svs_save_bank_register_data(svsp, SVSB_PHASE_INIT02);

	svsb->phase = SVSB_PHASE_INIT02;
	svsb->phase = SVSB_PHASE_INIT02;
	svsb->get_volts(svsp);
	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;
	struct svs_bank *svsb = svsp->pbank;


	svs_save_bank_register_data(svsp, SVSB_PHASE_MON);

	svsb->phase = SVSB_PHASE_MON;
	svsb->phase = SVSB_PHASE_MON;
	svsb->get_volts(svsp);
	svsb->get_volts(svsp);


@@ -1626,6 +1895,12 @@ static int svs_probe(struct platform_device *pdev)
		goto svs_probe_iounmap;
		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;
	return 0;


svs_probe_iounmap:
svs_probe_iounmap: