Commit e4507600 authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Vinod Koul
Browse files

phy: mediatek: tphy: add debugfs files



These debugfs files are mainly used to make eye diagram test easier,
especially helpful to do HQA test for a new IC without efuse enabled.

Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/20230513092218.21139-2-chunfeng.yun@mediatek.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 91694772
Loading
Loading
Loading
Loading
+356 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
@@ -264,6 +265,8 @@

#define TPHY_CLKS_CNT	2

#define USER_BUF_LEN(count) min_t(size_t, 8, (count))

enum mtk_phy_version {
	MTK_PHY_V1 = 1,
	MTK_PHY_V2,
@@ -336,6 +339,358 @@ struct mtk_tphy {
	int src_coef; /* coefficient for slew rate calibrate */
};

#if IS_ENABLED(CONFIG_DEBUG_FS)

enum u2_phy_params {
	U2P_EYE_VRT = 0,
	U2P_EYE_TERM,
	U2P_EFUSE_EN,
	U2P_EFUSE_INTR,
	U2P_DISCTH,
	U2P_PRE_EMPHASIS,
};

enum u3_phy_params {
	U3P_EFUSE_EN = 0,
	U3P_EFUSE_INTR,
	U3P_EFUSE_TX_IMP,
	U3P_EFUSE_RX_IMP,
};

static const char *const u2_phy_files[] = {
	[U2P_EYE_VRT] = "vrt",
	[U2P_EYE_TERM] = "term",
	[U2P_EFUSE_EN] = "efuse",
	[U2P_EFUSE_INTR] = "intr",
	[U2P_DISCTH] = "discth",
	[U2P_PRE_EMPHASIS] = "preemph",
};

static const char *const u3_phy_files[] = {
	[U3P_EFUSE_EN] = "efuse",
	[U3P_EFUSE_INTR] = "intr",
	[U3P_EFUSE_TX_IMP] = "tx-imp",
	[U3P_EFUSE_RX_IMP] = "rx-imp",
};

static int u2_phy_params_show(struct seq_file *sf, void *unused)
{
	struct mtk_phy_instance *inst = sf->private;
	const char *fname = file_dentry(sf->file)->d_iname;
	struct u2phy_banks *u2_banks = &inst->u2_banks;
	void __iomem *com = u2_banks->com;
	u32 max = 0;
	u32 tmp = 0;
	u32 val = 0;
	int ret;

	ret = match_string(u2_phy_files, ARRAY_SIZE(u2_phy_files), fname);
	if (ret < 0)
		return ret;

	switch (ret) {
	case U2P_EYE_VRT:
		tmp = readl(com + U3P_USBPHYACR1);
		val = FIELD_GET(PA1_RG_VRT_SEL, tmp);
		max = FIELD_MAX(PA1_RG_VRT_SEL);
		break;

	case U2P_EYE_TERM:
		tmp = readl(com + U3P_USBPHYACR1);
		val = FIELD_GET(PA1_RG_TERM_SEL, tmp);
		max = FIELD_MAX(PA1_RG_TERM_SEL);
		break;

	case U2P_EFUSE_EN:
		if (u2_banks->misc) {
			tmp = readl(u2_banks->misc + U3P_MISC_REG1);
			max = 1;
		}

		val = !!(tmp & MR1_EFUSE_AUTO_LOAD_DIS);
		break;

	case U2P_EFUSE_INTR:
		tmp = readl(com + U3P_USBPHYACR1);
		val = FIELD_GET(PA1_RG_INTR_CAL, tmp);
		max = FIELD_MAX(PA1_RG_INTR_CAL);
		break;

	case U2P_DISCTH:
		tmp = readl(com + U3P_USBPHYACR6);
		val = FIELD_GET(PA6_RG_U2_DISCTH, tmp);
		max = FIELD_MAX(PA6_RG_U2_DISCTH);
		break;

	case U2P_PRE_EMPHASIS:
		tmp = readl(com + U3P_USBPHYACR6);
		val = FIELD_GET(PA6_RG_U2_PRE_EMP, tmp);
		max = FIELD_MAX(PA6_RG_U2_PRE_EMP);
		break;

	default:
		seq_printf(sf, "invalid, %d\n", ret);
		break;
	}

	seq_printf(sf, "%s : %d [0, %d]\n", fname, val, max);

	return 0;
}

static int u2_phy_params_open(struct inode *inode, struct file *file)
{
	return single_open(file, u2_phy_params_show, inode->i_private);
}

static ssize_t u2_phy_params_write(struct file *file, const char __user *ubuf,
				   size_t count, loff_t *ppos)
{
	const char *fname = file_dentry(file)->d_iname;
	struct seq_file *sf = file->private_data;
	struct mtk_phy_instance *inst = sf->private;
	struct u2phy_banks *u2_banks = &inst->u2_banks;
	void __iomem *com = u2_banks->com;
	ssize_t rc;
	u32 val;
	int ret;

	rc = kstrtouint_from_user(ubuf, USER_BUF_LEN(count), 0, &val);
	if (rc)
		return rc;

	ret = match_string(u2_phy_files, ARRAY_SIZE(u2_phy_files), fname);
	if (ret < 0)
		return (ssize_t)ret;

	switch (ret) {
	case U2P_EYE_VRT:
		mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL, val);
		break;

	case U2P_EYE_TERM:
		mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL, val);
		break;

	case U2P_EFUSE_EN:
		if (u2_banks->misc)
			mtk_phy_update_field(u2_banks->misc + U3P_MISC_REG1,
					     MR1_EFUSE_AUTO_LOAD_DIS, !!val);
		break;

	case U2P_EFUSE_INTR:
		mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, val);
		break;

	case U2P_DISCTH:
		mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH, val);
		break;

	case U2P_PRE_EMPHASIS:
		mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_PRE_EMP, val);
		break;

	default:
		break;
	}

	return count;
}

static const struct file_operations u2_phy_fops = {
	.open = u2_phy_params_open,
	.write = u2_phy_params_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};

static void u2_phy_dbgfs_files_create(struct mtk_phy_instance *inst)
{
	u32 count = ARRAY_SIZE(u2_phy_files);
	int i;

	for (i = 0; i < count; i++)
		debugfs_create_file(u2_phy_files[i], 0644, inst->phy->debugfs,
				    inst, &u2_phy_fops);
}

static int u3_phy_params_show(struct seq_file *sf, void *unused)
{
	struct mtk_phy_instance *inst = sf->private;
	const char *fname = file_dentry(sf->file)->d_iname;
	struct u3phy_banks *u3_banks = &inst->u3_banks;
	u32 val = 0;
	u32 max = 0;
	u32 tmp;
	int ret;

	ret = match_string(u3_phy_files, ARRAY_SIZE(u3_phy_files), fname);
	if (ret < 0)
		return ret;

	switch (ret) {
	case U3P_EFUSE_EN:
		tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV);
		val = !!(tmp & P3D_RG_EFUSE_AUTO_LOAD_DIS);
		max = 1;
		break;

	case U3P_EFUSE_INTR:
		tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0);
		val = FIELD_GET(P3A_RG_IEXT_INTR, tmp);
		max = FIELD_MAX(P3A_RG_IEXT_INTR);
		break;

	case U3P_EFUSE_TX_IMP:
		tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0);
		val = FIELD_GET(P3D_RG_TX_IMPEL, tmp);
		max = FIELD_MAX(P3D_RG_TX_IMPEL);
		break;

	case U3P_EFUSE_RX_IMP:
		tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1);
		val = FIELD_GET(P3D_RG_RX_IMPEL, tmp);
		max = FIELD_MAX(P3D_RG_RX_IMPEL);
		break;

	default:
		seq_printf(sf, "invalid, %d\n", ret);
		break;
	}

	seq_printf(sf, "%s : %d [0, %d]\n", fname, val, max);

	return 0;
}

static int u3_phy_params_open(struct inode *inode, struct file *file)
{
	return single_open(file, u3_phy_params_show, inode->i_private);
}

static ssize_t u3_phy_params_write(struct file *file, const char __user *ubuf,
				   size_t count, loff_t *ppos)
{
	const char *fname = file_dentry(file)->d_iname;
	struct seq_file *sf = file->private_data;
	struct mtk_phy_instance *inst = sf->private;
	struct u3phy_banks *u3_banks = &inst->u3_banks;
	void __iomem *phyd = u3_banks->phyd;
	ssize_t rc;
	u32 val;
	int ret;

	rc = kstrtouint_from_user(ubuf, USER_BUF_LEN(count), 0, &val);
	if (rc)
		return rc;

	ret = match_string(u3_phy_files, ARRAY_SIZE(u3_phy_files), fname);
	if (ret < 0)
		return (ssize_t)ret;

	switch (ret) {
	case U3P_EFUSE_EN:
		mtk_phy_update_field(phyd + U3P_U3_PHYD_RSV,
				     P3D_RG_EFUSE_AUTO_LOAD_DIS, !!val);
		break;

	case U3P_EFUSE_INTR:
		mtk_phy_update_field(u3_banks->phya + U3P_U3_PHYA_REG0,
				     P3A_RG_IEXT_INTR, val);
		break;

	case U3P_EFUSE_TX_IMP:
		mtk_phy_update_field(phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL, val);
		mtk_phy_set_bits(phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL);
		break;

	case U3P_EFUSE_RX_IMP:
		mtk_phy_update_field(phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL, val);
		mtk_phy_set_bits(phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL);
		break;

	default:
		break;
	}

	return count;
}

static const struct file_operations u3_phy_fops = {
	.open = u3_phy_params_open,
	.write = u3_phy_params_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};

static void u3_phy_dbgfs_files_create(struct mtk_phy_instance *inst)
{
	u32 count = ARRAY_SIZE(u3_phy_files);
	int i;

	for (i = 0; i < count; i++)
		debugfs_create_file(u3_phy_files[i], 0644, inst->phy->debugfs,
				    inst, &u3_phy_fops);
}

static int phy_type_show(struct seq_file *sf, void *unused)
{
	struct mtk_phy_instance *inst = sf->private;
	const char *type;

	switch (inst->type) {
	case PHY_TYPE_USB2:
		type = "USB2";
		break;
	case PHY_TYPE_USB3:
		type = "USB3";
		break;
	case PHY_TYPE_PCIE:
		type = "PCIe";
		break;
	case PHY_TYPE_SGMII:
		type = "SGMII";
		break;
	case PHY_TYPE_SATA:
		type = "SATA";
		break;
	default:
		type = "";
	}

	seq_printf(sf, "%s\n", type);

	return 0;
}
DEFINE_SHOW_ATTRIBUTE(phy_type);

/* these files will be removed when phy is released by phy core */
static void phy_debugfs_init(struct mtk_phy_instance *inst)
{
	debugfs_create_file("type", 0444, inst->phy->debugfs, inst, &phy_type_fops);

	switch (inst->type) {
	case PHY_TYPE_USB2:
		u2_phy_dbgfs_files_create(inst);
		break;
	case PHY_TYPE_USB3:
	case PHY_TYPE_PCIE:
		u3_phy_dbgfs_files_create(inst);
		break;
	default:
		break;
	}
}

#else

static void phy_debugfs_init(struct mtk_phy_instance *inst)
{}

#endif

static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
	struct mtk_phy_instance *instance)
{
@@ -1140,6 +1495,7 @@ static struct phy *mtk_phy_xlate(struct device *dev,

	phy_parse_property(tphy, instance);
	phy_type_set(instance);
	phy_debugfs_init(instance);

	return instance->phy;
}