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: Chunfeng Yun <chunfeng.yun@mediatek.com> Link: https://lore.kernel.org/r/20230513092218.21139-2-chunfeng.yun@mediatek.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
9169477206
commit
e45076007e
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user