From: Chunfeng Yun <chunfeng.yun@mediatek.com>
To: Vinod Koul <vkoul@kernel.org>
Cc: Chunfeng Yun <chunfeng.yun@mediatek.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
<linux-arm-kernel@lists.infradead.org>,
<linux-mediatek@lists.infradead.org>,
<linux-phy@lists.infradead.org>, <linux-kernel@vger.kernel.org>,
Eddie Hung <eddie.hung@mediatek.com>
Subject: [PATCH v9 2/2] phy: mediatek: tphy: add debugfs files
Date: Thu, 16 Mar 2023 15:14:52 +0800 [thread overview]
Message-ID: <20230316071452.9161-2-chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <20230316071452.9161-1-chunfeng.yun@mediatek.com>
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>
---
v9: 1. use the debugfs entry of struct phy instead of private one suggested by Vinod;
2. modify changelog of v8 due to misunderstanding the means of Vinod
v8: abandon patch to create phy debug root file /sys/kernel/debug/phy/
v6~v7: no changes
v5: using common debugfs config CONFIG_DEBUG_FS
v4: fix build warning of sometimes uninitialized variable
v3: fix typo of "debugfs" suggested by AngeloGioacchino
v2: add CONFIG_PHY_MTK_TPHY_DEBUGFS suggested by AngeloGioacchino
---
drivers/phy/mediatek/phy-mtk-tphy.c | 356 ++++++++++++++++++++++++++++
1 file changed, 356 insertions(+)
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index e906a82791bd..0d110e50bbfd 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -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;
}
--
2.18.0
WARNING: multiple messages have this Message-ID (diff)
From: Chunfeng Yun <chunfeng.yun@mediatek.com>
To: Vinod Koul <vkoul@kernel.org>
Cc: Chunfeng Yun <chunfeng.yun@mediatek.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
<linux-arm-kernel@lists.infradead.org>,
<linux-mediatek@lists.infradead.org>,
<linux-phy@lists.infradead.org>, <linux-kernel@vger.kernel.org>,
Eddie Hung <eddie.hung@mediatek.com>
Subject: [PATCH v9 2/2] phy: mediatek: tphy: add debugfs files
Date: Thu, 16 Mar 2023 15:14:52 +0800 [thread overview]
Message-ID: <20230316071452.9161-2-chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <20230316071452.9161-1-chunfeng.yun@mediatek.com>
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>
---
v9: 1. use the debugfs entry of struct phy instead of private one suggested by Vinod;
2. modify changelog of v8 due to misunderstanding the means of Vinod
v8: abandon patch to create phy debug root file /sys/kernel/debug/phy/
v6~v7: no changes
v5: using common debugfs config CONFIG_DEBUG_FS
v4: fix build warning of sometimes uninitialized variable
v3: fix typo of "debugfs" suggested by AngeloGioacchino
v2: add CONFIG_PHY_MTK_TPHY_DEBUGFS suggested by AngeloGioacchino
---
drivers/phy/mediatek/phy-mtk-tphy.c | 356 ++++++++++++++++++++++++++++
1 file changed, 356 insertions(+)
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index e906a82791bd..0d110e50bbfd 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -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;
}
--
2.18.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
WARNING: multiple messages have this Message-ID (diff)
From: Chunfeng Yun <chunfeng.yun@mediatek.com>
To: Vinod Koul <vkoul@kernel.org>
Cc: Chunfeng Yun <chunfeng.yun@mediatek.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
<linux-arm-kernel@lists.infradead.org>,
<linux-mediatek@lists.infradead.org>,
<linux-phy@lists.infradead.org>, <linux-kernel@vger.kernel.org>,
Eddie Hung <eddie.hung@mediatek.com>
Subject: [PATCH v9 2/2] phy: mediatek: tphy: add debugfs files
Date: Thu, 16 Mar 2023 15:14:52 +0800 [thread overview]
Message-ID: <20230316071452.9161-2-chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <20230316071452.9161-1-chunfeng.yun@mediatek.com>
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>
---
v9: 1. use the debugfs entry of struct phy instead of private one suggested by Vinod;
2. modify changelog of v8 due to misunderstanding the means of Vinod
v8: abandon patch to create phy debug root file /sys/kernel/debug/phy/
v6~v7: no changes
v5: using common debugfs config CONFIG_DEBUG_FS
v4: fix build warning of sometimes uninitialized variable
v3: fix typo of "debugfs" suggested by AngeloGioacchino
v2: add CONFIG_PHY_MTK_TPHY_DEBUGFS suggested by AngeloGioacchino
---
drivers/phy/mediatek/phy-mtk-tphy.c | 356 ++++++++++++++++++++++++++++
1 file changed, 356 insertions(+)
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index e906a82791bd..0d110e50bbfd 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -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;
}
--
2.18.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2023-03-16 7:15 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-03-16 7:14 [PATCH v9 1/2] phy: core: add debugfs files Chunfeng Yun
2023-03-16 7:14 ` Chunfeng Yun
2023-03-16 7:14 ` Chunfeng Yun
2023-03-16 7:14 ` Chunfeng Yun [this message]
2023-03-16 7:14 ` [PATCH v9 2/2] phy: mediatek: tphy: " Chunfeng Yun
2023-03-16 7:14 ` Chunfeng Yun
2023-04-03 15:06 ` [PATCH v9 1/2] phy: core: " Vinod Koul
2023-04-03 15:06 ` Vinod Koul
2023-04-03 15:06 ` Vinod Koul
2023-04-07 5:30 ` Chunfeng Yun (云春峰)
2023-04-07 5:30 ` Chunfeng Yun (云春峰)
2023-04-07 5:30 ` Chunfeng Yun (云春峰)
2023-04-07 5:33 ` Chunfeng Yun (云春峰)
2023-04-07 5:33 ` Chunfeng Yun (云春峰)
2023-04-07 5:33 ` Chunfeng Yun (云春峰)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230316071452.9161-2-chunfeng.yun@mediatek.com \
--to=chunfeng.yun@mediatek.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=eddie.hung@mediatek.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-phy@lists.infradead.org \
--cc=matthias.bgg@gmail.com \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.