phy: mediatek: tphy: add debugfs files
authorChunfeng Yun <chunfeng.yun@mediatek.com>
Sat, 13 May 2023 09:22:18 +0000 (17:22 +0800)
committerVinod Koul <vkoul@kernel.org>
Fri, 19 May 2023 17:53:52 +0000 (23:23 +0530)
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>
drivers/phy/mediatek/phy-mtk-tphy.c

index e906a82..0d110e5 100644 (file)
@@ -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>
 
 #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;
 }