* [PATCH v2 05/12] phy: phy-mtk-dp: Implement power_on and power_off PHY callbacks
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
Add .power_on() and .power_off() callbacks to mtk_dp_phy_dev_ops
to be able to call those with phy_power_on() and phy_power_off()
API in the DisplayPort driver to be able to stop using all of
those hardcoded register writes in that external driver.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 82 +++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index 915f148325c4..0c3f06791613 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -20,6 +20,9 @@
#define MTK_DP_PHY_MAX_LANES 4
+/* DP_PHYA_GLB_FORCE_CTRL_1 */
+#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
+
/* DP_PHYD_PLL_CTL_1 */
#define TPLL_SSC_EN BIT(3)
@@ -37,6 +40,11 @@
# define DP_GLB_SW_RST_XTAL_CLK BIT(2)
# define DP_GLB_SW_RST_MAIN_LINK BIT(3)
+/* DP_PHYD_AUX_RX_CTL */
+#define PHYD_DIG_DPAUX_RX_EN BIT(0)
+#define PHYD_DIG_XTP_GLB_CKDET_EN BIT(1)
+#define PHYD_DIG_DPAUX_RX_DEGLITCH_EN BIT(2)
+
#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
@@ -83,6 +91,12 @@
#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+enum mtk_dp_phya_ana_glb_regidx {
+ DP_PHYA_GLB_FORCE_CTRL_0,
+ DP_PHYA_GLB_FORCE_CTRL_1,
+ DP_PHYA_GLOBAL_MAX
+};
+
enum mtk_dp_phyd_dig_lane_regidx {
DP_PHYD_LAN_DRIVING_PARAM_0,
DP_PHYD_LAN_MAX
@@ -93,9 +107,15 @@ enum mtk_dp_phyd_dig_glb_regidx {
DP_PHYD_PLL_CTL_1,
DP_PHYD_SW_RST,
DP_PHYD_BIT_RATE,
+ DP_PHYD_AUX_RX_CTL,
DP_PHYD_GLOBAL_MAX
};
+static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
+ [DP_PHYA_GLB_FORCE_CTRL_0] = 0x30,
+ [DP_PHYA_GLB_FORCE_CTRL_1] = 0x34,
+};
+
static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
[DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
};
@@ -105,21 +125,26 @@ static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_PLL_CTL_1] = 0x14,
[DP_PHYD_SW_RST] = 0x38,
[DP_PHYD_BIT_RATE] = 0x3c,
+ [DP_PHYD_AUX_RX_CTL] = 0x40,
};
/**
* struct mtk_dp_phy_pdata - Platform data and defaults for MediaTek DP/eDP PHY
+ * @off_ana_glb: Base offset for dptx_phyd_sifslv_ana_glb
* @off_dig_glb: Base offset for dptx_phyd_sifslv_dig_glb
* @off_dig_lane: Base offsets for dptx_phyd_sifslv_dig_lan (for each lane)
+ * @regs_ana_glb: Register (layout) offsets for ana_glb
* @regs_dig_glb: Register (layout) offsets for dig_glb
* @regs_dig_lane: Register (layout) offsets for dig_lan
*/
struct mtk_dp_phy_pdata {
/* Register offsets */
+ u16 off_ana_glb;
u16 off_dig_glb;
u16 off_dig_lane[MTK_DP_PHY_MAX_LANES];
/* Register maps */
+ const u8 *regs_ana_glb;
const u8 *regs_dig_glb;
const u8 *regs_dig_lane;
};
@@ -200,6 +225,59 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
return 0;
}
+static int mtk_dp_phy_power_on(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs_dig = pdata->regs_dig_glb;
+ const u8 *regs_ana = pdata->regs_ana_glb;
+ int ret;
+
+ /* Enable AUX Channel with RX De-Glitch and input clock detection */
+ ret = regmap_write(dp_phy->regmap,
+ pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL],
+ PHYD_DIG_DPAUX_RX_EN |
+ PHYD_DIG_XTP_GLB_CKDET_EN |
+ PHYD_DIG_DPAUX_RX_DEGLITCH_EN);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
+ CKM_CKTX0_EN_FORCE_MODE);
+ if (ret) {
+ /* Shut down AUX channel */
+ regmap_write(dp_phy->regmap,
+ pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL], 0);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_dp_phy_power_off(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs_dig = pdata->regs_dig_glb;
+ const u8 *regs_ana = pdata->regs_ana_glb;
+ int ret;
+
+ ret = regmap_set_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
+ CKM_CKTX0_EN_FORCE_MODE);
+ if (ret)
+ return ret;
+
+ /* Disable RX */
+ ret = regmap_write(dp_phy->regmap,
+ pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL], 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int mtk_dp_phy_reset(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
@@ -220,6 +298,8 @@ static int mtk_dp_phy_reset(struct phy *phy)
static const struct phy_ops mtk_dp_phy_dev_ops = {
.init = mtk_dp_phy_init,
+ .power_on = mtk_dp_phy_power_on,
+ .power_off = mtk_dp_phy_power_off,
.configure = mtk_dp_phy_configure,
.reset = mtk_dp_phy_reset,
.owner = THIS_MODULE,
@@ -319,8 +399,10 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
}
static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
+ .off_ana_glb = 0x0,
.off_dig_glb = 0x1000,
.off_dig_lane = (const u16[]) { 0x1100, 0x1200, 0x1300, 0x1400 },
+ .regs_ana_glb = mt8195_phy_ana_glb_regs,
.regs_dig_glb = mt8195_phy_dig_glb_regs,
.regs_dig_lane = mt8195_phy_dig_lane_regs,
};
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 04/12] phy: phy-mtk-dp: Migrate register offsets to SoC specific pdata
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
In preparation for adding support for newer SoCs and for adding
more capabilities to this driver in an efficient manner, migrate
all of the hardcoded register offsets to SoC specific pdata and
assign that for both DT and platform probing.
While at it also cleanup writing the driving parameters to the PHY
by iterating through all lanes with a loop instead.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 138 +++++++++++++++++++++++-------
1 file changed, 109 insertions(+), 29 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index b042ab67ebaf..915f148325c4 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -4,6 +4,10 @@
*
* Copyright (c) 2022, BayLibre Inc.
* Copyright (c) 2022, MediaTek Inc.
+ *
+ * Major refactoring
+ * Copyright (c) 2026, Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/delay.h>
@@ -14,24 +18,25 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
-#define PHY_OFFSET 0x1000
+#define MTK_DP_PHY_MAX_LANES 4
-#define MTK_DP_PHY_DIG_PLL_CTL_1 (PHY_OFFSET + 0x14)
+/* DP_PHYD_PLL_CTL_1 */
#define TPLL_SSC_EN BIT(3)
-#define MTK_DP_PHY_DIG_BIT_RATE (PHY_OFFSET + 0x3C)
-#define BIT_RATE_RBR 0
-#define BIT_RATE_HBR 1
-#define BIT_RATE_HBR2 2
-#define BIT_RATE_HBR3 3
+/* DP_PHYD_BIT_RATE */
+#define PHYD_DIG_RG_BIT_RATE GENMASK(1, 0)
+# define BIT_RATE_RBR 0
+# define BIT_RATE_HBR 1
+# define BIT_RATE_HBR2 2
+# define BIT_RATE_HBR3 3
-#define MTK_DP_PHY_DIG_SW_RST (PHY_OFFSET + 0x38)
-#define DP_GLB_SW_RST_PHYD BIT(0)
+/* DP_PHYD_SW_RST */
+#define PHYD_DIG_GLB_SW_RST_B GENMASK(7, 0)
+# define DP_GLB_SW_RST_PHYD BIT(0)
+# define DP_GLB_SW_RST_TFIFO_ANA BIT(1)
+# define DP_GLB_SW_RST_XTAL_CLK BIT(2)
+# define DP_GLB_SW_RST_MAIN_LINK BIT(3)
-#define MTK_DP_LANE0_DRIVING_PARAM_3 (PHY_OFFSET + 0x138)
-#define MTK_DP_LANE1_DRIVING_PARAM_3 (PHY_OFFSET + 0x238)
-#define MTK_DP_LANE2_DRIVING_PARAM_3 (PHY_OFFSET + 0x338)
-#define MTK_DP_LANE3_DRIVING_PARAM_3 (PHY_OFFSET + 0x438)
#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
@@ -78,14 +83,58 @@
#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+enum mtk_dp_phyd_dig_lane_regidx {
+ DP_PHYD_LAN_DRIVING_PARAM_0,
+ DP_PHYD_LAN_MAX
+};
+
+enum mtk_dp_phyd_dig_glb_regidx {
+ DP_PHYD_PLL_CTL_0,
+ DP_PHYD_PLL_CTL_1,
+ DP_PHYD_SW_RST,
+ DP_PHYD_BIT_RATE,
+ DP_PHYD_GLOBAL_MAX
+};
+
+static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
+ [DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
+};
+
+static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
+ [DP_PHYD_PLL_CTL_0] = 0x10,
+ [DP_PHYD_PLL_CTL_1] = 0x14,
+ [DP_PHYD_SW_RST] = 0x38,
+ [DP_PHYD_BIT_RATE] = 0x3c,
+};
+
+/**
+ * struct mtk_dp_phy_pdata - Platform data and defaults for MediaTek DP/eDP PHY
+ * @off_dig_glb: Base offset for dptx_phyd_sifslv_dig_glb
+ * @off_dig_lane: Base offsets for dptx_phyd_sifslv_dig_lan (for each lane)
+ * @regs_dig_glb: Register (layout) offsets for dig_glb
+ * @regs_dig_lane: Register (layout) offsets for dig_lan
+ */
+struct mtk_dp_phy_pdata {
+ /* Register offsets */
+ u16 off_dig_glb;
+ u16 off_dig_lane[MTK_DP_PHY_MAX_LANES];
+
+ /* Register maps */
+ const u8 *regs_dig_glb;
+ const u8 *regs_dig_lane;
+};
+
struct mtk_dp_phy {
struct device *dev;
struct regmap *regmap;
+ const struct mtk_dp_phy_pdata *pdata;
};
static int mtk_dp_phy_init(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u32 reg = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_PARAM_0];
static const u32 driving_params[] = {
DRIVING_PARAM_3_DEFAULT,
DRIVING_PARAM_4_DEFAULT,
@@ -94,15 +143,21 @@ static int mtk_dp_phy_init(struct phy *phy)
DRIVING_PARAM_7_DEFAULT,
DRIVING_PARAM_8_DEFAULT
};
-
- regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE0_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE1_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE2_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE3_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
+ int i, ret;
+
+ /*
+ * Assume that all lanes need the same driving parameters: this
+ * will bulk write from DRIVING_PARAM_0 to DRIVING_PARAM_8 on
+ * all lanes (a grand total of [9 * num_lanes] 32-bit writes)
+ */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ ret = regmap_bulk_write(dp_phy->regmap,
+ pdata->off_dig_lane[i] + reg,
+ driving_params,
+ ARRAY_SIZE(driving_params));
+ if (ret)
+ return ret;
+ };
return 0;
}
@@ -110,9 +165,12 @@ static int mtk_dp_phy_init(struct phy *phy)
static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
u32 val;
if (opts->dp.set_rate) {
+ const u32 reg_bit_rate = pdata->regs_dig_glb[DP_PHYD_BIT_RATE];
+
switch (opts->dp.link_rate) {
default:
dev_err(&phy->dev,
@@ -132,10 +190,11 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
val = BIT_RATE_HBR3;
break;
}
- regmap_write(dp_phy->regmap, MTK_DP_PHY_DIG_BIT_RATE, val);
+ regmap_write(dp_phy->regmap, pdata->off_dig_glb + reg_bit_rate, val);
}
- regmap_update_bits(dp_phy->regmap, MTK_DP_PHY_DIG_PLL_CTL_1,
+ regmap_update_bits(dp_phy->regmap,
+ pdata->off_dig_glb + pdata->regs_dig_glb[DP_PHYD_PLL_CTL_1],
TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
return 0;
@@ -144,12 +203,17 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
static int mtk_dp_phy_reset(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u32 reg_rst = pdata->regs_dig_glb[DP_PHYD_SW_RST];
+
+ /* Clearing bits sets reset state */
+ regmap_clear_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
- regmap_update_bits(dp_phy->regmap, MTK_DP_PHY_DIG_SW_RST,
- DP_GLB_SW_RST_PHYD, 0);
+ /* PHYD needs 50uS to guarantee reset done */
usleep_range(50, 200);
- regmap_update_bits(dp_phy->regmap, MTK_DP_PHY_DIG_SW_RST,
- DP_GLB_SW_RST_PHYD, 1);
+
+ /* Setting bits means go out of reset */
+ regmap_set_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
return 0;
}
@@ -169,12 +233,19 @@ static void mtk_dp_phy_legacy_remove_lookup(void *data)
phy_remove_lookup(phy, "dp", dev_name(dp_phy->dev));
}
+static const struct mtk_dp_phy_pdata mt8195_dp_phy_data;
+
static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_phy *dp_phy)
{
struct device *dev = &pdev->dev;
struct phy *phy;
int ret;
+ /*
+ * If legacy platform driver probe, assume this is MT8195 or compatible
+ * with a devicetree that was not migrated to the new, proper bindings.
+ */
+ dp_phy->pdata = &mt8195_dp_phy_data;
dp_phy->regmap = *(struct regmap **)dev->platform_data;
if (!dp_phy->regmap)
return dev_err_probe(dev, -EINVAL, "No platform data available\n");
@@ -227,6 +298,8 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
if (IS_ERR(dp_phy->regmap))
return PTR_ERR(dp_phy->regmap);
+ dp_phy->pdata = device_get_match_data(dev);
+
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
@@ -245,8 +318,15 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
return 0;
}
+static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
+ .off_dig_glb = 0x1000,
+ .off_dig_lane = (const u16[]) { 0x1100, 0x1200, 0x1300, 0x1400 },
+ .regs_dig_glb = mt8195_phy_dig_glb_regs,
+ .regs_dig_lane = mt8195_phy_dig_lane_regs,
+};
+
static const struct of_device_id mtk_dp_phy_of_match[] = {
- { .compatible = "mediatek,mt8195-dp-phy" },
+ { .compatible = "mediatek,mt8195-dp-phy", .data = &mt8195_dp_phy_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_dp_phy_of_match);
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 08/12] phy: phy-mtk-dp: Add support for digital and analog calibration
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
Add support for reading the calibration values from eFuse: if
present, write those - otherwise, rely on the defaults from
SoC-specific data.
This also adds support for writing the calibration values for
the analog part of the PHY.
Note that before this change, only default hardcoded calibration
values were supported for the digital driving parameters.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 296 ++++++++++++++++++++++++++++--
1 file changed, 277 insertions(+), 19 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index 270825f130a9..d0ef8e8f6670 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
@@ -20,6 +21,19 @@
#define MTK_DP_PHY_MAX_LANES 4
+/* DP_PHYA_GLB_BIAS_GEN_0 (PHYA - Analog) */
+#define XTP_GLB_BIAS_INT_R_CTRL GENMASK(20, 16)
+
+/* DP_PHYA_GLB_FORCE_CTRL_1 */
+#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
+
+/* DP_PHYA_GLB_DPAUX_TX */
+#define CKM_PT0_CKTX_IMPSEL GENMASK(23, 20)
+
+/* DP_PHYA_LAN_LANE_TX_0 */
+#define XTP_LN_TX_IMPSEL_PMOS GENMASK(15, 12)
+#define XTP_LN_TX_IMPSEL_NMOS GENMASK(19, 16)
+
/* DP_PHYA_GLB_FORCE_CTRL_1 */
#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
@@ -53,11 +67,29 @@
#define PHYD_DP_TX_FORCE_VOLT_SWING_VAL GENMASK(2, 1)
#define PHYD_DP_TX_FORCE_PRE_EMPH_VAL GENMASK(4, 3)
+/*
+ * DRIVING_PARAM_X (PHYD - Digital)
+ *
+ * Driving param registers are split in three sets, all containing settings
+ * for Voltage Swing and Pre-Emphasis for each lane's differential pair.
+ *
+ * All three sets share the same layout, but for different physical signals;
+ * In particular:
+ * [0-2]: LC TX CM (Minus / Negative Edge)
+ * [3-5]: LC TX C (Logic State Change Point)
+ * [6-8]: LC TX CP (Plus / Positive Edge)
+ *
+ * And they contain values for:
+ * [0,3,6]: Swing 0 Pre[0-3]
+ * [1,4,7]: Swing 1 Pre[0-2] and Swing 2 Pre0
+ * [2,5,8]: Swing 2 Pre1 and Swing 3 Pre0
+ */
+#define PHYD_DIG_NUM_DRV_PARA_REGS 9
#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
#define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29)
-#define DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+#define MT8195_DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
@@ -66,21 +98,21 @@
#define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9)
#define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21))
#define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29)
-#define DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+#define MT8195_DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
#define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5))
#define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12)
-#define DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+#define MT8195_DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
#define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0
#define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10)
#define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19)
#define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28)
-#define DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+#define MT8195_DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
@@ -89,22 +121,30 @@
#define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9)
#define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18)
#define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0
-#define DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+#define MT8195_DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
#define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3)
#define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0
-#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+#define MT8195_DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
enum mtk_dp_phya_ana_glb_regidx {
+ DP_PHYA_GLB_BIAS_GEN_0,
+ DP_PHYA_GLB_BIAS_GEN_1,
+ DP_PHYA_GLB_DPAUX_TX,
DP_PHYA_GLB_FORCE_CTRL_0,
DP_PHYA_GLB_FORCE_CTRL_1,
DP_PHYA_GLOBAL_MAX
};
+enum mtk_dp_phya_ana_lane_regidx {
+ DP_PHYA_LAN_LANE_TX_0,
+ DP_PHYA_LAN_MAX
+};
+
enum mtk_dp_phyd_dig_lane_regidx {
DP_PHYD_LAN_DRIVING_FORCE,
DP_PHYD_LAN_DRIVING_PARAM_0,
@@ -122,10 +162,17 @@ enum mtk_dp_phyd_dig_glb_regidx {
};
static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
+ [DP_PHYA_GLB_BIAS_GEN_0] = 0x0,
+ [DP_PHYA_GLB_BIAS_GEN_1] = 0x4,
+ [DP_PHYA_GLB_DPAUX_TX] = 0x8,
[DP_PHYA_GLB_FORCE_CTRL_0] = 0x30,
[DP_PHYA_GLB_FORCE_CTRL_1] = 0x34,
};
+static const u8 mt8195_phy_ana_lane_regs[DP_PHYA_LAN_MAX] = {
+ [DP_PHYA_LAN_LANE_TX_0] = 0x4,
+};
+
static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
[DP_PHYD_LAN_DRIVING_FORCE] = 0x18,
[DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
@@ -140,46 +187,101 @@ static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_TX_CTL_0] = 0x44,
};
+/**
+ * struct mtk_dp_phya_imp_sel - Per-Lane Impedance Selection
+ * @pmos: Impedance selection for P-Channel MOSFET
+ * @nmos: Impedance selection for N-Channel MOSFET
+ */
+struct mtk_dp_phya_imp_sel {
+ u8 pmos : 4;
+ u8 nmos : 4;
+};
+
/**
* struct mtk_dp_phy_pdata - Platform data and defaults for MediaTek DP/eDP PHY
* @off_ana_glb: Base offset for dptx_phyd_sifslv_ana_glb
+ * @off_ana_lane: Base offsets for dptx_phyd_sifslv_ana_lan (for each lane)
* @off_dig_glb: Base offset for dptx_phyd_sifslv_dig_glb
* @off_dig_lane: Base offsets for dptx_phyd_sifslv_dig_lan (for each lane)
* @regs_ana_glb: Register (layout) offsets for ana_glb
+ * @regs_ana_lane: Register (layout) offsets for ana_lan
* @regs_dig_glb: Register (layout) offsets for dig_glb
* @regs_dig_lane: Register (layout) offsets for dig_lan
+ * @ana_bias_r: Internal resistance "R" Selection Settings (global)
+ * @ana_cktx_imp: TX Clock Impedance Selection Settings (global)
+ * @ana_lanes_imp: TX Impedance Selection Settings (for all lanes)
+ * @driving_params: Voltage Swing and Pre-Emphasis settings (for all lanes)
*/
struct mtk_dp_phy_pdata {
/* Register offsets */
u16 off_ana_glb;
+ u16 off_ana_lane[MTK_DP_PHY_MAX_LANES];
u16 off_dig_glb;
u16 off_dig_lane[MTK_DP_PHY_MAX_LANES];
/* Register maps */
const u8 *regs_ana_glb;
+ const u8 *regs_ana_lane;
const u8 *regs_dig_glb;
const u8 *regs_dig_lane;
+
+ /* Calibration defaults */
+ u8 ana_bias_r;
+ u8 ana_cktx_imp;
+ struct mtk_dp_phya_imp_sel ana_lanes_imp;
+ u32 driving_params[PHYD_DIG_NUM_DRV_PARA_REGS];
};
struct mtk_dp_phy {
struct device *dev;
struct regmap *regmap;
const struct mtk_dp_phy_pdata *pdata;
+
+ u8 ana_bias_r;
+ u8 ana_cktx_imp;
+ struct mtk_dp_phya_imp_sel ana_impsel[MTK_DP_PHY_MAX_LANES];
};
-static int mtk_dp_phy_init(struct phy *phy)
+static int mtk_dp_phy_set_analog_calibration_params(struct mtk_dp_phy *dp_phy)
+{
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs_ana_glb = pdata->regs_ana_glb;
+ const u8 *regs_ana_lane = pdata->regs_ana_lane;
+ int i, ret;
+
+ ret = regmap_update_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_BIAS_GEN_0],
+ XTP_GLB_BIAS_INT_R_CTRL,
+ FIELD_PREP(XTP_GLB_BIAS_INT_R_CTRL, dp_phy->ana_bias_r));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_DPAUX_TX],
+ CKM_PT0_CKTX_IMPSEL,
+ FIELD_PREP(CKM_PT0_CKTX_IMPSEL, dp_phy->ana_cktx_imp));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ struct mtk_dp_phya_imp_sel *ana_imp = &dp_phy->ana_impsel[i];
+ u32 val = FIELD_PREP(XTP_LN_TX_IMPSEL_PMOS, ana_imp->pmos) |
+ FIELD_PREP(XTP_LN_TX_IMPSEL_NMOS, ana_imp->nmos);
+ u32 off_ana_lane = pdata->off_ana_lane[i];
+
+ ret = regmap_update_bits(dp_phy->regmap,
+ off_ana_lane + regs_ana_lane[DP_PHYA_LAN_LANE_TX_0],
+ XTP_LN_TX_IMPSEL_PMOS | XTP_LN_TX_IMPSEL_NMOS, val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int mtk_dp_phy_set_digital_drv_params(struct mtk_dp_phy *dp_phy)
{
- struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u32 reg = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_PARAM_0];
- static const u32 driving_params[] = {
- DRIVING_PARAM_3_DEFAULT,
- DRIVING_PARAM_4_DEFAULT,
- DRIVING_PARAM_5_DEFAULT,
- DRIVING_PARAM_6_DEFAULT,
- DRIVING_PARAM_7_DEFAULT,
- DRIVING_PARAM_8_DEFAULT
- };
int i, ret;
/*
@@ -190,11 +292,31 @@ static int mtk_dp_phy_init(struct phy *phy)
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
ret = regmap_bulk_write(dp_phy->regmap,
pdata->off_dig_lane[i] + reg,
- driving_params,
- ARRAY_SIZE(driving_params));
+ pdata->driving_params,
+ ARRAY_SIZE(pdata->driving_params));
if (ret)
return ret;
};
+ return 0;
+}
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ struct device *dev = &phy->dev;
+ int ret;
+
+ ret = mtk_dp_phy_set_digital_drv_params(dp_phy);
+ if (ret) {
+ dev_err(dev, "Cannot set driving params\n");
+ return ret;
+ }
+
+ ret = mtk_dp_phy_set_analog_calibration_params(dp_phy);
+ if (ret) {
+ dev_err(dev, "Cannot set analog calibration\n");
+ return ret;
+ }
return 0;
}
@@ -407,6 +529,111 @@ static const struct phy_ops mtk_dp_phy_dev_ops = {
.owner = THIS_MODULE,
};
+static void mtk_dp_phy_get_default_cal_data(struct mtk_dp_phy *dp_phy)
+{
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ int i;
+
+ dp_phy->ana_bias_r = pdata->ana_bias_r;
+ dp_phy->ana_cktx_imp = pdata->ana_cktx_imp;
+
+ /* Copy the default lane impedance settings to all lanes */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++)
+ memcpy(&dp_phy->ana_impsel[i], &pdata->ana_lanes_imp,
+ sizeof(dp_phy->ana_impsel[0]));
+
+ return;
+}
+
+static int mtk_dp_phy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
+{
+ u8 buf_byte;
+ u16 buf;
+ int ret;
+
+ /*
+ * All of the calibrations are always max 8 bits long, but some may
+ * be split between two different 8-bits cells: handle this corner
+ * case by retrying reading as u16.
+ */
+ ret = nvmem_cell_read_u8(dev, name, &buf_byte);
+ if (ret)
+ ret = nvmem_cell_read_u16(dev, name, &buf);
+ else
+ buf = buf_byte;
+
+ if (ret) {
+ dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret);
+ return ret;
+ };
+
+ if (buf == 0) {
+ dev_warn(dev, "No calibration for %s. Using defaults\n", name);
+ return -ENOENT;
+ }
+
+ if (buf > max_val) {
+ dev_err(dev, "Bad value %u retrieved for %s. Returning.\n", buf, name);
+ return -ERANGE;
+ };
+
+ return buf;
+}
+
+static int mtk_dp_phy_get_calibration_data(struct mtk_dp_phy *dp_phy)
+{
+ char mtk_dp_cal_lane_imp_name[] = "impedance-laneXM";
+ struct device *dev = dp_phy->dev;
+ int i, ret;
+
+ ret = mtk_dp_phy_get_one_cal_para(dev, "rbias-trim", FIELD_MAX(XTP_GLB_BIAS_INT_R_CTRL));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_bias_r = ret;
+
+ ret = mtk_dp_phy_get_one_cal_para(dev, "impedance-txclk", FIELD_MAX(CKM_PT0_CKTX_IMPSEL));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_cktx_imp = ret;
+
+ /* Get impedance params for each lane */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ /* P-MOSFET first */
+ snprintf(mtk_dp_cal_lane_imp_name, ARRAY_SIZE(mtk_dp_cal_lane_imp_name),
+ "impedance-lane%dp", i);
+ ret = mtk_dp_phy_get_one_cal_para(dev, mtk_dp_cal_lane_imp_name,
+ FIELD_MAX(XTP_LN_TX_IMPSEL_PMOS));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_impsel[i].pmos = ret;
+
+ /* ...and then N-MOSFET too */
+ snprintf(mtk_dp_cal_lane_imp_name, ARRAY_SIZE(mtk_dp_cal_lane_imp_name),
+ "impedance-lane%dn", i);
+ ret = mtk_dp_phy_get_one_cal_para(dev, mtk_dp_cal_lane_imp_name,
+ FIELD_MAX(XTP_LN_TX_IMPSEL_NMOS));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_impsel[i].nmos = ret;
+ }
+end:
+ if (ret < 0) {
+ /*
+ * If any of the calibration values is missing, or if there
+ * is no calibration at all in the eFuses, copy the default
+ * one entirely (as partial values shall not be mixed!)
+ */
+ if (ret == -ENOENT) {
+ dev_info(dev, "Using calibration default values\n");
+ mtk_dp_phy_get_default_cal_data(dp_phy);
+ return 0;
+ }
+ return ret;
+ };
+
+ return 0;
+}
+
static void mtk_dp_phy_legacy_remove_lookup(void *data)
{
struct phy *phy = data;
@@ -444,6 +671,8 @@ static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_p
if (ret)
return ret;
+ mtk_dp_phy_get_default_cal_data(dp_phy);
+
return 0;
}
@@ -461,6 +690,7 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
struct mtk_dp_phy *dp_phy;
void __iomem *base;
struct phy *phy;
+ int ret;
dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
if (!dp_phy)
@@ -482,6 +712,15 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
dp_phy->pdata = device_get_match_data(dev);
+ if (IS_REACHABLE(CONFIG_NVMEM)) {
+ ret = mtk_dp_phy_get_calibration_data(dp_phy);
+ if (ret)
+ return ret;
+ } else {
+ /* Use default calibration data */
+ mtk_dp_phy_get_default_cal_data(dp_phy);
+ }
+
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
@@ -501,12 +740,31 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
}
static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
- .off_ana_glb = 0x0,
+ .off_ana_glb = 0,
+ .off_ana_lane = (const u16[]) { 0x100, 0x200, 0x300, 0x400 },
.off_dig_glb = 0x1000,
.off_dig_lane = (const u16[]) { 0x1100, 0x1200, 0x1300, 0x1400 },
.regs_ana_glb = mt8195_phy_ana_glb_regs,
+ .regs_ana_lane = mt8195_phy_ana_lane_regs,
.regs_dig_glb = mt8195_phy_dig_glb_regs,
.regs_dig_lane = mt8195_phy_dig_lane_regs,
+ .ana_bias_r = 15,
+ .ana_cktx_imp = 8,
+ .ana_lanes_imp = {
+ .pmos = 8,
+ .nmos = 8,
+ },
+ .driving_params = (const u32[]) {
+ [0] = 0,
+ [1] = 0,
+ [2] = 0,
+ [3] = MT8195_DRIVING_PARAM_3_DEFAULT,
+ [4] = MT8195_DRIVING_PARAM_4_DEFAULT,
+ [5] = MT8195_DRIVING_PARAM_5_DEFAULT,
+ [6] = MT8195_DRIVING_PARAM_6_DEFAULT,
+ [7] = MT8195_DRIVING_PARAM_7_DEFAULT,
+ [8] = MT8195_DRIVING_PARAM_8_DEFAULT
+ },
};
static const struct of_device_id mtk_dp_phy_of_match[] = {
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 07/12] phy: phy-mtk-dp: Support setting volt swing and preemphasis values
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
Add support for honoring the set_voltages request to set the
voltage swing and preemphasis values in the .configure() callback.
For proper hardware cleanup, reset both in the .reset() callback.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 44 ++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index 2f3ec3fa384d..270825f130a9 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -48,6 +48,11 @@
/* DP_PHYD_TX_CTL_0 */
#define PHYD_TX_LN_EN GENMASK(7, 4)
+/* DP_PHYD_DRIVING_FORCE */
+#define PHYD_DP_TX_FORCE_VOLT_SWING_EN BIT(0)
+#define PHYD_DP_TX_FORCE_VOLT_SWING_VAL GENMASK(2, 1)
+#define PHYD_DP_TX_FORCE_PRE_EMPH_VAL GENMASK(4, 3)
+
#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
@@ -101,6 +106,7 @@ enum mtk_dp_phya_ana_glb_regidx {
};
enum mtk_dp_phyd_dig_lane_regidx {
+ DP_PHYD_LAN_DRIVING_FORCE,
DP_PHYD_LAN_DRIVING_PARAM_0,
DP_PHYD_LAN_MAX
};
@@ -121,6 +127,7 @@ static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
};
static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
+ [DP_PHYD_LAN_DRIVING_FORCE] = 0x18,
[DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
};
@@ -235,6 +242,30 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
PHYD_TX_LN_EN, val);
}
+ if (opts->dp.set_voltages) {
+ const u32 reg_drv_force = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_FORCE];
+
+ if (opts->dp.lanes > 4) {
+ dev_err(&phy->dev, "Wrong lanes config %u\n", opts->dp.lanes);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < opts->dp.lanes; i++) {
+ const u32 off_dig_lane = pdata->off_dig_lane[i];
+ u32 val;
+
+ val = FIELD_PREP(PHYD_DP_TX_FORCE_VOLT_SWING_VAL, opts->dp.voltage[i]);
+ val |= FIELD_PREP(PHYD_DP_TX_FORCE_PRE_EMPH_VAL, opts->dp.pre[i]);
+ val |= PHYD_DP_TX_FORCE_VOLT_SWING_EN;
+
+ regmap_update_bits(dp_phy->regmap, off_dig_lane + reg_drv_force,
+ PHYD_DP_TX_FORCE_VOLT_SWING_EN |
+ PHYD_DP_TX_FORCE_VOLT_SWING_VAL |
+ PHYD_DP_TX_FORCE_PRE_EMPH_VAL,
+ val);
+ }
+ }
+
regmap_update_bits(dp_phy->regmap,
pdata->off_dig_glb + pdata->regs_dig_glb[DP_PHYD_PLL_CTL_1],
TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
@@ -337,7 +368,8 @@ static int mtk_dp_phy_reset(struct phy *phy)
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u32 reg_rst = pdata->regs_dig_glb[DP_PHYD_SW_RST];
- int ret;
+ const u32 reg_drv_force = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_FORCE];
+ int i, ret;
/* Clearing bits sets reset state */
regmap_clear_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
@@ -353,6 +385,16 @@ static int mtk_dp_phy_reset(struct phy *phy)
if (ret)
dev_err(dp_phy->dev, "Could not disable lanes during reset!\n");
+ /* Reset Voltage Swing and Preemphasis values */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ const u32 off_dig_lane = pdata->off_dig_lane[i];
+
+ regmap_clear_bits(dp_phy->regmap, off_dig_lane + reg_drv_force,
+ PHYD_DP_TX_FORCE_VOLT_SWING_EN |
+ PHYD_DP_TX_FORCE_VOLT_SWING_VAL |
+ PHYD_DP_TX_FORCE_PRE_EMPH_VAL);
+ }
+
return 0;
}
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 02/12] phy: phy-mtk-dp: Rename regs to regmap in struct mtk_dp_phy
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
In preparation to perform further cleanups and to extend the
driver to support more SoCs, rename the `regs` member to `regmap`
to improve readability, as this is a common name across many
kernel drivers for a struct regmap.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index d7024a144335..bf7b3a95e72d 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -79,7 +79,7 @@
XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
struct mtk_dp_phy {
- struct regmap *regs;
+ struct regmap *regmap;
};
static int mtk_dp_phy_init(struct phy *phy)
@@ -94,13 +94,13 @@ static int mtk_dp_phy_init(struct phy *phy)
DRIVING_PARAM_8_DEFAULT
};
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+ regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE0_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+ regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE1_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+ regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE2_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+ regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE3_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
return 0;
@@ -131,10 +131,10 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
val = BIT_RATE_HBR3;
break;
}
- regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+ regmap_write(dp_phy->regmap, MTK_DP_PHY_DIG_BIT_RATE, val);
}
- regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+ regmap_update_bits(dp_phy->regmap, MTK_DP_PHY_DIG_PLL_CTL_1,
TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
return 0;
@@ -144,10 +144,10 @@ static int mtk_dp_phy_reset(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
- regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+ regmap_update_bits(dp_phy->regmap, MTK_DP_PHY_DIG_SW_RST,
DP_GLB_SW_RST_PHYD, 0);
usleep_range(50, 200);
- regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+ regmap_update_bits(dp_phy->regmap, MTK_DP_PHY_DIG_SW_RST,
DP_GLB_SW_RST_PHYD, 1);
return 0;
@@ -176,7 +176,7 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
if (!dp_phy)
return -ENOMEM;
- dp_phy->regs = regs;
+ dp_phy->regmap = regs;
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 03/12] phy: phy-mtk-dp: Allow probing with devicetree match
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
Make it possible to decouple the registration of the DisplayPort
PHY driver from the DisplayPort IP driver by adding a devicetree
match to probe the PHY, registering an OF PHY provider and this
device's own MMIO regmap - if, and only if, this PHY driver was
registered with an OF match.
In order to retain compatibility with older devicetrees that are
not declaring the DisplayPort PHY as a separate node, the legacy
code was moved in a `mtk_dp_phy_legacy_probe()` function, which
gets called if the driver was registered by the DisplayPort one.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 84 +++++++++++++++++++++++++++----
1 file changed, 74 insertions(+), 10 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index bf7b3a95e72d..b042ab67ebaf 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -79,6 +79,7 @@
XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
struct mtk_dp_phy {
+ struct device *dev;
struct regmap *regmap;
};
@@ -160,43 +161,106 @@ static const struct phy_ops mtk_dp_phy_dev_ops = {
.owner = THIS_MODULE,
};
+static void mtk_dp_phy_legacy_remove_lookup(void *data)
+{
+ struct phy *phy = data;
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+ phy_remove_lookup(phy, "dp", dev_name(dp_phy->dev));
+}
+
+static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_phy *dp_phy)
+{
+ struct device *dev = &pdev->dev;
+ struct phy *phy;
+ int ret;
+
+ dp_phy->regmap = *(struct regmap **)dev->platform_data;
+ if (!dp_phy->regmap)
+ return dev_err_probe(dev, -EINVAL, "No platform data available\n");
+
+ phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+ if (IS_ERR(phy))
+ return dev_err_probe(dev, PTR_ERR(phy),
+ "Failed to create DP PHY\n");
+
+ phy_set_drvdata(phy, dp_phy);
+ phy_create_lookup(phy, "dp", dev_name(dev));
+
+ ret = devm_add_action_or_reset(dev, mtk_dp_phy_legacy_remove_lookup, phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct regmap_config mtk_dp_phy_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .disable_locking = true,
+};
+
static int mtk_dp_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
struct mtk_dp_phy *dp_phy;
+ void __iomem *base;
struct phy *phy;
- struct regmap *regs;
-
- regs = *(struct regmap **)dev->platform_data;
- if (!regs)
- return dev_err_probe(dev, -EINVAL,
- "No data passed, requires struct regmap**\n");
dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
if (!dp_phy)
return -ENOMEM;
- dp_phy->regmap = regs;
+ dp_phy->dev = dev;
+
+ /* If there's no devicetree, go for legacy pdev probe */
+ if (!dev->of_node)
+ return mtk_dp_phy_legacy_probe(pdev, dp_phy);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dp_phy->regmap = devm_regmap_init_mmio(dev, base, &mtk_dp_phy_regmap_cfg);
+ if (IS_ERR(dp_phy->regmap))
+ return PTR_ERR(dp_phy->regmap);
+
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
"Failed to create DP PHY\n");
phy_set_drvdata(phy, dp_phy);
- if (!dev->of_node)
- phy_create_lookup(phy, "dp", dev_name(dev));
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return PTR_ERR(provider);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
return 0;
}
+static const struct of_device_id mtk_dp_phy_of_match[] = {
+ { .compatible = "mediatek,mt8195-dp-phy" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_phy_of_match);
+
static struct platform_driver mtk_dp_phy_driver = {
.probe = mtk_dp_phy_probe,
.driver = {
.name = "mediatek-dp-phy",
+ .of_match_table = mtk_dp_phy_of_match,
},
};
module_platform_driver(mtk_dp_phy_driver);
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
-MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_DESCRIPTION("MediaTek DisplayPort PHY Driver");
MODULE_LICENSE("GPL");
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 09/12] phy: phy-mtk-dp: Rewrite and document default driving param macros
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
Use FIELD_PREP_CONST and add nicer definitions/macros to build the
default driving parameters for the PHY and, while at it, also add
comments explaining what they are supposed to set in the PHY.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 86 +++++++++++++++----------------
1 file changed, 41 insertions(+), 45 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index d0ef8e8f6670..9a800d6b91c3 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -85,51 +85,47 @@
* [2,5,8]: Swing 2 Pre1 and Swing 3 Pre0
*/
#define PHYD_DIG_NUM_DRV_PARA_REGS 9
-#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
-#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
-#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
-#define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29)
-#define MT8195_DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
-
-#define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT GENMASK(4, 3)
-#define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9)
-#define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21))
-#define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29)
-#define MT8195_DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
-
-#define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5))
-#define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12)
-#define MT8195_DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
- XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
-
-#define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0
-#define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10)
-#define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19)
-#define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28)
-#define MT8195_DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
-
-#define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT 0
-#define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9)
-#define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18)
-#define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0
-#define MT8195_DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
-
-#define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3)
-#define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0
-#define MT8195_DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
- XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+#define XTP_LN_TX_LCTXC_SW0_PRE0 GENMASK(5, 0)
+#define XTP_LN_TX_LCTXC_SW0_PRE1 GENMASK(13, 8)
+#define XTP_LN_TX_LCTXC_SW0_PRE2 GENMASK(21, 16)
+#define XTP_LN_TX_LCTXC_SW0_PRE3 GENMASK(29, 24)
+
+#define XTP_LN_TX_LCTXC_SW1_PRE0 GENMASK(5, 0)
+#define XTP_LN_TX_LCTXC_SW1_PRE1 GENMASK(13, 8)
+#define XTP_LN_TX_LCTXC_SW1_PRE2 GENMASK(21, 16)
+#define XTP_LN_TX_LCTXC_SW2_PRE0 GENMASK(29, 24)
+
+#define XTP_LN_TX_LCTXC_SW2_PRE1 GENMASK(5, 0)
+#define XTP_LN_TX_LCTXC_SW3_PRE0 GENMASK(13, 8)
+
+#define BUILD_DRIVING_PARAM_0(sw0_pre0, sw0_pre1, sw0_pre2, sw0_pre3) ( \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE0, sw0_pre0) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE1, sw0_pre1) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE2, sw0_pre2) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE3, sw0_pre3) \
+)
+
+#define BUILD_DRIVING_PARAM_12(sw1_pre0, sw1_pre1, sw1_pre2, sw2_pre0) (\
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE0, sw1_pre0) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE1, sw1_pre1) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE2, sw1_pre2) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW2_PRE0, sw2_pre0) \
+)
+
+#define BUILD_DRIVING_PARAM_23(sw2_pre1, sw3_pre0) ( \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW2_PRE1, sw2_pre1) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW3_PRE0, sw3_pre0) \
+)
+
+/* MT8195: Logic State Change Point (LC TX C) */
+#define MT8195_DRIVING_PARAM_3_DEFAULT BUILD_DRIVING_PARAM_0( 16, 20, 24, 32)
+#define MT8195_DRIVING_PARAM_4_DEFAULT BUILD_DRIVING_PARAM_12(24, 30, 36, 32)
+#define MT8195_DRIVING_PARAM_5_DEFAULT BUILD_DRIVING_PARAM_23(40, 48)
+
+/* MT8195: Positive Edge (LC TX CP) */
+#define MT8195_DRIVING_PARAM_6_DEFAULT BUILD_DRIVING_PARAM_0( 0, 4, 8, 16)
+#define MT8195_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 6, 12, 0)
+#define MT8195_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(8, 0)
enum mtk_dp_phya_ana_glb_regidx {
DP_PHYA_GLB_BIAS_GEN_0,
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 10/12] phy: phy-mtk-dp: Add bitrate register val definitions to SoC data
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
In preparation for adding support for the eDP PHY found in newer
SoCs, transfer the bitrate register value definitions to SoC
specific data.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 36 +++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index 9a800d6b91c3..8ac086360138 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -42,10 +42,6 @@
/* DP_PHYD_BIT_RATE */
#define PHYD_DIG_RG_BIT_RATE GENMASK(1, 0)
-# define BIT_RATE_RBR 0
-# define BIT_RATE_HBR 1
-# define BIT_RATE_HBR2 2
-# define BIT_RATE_HBR3 3
/* DP_PHYD_SW_RST */
#define PHYD_DIG_GLB_SW_RST_B GENMASK(7, 0)
@@ -157,6 +153,14 @@ enum mtk_dp_phyd_dig_glb_regidx {
DP_PHYD_GLOBAL_MAX
};
+enum mtk_dp_phyd_bit_rate_regval {
+ DP_PHYD_BIT_RATE_RBR,
+ DP_PHYD_BIT_RATE_HBR,
+ DP_PHYD_BIT_RATE_HBR2,
+ DP_PHYD_BIT_RATE_HBR3,
+ DP_PHYD_BIT_RATE_MAX,
+};
+
static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
[DP_PHYA_GLB_BIAS_GEN_0] = 0x0,
[DP_PHYA_GLB_BIAS_GEN_1] = 0x4,
@@ -183,6 +187,13 @@ static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_TX_CTL_0] = 0x44,
};
+static const u8 mt8195_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
+ [DP_PHYD_BIT_RATE_RBR] = 0,
+ [DP_PHYD_BIT_RATE_HBR] = 1,
+ [DP_PHYD_BIT_RATE_HBR2] = 2,
+ [DP_PHYD_BIT_RATE_HBR3] = 3
+};
+
/**
* struct mtk_dp_phya_imp_sel - Per-Lane Impedance Selection
* @pmos: Impedance selection for P-Channel MOSFET
@@ -203,6 +214,7 @@ struct mtk_dp_phya_imp_sel {
* @regs_ana_lane: Register (layout) offsets for ana_lan
* @regs_dig_glb: Register (layout) offsets for dig_glb
* @regs_dig_lane: Register (layout) offsets for dig_lan
+ * @val_dig_bitrate:IP Version specific register values for Bit Rate setting
* @ana_bias_r: Internal resistance "R" Selection Settings (global)
* @ana_cktx_imp: TX Clock Impedance Selection Settings (global)
* @ana_lanes_imp: TX Impedance Selection Settings (for all lanes)
@@ -221,6 +233,9 @@ struct mtk_dp_phy_pdata {
const u8 *regs_dig_glb;
const u8 *regs_dig_lane;
+ /* IP-Version specific register value arrays */
+ const u8 *val_dig_bitrate;
+
/* Calibration defaults */
u8 ana_bias_r;
u8 ana_cktx_imp;
@@ -326,6 +341,7 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
if (opts->dp.set_rate) {
const u32 reg_bit_rate = pdata->regs_dig_glb[DP_PHYD_BIT_RATE];
+ enum mtk_dp_phyd_bit_rate_regval regval_idx;
switch (opts->dp.link_rate) {
default:
@@ -334,19 +350,20 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
opts->dp.link_rate);
return -EINVAL;
case 1620:
- val = BIT_RATE_RBR;
+ regval_idx = DP_PHYD_BIT_RATE_RBR;
break;
case 2700:
- val = BIT_RATE_HBR;
+ regval_idx = DP_PHYD_BIT_RATE_HBR;
break;
case 5400:
- val = BIT_RATE_HBR2;
+ regval_idx = DP_PHYD_BIT_RATE_HBR2;
break;
case 8100:
- val = BIT_RATE_HBR3;
+ regval_idx = DP_PHYD_BIT_RATE_HBR3;
break;
}
- regmap_write(dp_phy->regmap, pdata->off_dig_glb + reg_bit_rate, val);
+ regmap_write(dp_phy->regmap, pdata->off_dig_glb + reg_bit_rate,
+ pdata->val_dig_bitrate[regval_idx]);
}
if (opts->dp.set_lanes) {
@@ -744,6 +761,7 @@ static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
.regs_ana_lane = mt8195_phy_ana_lane_regs,
.regs_dig_glb = mt8195_phy_dig_glb_regs,
.regs_dig_lane = mt8195_phy_dig_lane_regs,
+ .val_dig_bitrate = mt8195_phy_dig_bitrate_val,
.ana_bias_r = 15,
.ana_cktx_imp = 8,
.ana_lanes_imp = {
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 11/12] phy: phy-mtk-dp: Add PHYD Lane EN register mask to SoC data
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
In preparation for adding support for the eDP PHY found in newer
SoCs, transfer the register mask for PHYD_TX_LN_EN to SoC specific
data.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index 8ac086360138..64bdfda5394f 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -214,6 +214,7 @@ struct mtk_dp_phya_imp_sel {
* @regs_ana_lane: Register (layout) offsets for ana_lan
* @regs_dig_glb: Register (layout) offsets for dig_glb
* @regs_dig_lane: Register (layout) offsets for dig_lan
+ * @mask_dig_tx_ln: Register mask for PHYD_TX_LN_EN field
* @val_dig_bitrate:IP Version specific register values for Bit Rate setting
* @ana_bias_r: Internal resistance "R" Selection Settings (global)
* @ana_cktx_imp: TX Clock Impedance Selection Settings (global)
@@ -233,6 +234,9 @@ struct mtk_dp_phy_pdata {
const u8 *regs_dig_glb;
const u8 *regs_dig_lane;
+ /* Register masks */
+ u32 mask_dig_tx_ln;
+
/* IP-Version specific register value arrays */
const u8 *val_dig_bitrate;
@@ -371,10 +375,10 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
val = 0;
for (i = 0; i < opts->dp.lanes; i++)
- val |= FIELD_PREP(PHYD_TX_LN_EN, BIT(i));
+ val |= field_prep(pdata->mask_dig_tx_ln, BIT(i));
regmap_update_bits(dp_phy->regmap, pdata->off_dig_glb + reg_dig_tx_ctl,
- PHYD_TX_LN_EN, val);
+ pdata->mask_dig_tx_ln, val);
}
if (opts->dp.set_voltages) {
@@ -450,7 +454,7 @@ static int mtk_dp_phy_disable_all_lanes(struct mtk_dp_phy *dp_phy)
return ret;
/* Get mask of currently enabled lane */
- val = FIELD_GET(PHYD_TX_LN_EN, val);
+ val = field_get(pdata->mask_dig_tx_ln, val);
if (val == 0)
return 0;
@@ -461,7 +465,7 @@ static int mtk_dp_phy_disable_all_lanes(struct mtk_dp_phy *dp_phy)
ret = regmap_clear_bits(dp_phy->regmap,
pdata->off_dig_glb + regs[DP_PHYD_TX_CTL_0],
- FIELD_PREP(PHYD_TX_LN_EN, BIT(lane_num)));
+ field_prep(pdata->mask_dig_tx_ln, BIT(lane_num)));
if (ret)
return ret;
} while (val);
@@ -761,6 +765,7 @@ static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
.regs_ana_lane = mt8195_phy_ana_lane_regs,
.regs_dig_glb = mt8195_phy_dig_glb_regs,
.regs_dig_lane = mt8195_phy_dig_lane_regs,
+ .mask_dig_tx_ln = PHYD_TX_LN_EN,
.val_dig_bitrate = mt8195_phy_dig_bitrate_val,
.ana_bias_r = 15,
.ana_cktx_imp = 8,
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 12/12] phy: phy-mtk-dp: Add support for MT8196 eDP PHY
From: AngeloGioacchino Del Regno @ 2026-07-07 14:19 UTC (permalink / raw)
To: chunfeng.yun
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, chunkuang.hu, p.zabel, justin.yeh,
linux-arm-kernel, linux-mediatek, linux-phy, devicetree,
linux-kernel, dri-devel, kernel
In-Reply-To: <20260707141931.191172-1-angelogioacchino.delregno@collabora.com>
The MT8196 SoC features an updated PHY IP compared to the older
ones, and there is one that is specific to Embedded DisplayPort.
Add support for the eDP PHY found in the MediaTek MT8196 SoC and
all of its variants.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/phy/mediatek/phy-mtk-dp.c | 66 ++++++++++++++++++++++++++++++-
1 file changed, 65 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index 64bdfda5394f..06337a546c21 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -5,7 +5,7 @@
* Copyright (c) 2022, BayLibre Inc.
* Copyright (c) 2022, MediaTek Inc.
*
- * Major refactoring
+ * Major refactoring and new SoCs support
* Copyright (c) 2026, Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
@@ -41,6 +41,7 @@
#define TPLL_SSC_EN BIT(3)
/* DP_PHYD_BIT_RATE */
+#define PHYD_DIG_RG_BIT_RATE_V2 GENMASK(3, 0)
#define PHYD_DIG_RG_BIT_RATE GENMASK(1, 0)
/* DP_PHYD_SW_RST */
@@ -57,6 +58,7 @@
/* DP_PHYD_TX_CTL_0 */
#define PHYD_TX_LN_EN GENMASK(7, 4)
+#define PHYD_TX_LN_EN_V2 GENMASK(3, 0)
/* DP_PHYD_DRIVING_FORCE */
#define PHYD_DP_TX_FORCE_VOLT_SWING_EN BIT(0)
@@ -123,6 +125,16 @@
#define MT8195_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 6, 12, 0)
#define MT8195_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(8, 0)
+/* MT8196/MT6991: Logic State Change Point (LC TX C) */
+#define MT8196_DRIVING_PARAM_3_DEFAULT BUILD_DRIVING_PARAM_0( 10, 12, 14, 17)
+#define MT8196_DRIVING_PARAM_4_DEFAULT BUILD_DRIVING_PARAM_12(14, 17, 18, 18)
+#define MT8196_DRIVING_PARAM_5_DEFAULT BUILD_DRIVING_PARAM_23(21, 24)
+
+/* MT8196/MT6991: Positive Edge (LC TX CP) */
+#define MT8196_DRIVING_PARAM_6_DEFAULT BUILD_DRIVING_PARAM_0( 0, 2, 4, 7)
+#define MT8196_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 3, 6, 0)
+#define MT8196_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(3, 0)
+
enum mtk_dp_phya_ana_glb_regidx {
DP_PHYA_GLB_BIAS_GEN_0,
DP_PHYA_GLB_BIAS_GEN_1,
@@ -178,6 +190,11 @@ static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
[DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
};
+static const u8 mt8196_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
+ [DP_PHYD_LAN_DRIVING_FORCE] = 0x30,
+ [DP_PHYD_LAN_DRIVING_PARAM_0] = 0x34,
+};
+
static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_PLL_CTL_0] = 0x10,
[DP_PHYD_PLL_CTL_1] = 0x14,
@@ -187,6 +204,15 @@ static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_TX_CTL_0] = 0x44,
};
+static const u8 mt8196_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
+ [DP_PHYD_PLL_CTL_0] = 0x10,
+ [DP_PHYD_PLL_CTL_1] = 0x14,
+ [DP_PHYD_SW_RST] = 0x38,
+ [DP_PHYD_BIT_RATE] = 0x3c,
+ [DP_PHYD_AUX_RX_CTL] = 0x40,
+ [DP_PHYD_TX_CTL_0] = 0x74,
+};
+
static const u8 mt8195_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
[DP_PHYD_BIT_RATE_RBR] = 0,
[DP_PHYD_BIT_RATE_HBR] = 1,
@@ -194,6 +220,13 @@ static const u8 mt8195_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
[DP_PHYD_BIT_RATE_HBR3] = 3
};
+static const u8 mt8196_edp_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
+ [DP_PHYD_BIT_RATE_RBR] = 1,
+ [DP_PHYD_BIT_RATE_HBR] = 4,
+ [DP_PHYD_BIT_RATE_HBR2] = 7,
+ [DP_PHYD_BIT_RATE_HBR3] = 9
+};
+
/**
* struct mtk_dp_phya_imp_sel - Per-Lane Impedance Selection
* @pmos: Impedance selection for P-Channel MOSFET
@@ -786,8 +819,39 @@ static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
},
};
+static const struct mtk_dp_phy_pdata mt8196_edp_phy_data = {
+ .off_ana_glb = 0x400,
+ .off_ana_lane = (const u16[]) { 0x0, 0x100, 0x200, 0x300 },
+ .off_dig_glb = 0x1400,
+ .off_dig_lane = (const u16[]) { 0x1000, 0x1100, 0x1200, 0x1300 },
+ .regs_ana_glb = mt8195_phy_ana_glb_regs,
+ .regs_ana_lane = mt8195_phy_ana_lane_regs,
+ .regs_dig_glb = mt8196_phy_dig_glb_regs,
+ .regs_dig_lane = mt8196_phy_dig_lane_regs,
+ .mask_dig_tx_ln = PHYD_TX_LN_EN_V2,
+ .val_dig_bitrate = mt8196_edp_phy_dig_bitrate_val,
+ .ana_bias_r = 15,
+ .ana_cktx_imp = 8,
+ .ana_lanes_imp = {
+ .pmos = 8,
+ .nmos = 8,
+ },
+ .driving_params = (const u32[]) {
+ [0] = 0,
+ [1] = 0,
+ [2] = 0,
+ [3] = MT8196_DRIVING_PARAM_3_DEFAULT,
+ [4] = MT8196_DRIVING_PARAM_4_DEFAULT,
+ [5] = MT8196_DRIVING_PARAM_5_DEFAULT,
+ [6] = MT8196_DRIVING_PARAM_6_DEFAULT,
+ [7] = MT8196_DRIVING_PARAM_7_DEFAULT,
+ [8] = MT8196_DRIVING_PARAM_8_DEFAULT
+ },
+};
+
static const struct of_device_id mtk_dp_phy_of_match[] = {
{ .compatible = "mediatek,mt8195-dp-phy", .data = &mt8195_dp_phy_data },
+ { .compatible = "mediatek,mt8196-edp-phy", .data = &mt8196_edp_phy_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_dp_phy_of_match);
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v2 02/12] phy: phy-mtk-dp: Rename regs to regmap in struct mtk_dp_phy
From: sashiko-bot @ 2026-07-07 14:27 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: conor+dt, vkoul, devicetree, dri-devel, linux-phy, olteanv,
neil.armstrong, robh
In-Reply-To: <20260707141931.191172-3-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing NULL check for `dev->platform_data` before dereferencing it in `mtk_dp_phy_probe`.
--
--- Patch [2]: [PATCH v2 02/12] phy: phy-mtk-dp: Rename regs to regmap in struct mtk_dp_phy ---
commit b41d4072a9780ff4f56cf8b17085088b1f2777ae
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Rename regs to regmap in struct mtk_dp_phy
This commit renames the regs member of struct mtk_dp_phy to regmap to
improve readability and align with common kernel driver naming conventions.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> index d7024a1443358..bf7b3a95e72d2 100644
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> @@ -176,7 +176,7 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
[Severity: High]
This is a pre-existing issue, but does mtk_dp_phy_probe() lack a NULL check
for dev->platform_data before dereferencing it?
If the platform device is probed without platform_data (for example, if
manually bound via the sysfs bind interface to a generic platform device
lacking this data), dev->platform_data will be NULL.
Looking at the code in mtk_dp_phy_probe():
regs = *(struct regmap **)dev->platform_data;
if (!regs)
return dev_err_probe(dev, -EINVAL, ...
Dereferencing dev->platform_data to assign regs before checking if
platform_data itself is valid will cause a kernel oops.
> if (!dp_phy)
> return -ENOMEM;
>
> - dp_phy->regs = regs;
> + dp_phy->regmap = regs;
> phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> if (IS_ERR(phy))
> return dev_err_probe(dev, PTR_ERR(phy),
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 09/12] phy: phy-mtk-dp: Rewrite and document default driving param macros
From: sashiko-bot @ 2026-07-07 14:28 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: dri-devel, vkoul, olteanv, neil.armstrong, linux-phy, conor+dt,
robh, devicetree
In-Reply-To: <20260707141931.191172-10-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The patch uses `FIELD_PREP_CONST` (and earlier patches in the series use `FIELD_PREP`) without explicitly including `<linux/bitfield.h>`.
--
--- Patch [9]: [PATCH v2 09/12] phy: phy-mtk-dp: Rewrite and document default driving param macros ---
commit e682fc4989b37eaa003ac03d4d2a9a69b306a63b
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Rewrite and document default driving param macros
This commit rewrites the default driving parameters for the PHY using
FIELD_PREP_CONST. It also adds definitions and comments to explain what
these parameters are supposed to set.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> index d0ef8e8f6670c..9a800d6b91c3f 100644
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -85,51 +85,47 @@
[ ... ]
> +#define XTP_LN_TX_LCTXC_SW2_PRE1 GENMASK(5, 0)
> +#define XTP_LN_TX_LCTXC_SW3_PRE0 GENMASK(13, 8)
> +
> +#define BUILD_DRIVING_PARAM_0(sw0_pre0, sw0_pre1, sw0_pre2, sw0_pre3) ( \
> + FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE0, sw0_pre0) | \
[Severity: Low]
This isn't a bug, but should linux/bitfield.h be explicitly included since
this code now uses FIELD_PREP_CONST?
Relying on implicit inclusions can cause build breakages in the future if a
header like linux/phy/phy.h or linux/regmap.h stops including it.
> + FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE1, sw0_pre1) | \
> + FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE2, sw0_pre2) | \
> + FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE3, sw0_pre3) \
> +)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=9
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 08/12] phy: phy-mtk-dp: Add support for digital and analog calibration
From: sashiko-bot @ 2026-07-07 14:31 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: dri-devel, linux-phy, vkoul, robh, conor+dt, neil.armstrong,
olteanv, devicetree
In-Reply-To: <20260707141931.191172-9-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] The DP PHY is published and exposed to concurrent consumers before its calibration data is initialized in the legacy probe path.
- [Medium] Unconditional `dev_err` logs an error for missing optional NVMEM cells and probe deferrals in `mtk_dp_phy_get_one_cal_para`.
Pre-existing issues:
- [High] Dereferencing `dev->platform_data` without checking for NULL in `mtk_dp_phy_legacy_probe`.
--
--- Patch [8]: [PATCH v2 08/12] phy: phy-mtk-dp: Add support for digital and analog calibration ---
commit 0db0b57c522a5f7eb7d179aeee49708015901046
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Add support for digital and analog calibration
This commit adds support for reading DP PHY calibration values from eFuse
via nvmem. If the values are present, they are applied; otherwise, the
driver relies on default calibration data. Analog calibration writing
support is also introduced.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> +static int mtk_dp_phy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
> +{
> + u8 buf_byte;
> + u16 buf;
> + int ret;
> +
> + /*
> + * All of the calibrations are always max 8 bits long, but some may
> + * be split between two different 8-bits cells: handle this corner
> + * case by retrying reading as u16.
> + */
> + ret = nvmem_cell_read_u8(dev, name, &buf_byte);
> + if (ret)
> + ret = nvmem_cell_read_u16(dev, name, &buf);
> + else
> + buf = buf_byte;
> +
> + if (ret) {
> + dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret);
> + return ret;
> + };
[Severity: Medium]
Will this unconditionally log a hard error if the NVMEM cells are absent
or if the provider returns -EPROBE_DEFER?
Since nvmem_cell_read_u8() returns -ENOENT when optional cells are missing
in the device tree, could this cause spurious error messages in the kernel
log on normal boots? It might also spam the logs during standard probe
deferrals.
[ ... ]
> @@ -444,6 +671,8 @@ static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_p
> if (ret)
> return ret;
>
> + mtk_dp_phy_get_default_cal_data(dp_phy);
> +
> return 0;
> }
[Severity: High]
Is it safe to publish the PHY to consumers before the calibration defaults
are initialized?
Earlier in mtk_dp_phy_legacy_probe(), phy_create_lookup() exposes the PHY.
If a consumer driver probes concurrently and acquires the PHY right after
that lookup registration, could it call phy_init() while the analog
parameter registers are still zero-initialized by devm_kzalloc(), rather
than having these defaults?
[Severity: High]
This is a pre-existing issue, but can dev->platform_data be NULL earlier
in mtk_dp_phy_legacy_probe()?
Looking at the start of the function:
dp_phy->regmap = *(struct regmap **)dev->platform_data;
if (!dp_phy->regmap)
return dev_err_probe(dev, -EINVAL, "No platform data available\n");
If the driver is bound to a device without platform data (e.g. manually
via sysfs bind), it looks like dereferencing dev->platform_data to read
the regmap will cause a kernel panic before it reaches the NULL check.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=8
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 03/12] phy: phy-mtk-dp: Allow probing with devicetree match
From: sashiko-bot @ 2026-07-07 14:31 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: olteanv, neil.armstrong, linux-phy, vkoul, robh, conor+dt,
dri-devel, devicetree
In-Reply-To: <20260707141931.191172-4-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] PM Runtime on the parent device is enabled after the PHY device is created, preventing PM runtime from propagating to the parent and causing unpowered hardware accesses.
- [Medium] The DP PHY driver fails to parse and apply the `nvmem-cells` calibration data specified in the new device tree binding.
- [Low] The variable `ret` is used but not declared in `mtk_dp_phy_probe()`, causing a build failure.
--
--- Patch [3]: [PATCH v2 03/12] phy: phy-mtk-dp: Allow probing with devicetree match ---
commit e7cb0174c7cb05364cf12874f491ce4c799f2c61
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Allow probing with devicetree match
This commit adds a devicetree match to decouple the MediaTek DP PHY driver
from the DisplayPort IP driver. It implements a standalone probe path while
retaining a legacy probe function for backwards compatibility.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> @@ -160,43 +161,106 @@ static const struct phy_ops mtk_dp_phy_dev_ops = {
[ ... ]
> static int mtk_dp_phy_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> + struct phy_provider *provider;
> struct mtk_dp_phy *dp_phy;
> + void __iomem *base;
> struct phy *phy;
> - struct regmap *regs;
> -
> - regs = *(struct regmap **)dev->platform_data;
> - if (!regs)
> - return dev_err_probe(dev, -EINVAL,
> - "No data passed, requires struct regmap**\n");
>
> dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> if (!dp_phy)
> return -ENOMEM;
>
> - dp_phy->regmap = regs;
> + dp_phy->dev = dev;
> +
> + /* If there's no devicetree, go for legacy pdev probe */
> + if (!dev->of_node)
> + return mtk_dp_phy_legacy_probe(pdev, dp_phy);
[Severity: Medium]
Does the new devicetree probe path need to parse and apply the nvmem-cells
calibration data?
It looks like the new DT bindings specify nvmem-cells for optimal EYE signal
pattern calibration, but the standalone probe path doesn't retrieve them,
which might leave the hardware running with sub-optimal defaults.
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + dp_phy->regmap = devm_regmap_init_mmio(dev, base, &mtk_dp_phy_regmap_cfg);
> + if (IS_ERR(dp_phy->regmap))
> + return PTR_ERR(dp_phy->regmap);
> +
> phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
[Severity: High]
Will creating the PHY device before enabling PM runtime on the parent
prevent the PHY framework from propagating PM runtime to the child PHY
device?
In phy_create(), the framework checks pm_runtime_enabled(dev) on the parent
to decide whether to enable PM runtime on the newly created PHY device. Since
devm_pm_runtime_enable(dev) is called later, this check evaluates to false.
Could this lead to phy_pm_runtime_get_sync() returning -ENOTSUPP and
bypassing hardware wake-ups, resulting in unpowered register accesses?
> if (IS_ERR(phy))
> return dev_err_probe(dev, PTR_ERR(phy),
> "Failed to create DP PHY\n");
>
> phy_set_drvdata(phy, dp_phy);
> - if (!dev->of_node)
> - phy_create_lookup(phy, "dp", dev_name(dev));
> +
> + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> + if (IS_ERR(provider))
> + return PTR_ERR(provider);
> +
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return ret;
[Severity: Low]
Is the variable ret missing a declaration here?
It appears int ret; is not declared in mtk_dp_phy_probe(), which will cause
a build failure when compiling the driver.
>
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 05/12] phy: phy-mtk-dp: Implement power_on and power_off PHY callbacks
From: sashiko-bot @ 2026-07-07 14:33 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: conor+dt, neil.armstrong, vkoul, linux-phy, robh, dri-devel,
olteanv, devicetree
In-Reply-To: <20260707141931.191172-6-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Medium] mtk_dp_phy_power_off() aborts early on regmap_set_bits failure, leaving the RX channel perpetually enabled.
- [Medium] Missing check for device_get_match_data() returning NULL in mtk_dp_phy_probe, leading to a crash in PHY operations.
- [Low] Undeclared variable 'ret' in mtk_dp_phy_probe() causes a strict build failure.
Pre-existing issues:
- [High] Dereferencing dev->platform_data without checking for NULL in mtk_dp_phy_legacy_probe leads to a kernel panic.
--
--- Patch [5]: [PATCH v2 05/12] phy: phy-mtk-dp: Implement power_on and power_off PHY callbacks ---
commit db273838783f1ea341d7728660183966ad3c4d16
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Implement power_on and power_off PHY callbacks
This commit adds .power_on() and .power_off() callbacks to the DisplayPort
PHY operations. These additions allow the DisplayPort driver to use the
phy_power_on() and phy_power_off() APIs instead of hardcoded register writes.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> index 915f148325c49..0c3f06791613a 100644
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> @@ -200,6 +225,59 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
> return 0;
> }
>
> +static int mtk_dp_phy_power_on(struct phy *phy)
> +{
[ ... ]
> +static int mtk_dp_phy_power_off(struct phy *phy)
> +{
> + struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> + const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
> + const u8 *regs_dig = pdata->regs_dig_glb;
> + const u8 *regs_ana = pdata->regs_ana_glb;
> + int ret;
> +
> + ret = regmap_set_bits(dp_phy->regmap,
> + pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
> + CKM_CKTX0_EN_FORCE_MODE);
> + if (ret)
> + return ret;
[Severity: Medium]
If regmap_set_bits() fails and returns early, could this leave the hardware
RX channel powered on indefinitely?
Teardown steps usually proceed on a best-effort basis to ensure subsequent
hardware disable operations are executed.
> +
> + /* Disable RX */
> + ret = regmap_write(dp_phy->regmap,
> + pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL], 0);
[ ... ]
> @@ -319,8 +399,10 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
> }
[Severity: Low]
Does this code cause a build failure?
drivers/phy/mediatek/phy-mtk-dp.c:mtk_dp_phy_probe() {
...
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
...
}
It appears the variable 'ret' is used but not declared.
[Severity: Medium]
Does this code need a NULL check for the return value of
device_get_match_data()?
drivers/phy/mediatek/phy-mtk-dp.c:mtk_dp_phy_probe() {
...
dp_phy->pdata = device_get_match_data(dev);
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
...
}
If the device is bound manually via sysfs and the compatible string doesn't
match, device_get_match_data() could return NULL, which might crash the
kernel in subsequent operations like mtk_dp_phy_power_on().
[Severity: High]
This is a pre-existing issue, but does dereferencing dev->platform_data
without checking for NULL in mtk_dp_phy_legacy_probe() lead to a crash?
drivers/phy/mediatek/phy-mtk-dp.c:mtk_dp_phy_legacy_probe() {
...
dp_phy->regmap = *(struct regmap **)dev->platform_data;
if (!dp_phy->regmap)
return dev_err_probe(dev, -EINVAL, "No platform data available\n");
...
}
If pdev->dev.platform_data is NULL, the blind pointer dereference happens
before the sanity check is reached.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=5
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 07/12] phy: phy-mtk-dp: Support setting volt swing and preemphasis values
From: sashiko-bot @ 2026-07-07 14:33 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: vkoul, robh, linux-phy, devicetree, neil.armstrong, conor+dt,
dri-devel, olteanv
In-Reply-To: <20260707141931.191172-8-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Analog PHY driving parameters are written to the wrong register offsets, misaligning signal driving settings.
- [Low] Compilation error due to undeclared variable `ret` in `mtk_dp_phy_probe`.
--
--- Patch [7]: [PATCH v2 07/12] phy: phy-mtk-dp: Support setting volt swing and preemphasis values ---
commit 83b16a974d2259d5e6f2e7cfa570e7b31e4ec7e5
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Support setting volt swing and preemphasis values
This commit adds support for setting voltage swing and preemphasis values
in the DP PHY .configure() callback by honoring the set_voltages request.
It also ensures proper hardware cleanup by resetting both values in the
.reset() callback.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> index 2f3ec3fa384df..270825f130a95 100644
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> @@ -101,6 +106,7 @@ enum mtk_dp_phya_ana_glb_regidx {
> };
>
> enum mtk_dp_phyd_dig_lane_regidx {
> + DP_PHYD_LAN_DRIVING_FORCE,
> DP_PHYD_LAN_DRIVING_PARAM_0,
> DP_PHYD_LAN_MAX
> };
[Severity: High]
Are the analog PHY driving parameters being written to the wrong register
offsets, misaligning the signal driving settings?
The base offset for initializing the analog PHY driving parameters appears to
have shifted from PARAM_3 (0x1138) to PARAM_0 (0x112c), but the
driving_params array still contains the 6 default values meant for PARAM_3
through PARAM_8:
drivers/phy/mediatek/phy-mtk-dp.c:mtk_dp_phy_init() {
...
const u32 reg = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_PARAM_0];
static const u32 driving_params[] = {
DRIVING_PARAM_3_DEFAULT,
DRIVING_PARAM_4_DEFAULT,
DRIVING_PARAM_5_DEFAULT,
DRIVING_PARAM_6_DEFAULT,
DRIVING_PARAM_7_DEFAULT,
DRIVING_PARAM_8_DEFAULT
};
...
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
ret = regmap_bulk_write(dp_phy->regmap,
pdata->off_dig_lane[i] + reg,
driving_params,
ARRAY_SIZE(driving_params));
...
}
Writing the defaults for params 3-8 into params 0-5 would leave params 6-8
uninitialized and corrupt the settings for params 0-5, which could lead to
incorrect link training.
[Severity: Low]
Is there a missing declaration for the variable ret in mtk_dp_phy_probe()?
drivers/phy/mediatek/phy-mtk-dp.c:mtk_dp_phy_probe() {
...
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return PTR_ERR(provider);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
return 0;
}
This causes a compilation error because ret is used without being declared.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=7
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 06/12] phy: phy-mtk-dp: Support set_lanes in configure and properly cleanup
From: sashiko-bot @ 2026-07-07 14:34 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: olteanv, robh, linux-phy, vkoul, dri-devel, devicetree, conor+dt,
neil.armstrong
In-Reply-To: <20260707141931.191172-7-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing NULL check on device_get_match_data() result leads to potential NULL pointer dereference.
- [Low] Undeclared variable 'ret' causes a build failure.
--
--- Patch [6]: [PATCH v2 06/12] phy: phy-mtk-dp: Support set_lanes in configure and properly cleanup ---
commit 564fc9b179c0513c9ad23d3689aaf0c8211c61c3
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
phy: phy-mtk-dp: Support set_lanes in configure and properly cleanup
Add support for enabling a specifically requested number of lanes
in the .configure() callback and disable all lanes in power off
and reset callbacks for proper hardware cleanup.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> +static int mtk_dp_phy_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct phy_provider *provider;
> + struct mtk_dp_phy *dp_phy;
> + void __iomem *base;
> + struct phy *phy;
> +
> + dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> + if (!dp_phy)
> + return -ENOMEM;
> +
> + dp_phy->dev = dev;
> +
> + /* If there's no devicetree, go for legacy pdev probe */
> + if (!dev->of_node)
> + return mtk_dp_phy_legacy_probe(pdev, dp_phy);
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + dp_phy->regmap = devm_regmap_init_mmio(dev, base, &mtk_dp_phy_regmap_cfg);
> + if (IS_ERR(dp_phy->regmap))
> + return PTR_ERR(dp_phy->regmap);
> +
> + dp_phy->pdata = device_get_match_data(dev);
> +
> + phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
[Severity: High]
Can dp_phy->pdata be NULL here if the driver is bound manually?
If a user writes the driver name to 'driver_override' in sysfs for an
unrelated platform device, device_get_match_data() will return NULL since
the compatible string won't match.
The probe will succeed, but subsequent PHY framework calls to .init or
.configure will unconditionally dereference dp_phy->pdata, which could lead
to a kernel panic.
Would it be safer to verify the result of device_get_match_data() before
proceeding?
> + if (IS_ERR(phy))
> + return dev_err_probe(dev, PTR_ERR(phy),
> + "Failed to create DP PHY\n");
> +
> + phy_set_drvdata(phy, dp_phy);
> +
> + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> + if (IS_ERR(provider))
> + return PTR_ERR(provider);
> +
> + ret = devm_pm_runtime_enable(dev);
[Severity: Low]
Is the variable 'ret' missing a declaration in this function?
It appears 'ret' is assigned and checked here, but it wasn't declared in
the variable block at the start of mtk_dp_phy_probe(), which will cause a
build failure.
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=6
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 04/12] phy: phy-mtk-dp: Migrate register offsets to SoC specific pdata
From: sashiko-bot @ 2026-07-07 14:34 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: neil.armstrong, linux-phy, vkoul, conor+dt, robh, olteanv,
dri-devel, devicetree
In-Reply-To: <20260707141931.191172-5-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] PHY driving parameters are bulk written to the wrong starting register offset, corrupting hardware state.
- [High] Missing NULL check on the result of `device_get_match_data()` leads to a NULL pointer dereference.
- [Low] The array `off_dig_lane` in `mt8195_dp_phy_data` is initialized using a compound literal, causing a compilation error.
- [Low] The variable `ret` is used without being declared in `mtk_dp_phy_probe`, causing a build failure.
- [Low] Missing `#include <linux/pm_runtime.h>` for `devm_pm_runtime_enable`.
Pre-existing issues:
- [High] Dereferencing `dev->platform_data` before verifying it is not `NULL` causes a kernel panic.
--
--- Patch [4]: [PATCH v2 04/12] phy: phy-mtk-dp: Migrate register offsets to SoC specific pdata ---
commit b50fb23a21796e73379a3d3b300932c7ffb998c8
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Subject: phy: phy-mtk-dp: Migrate register offsets to SoC specific pdata
This commit migrates the hardcoded register offsets in the MediaTek DP PHY
driver to SoC-specific platform data. It also refactors the driving
parameter initialization to loop through all lanes instead of hardcoding
each lane.
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> index b042ab67ebaf3..915f148325c49 100644
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
[ ... ]
> @@ -94,15 +143,21 @@ static int mtk_dp_phy_init(struct phy *phy)
> DRIVING_PARAM_7_DEFAULT,
> DRIVING_PARAM_8_DEFAULT
> };
> -
> - regmap_bulk_write(dp_phy->regmap, MTK_DP_LANE0_DRIVING_PARAM_3,
> - driving_params, ARRAY_SIZE(driving_params));
[ ... ]
> + int i, ret;
> +
> + /*
> + * Assume that all lanes need the same driving parameters: this
> + * will bulk write from DRIVING_PARAM_0 to DRIVING_PARAM_8 on
> + * all lanes (a grand total of [9 * num_lanes] 32-bit writes)
> + */
> + for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
> + ret = regmap_bulk_write(dp_phy->regmap,
> + pdata->off_dig_lane[i] + reg,
> + driving_params,
> + ARRAY_SIZE(driving_params));
[Severity: High]
Does this bulk write start at the correct register offset?
The loop writes the driving_params array starting at the base offset for
DP_PHYD_LAN_DRIVING_PARAM_0. However, the driving_params array only contains 6
elements (defaults for PARAM_3 through PARAM_8).
Will this incorrectly write parameters 3-8 into the registers for 0-5, leaving
registers 6-8 uninitialized and corrupting the hardware PHY configuration?
> + if (ret)
> + return ret;
> + };
>
> return 0;
> }
[ ... ]
> @@ -169,12 +233,19 @@ static void mtk_dp_phy_legacy_remove_lookup(void *data)
> phy_remove_lookup(phy, "dp", dev_name(dp_phy->dev));
> }
>
> +static const struct mtk_dp_phy_pdata mt8195_dp_phy_data;
> +
> static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_phy *dp_phy)
> {
> struct device *dev = &pdev->dev;
> struct phy *phy;
> int ret;
>
> + /*
> + * If legacy platform driver probe, assume this is MT8195 or compatible
> + * with a devicetree that was not migrated to the new, proper bindings.
> + */
> + dp_phy->pdata = &mt8195_dp_phy_data;
> dp_phy->regmap = *(struct regmap **)dev->platform_data;
[Severity: High]
This is a pre-existing issue, but can dev->platform_data be NULL here?
The code directly dereferences dev->platform_data to assign dp_phy->regmap
before checking if it is NULL on the next line. If a device is probed without
devicetree and platform data is missing, will this trigger a kernel panic?
> if (!dp_phy->regmap)
> return dev_err_probe(dev, -EINVAL, "No platform data available\n");
[ ... ]
> @@ -227,6 +298,8 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
> if (IS_ERR(dp_phy->regmap))
> return PTR_ERR(dp_phy->regmap);
>
> + dp_phy->pdata = device_get_match_data(dev);
[Severity: High]
Could device_get_match_data() return NULL here?
If the driver is manually bound via sysfs without a matching OF compatible
(but the device has an of_node), device_get_match_data() will return NULL.
Since dp_phy->pdata is later unconditionally dereferenced in functions like
mtk_dp_phy_init() to look up register offsets, will this lead to a NULL pointer
dereference?
> +
> phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
[ ... ]
[Severity: Low]
Is the variable ret declared in the scope of mtk_dp_phy_probe?
It was noted that ret might be assigned the result of devm_pm_runtime_enable()
and evaluated:
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
But it appears it might not be declared within the local scope, which could
cause a compilation error.
[Severity: Low]
Should <linux/pm_runtime.h> be included in this file?
If devm_pm_runtime_enable() is used, it relies on the Power Management
API. Relying on implicit transitive inclusion for Power Management APIs can be
fragile and may cause a build failure.
> @@ -245,8 +318,15 @@ static int mtk_dp_phy_probe(struct platform_device *pdev)
> return 0;
> }
>
> +static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
> + .off_dig_glb = 0x1000,
> + .off_dig_lane = (const u16[]) { 0x1100, 0x1200, 0x1300, 0x1400 },
[Severity: Low]
Will this array initialization compile correctly?
In standard C, an array member inside a struct should be initialized using a
brace-enclosed list. Using a compound literal cast like (const u16[]) decays
to a pointer, which may cause a compilation error for an array type initializer.
> + .regs_dig_glb = mt8195_phy_dig_glb_regs,
> + .regs_dig_lane = mt8195_phy_dig_lane_regs,
> +};
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=4
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 01/12] dt-bindings: phy: Document MT8195 and MT8196 DisplayPort PHYs
From: sashiko-bot @ 2026-07-07 14:35 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: robh, linux-phy, neil.armstrong, olteanv, conor+dt, devicetree,
vkoul, dri-devel
In-Reply-To: <20260707141931.191172-2-angelogioacchino.delregno@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The proposed PHY binding incorrectly models the PHY as a standalone device node with an overlapping `reg` region that conflicts with the existing DP/eDP TX controller.
--
--- Patch [1]: [PATCH v2 01/12] dt-bindings: phy: Document MT8195 and MT8196 DisplayPort PHYs ---
commit 697e3d14510b082973bfd257d964015014dae7b9
Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
dt-bindings: phy: Document MT8195 and MT8196 DisplayPort PHYs
This adds bindings for the DisplayPort and Embedded DisplayPort PHYs found
in the MediaTek MT8195 and MT8196 SoCs. It includes properties for varying
impedance calibrations to optimize the EYE signal pattern.
> diff --git a/Documentation/devicetree/bindings/phy/mediatek,mt8195-dp-phy.yaml b/Documentation/devicetree/bindings/phy/mediatek,mt8195-dp-phy.yaml
> new file mode 100644
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/mediatek,mt8195-dp-phy.yaml
[ ... ]
> +required:
> + - compatible
> + - reg
> + - "#phy-cells"
[ ... ]
> +examples:
> + - |
> + phy@1c500000 {
> + compatible = "mediatek,mt8195-dp-phy";
> + reg = <0x1c500000 0x2000>;
[Severity: High]
Will this reg region conflict with the existing DP/eDP TX controller?
The existing MT8195 DP controller node (edp_tx in mt8195.dtsi) already uses
the base address 0x1c500000 for its region, and the monolithic mtk_dp.c
driver requests this entire exclusive region.
If the PHY is modeled as a standalone sibling node with an overlapping memory
bound, it seems the kernel's devm_platform_ioremap_resource will fail with
-EBUSY during driver probe and break the display controller.
Should the PHY instead be modeled as a child node without an absolute reg, or
should these calibration properties be merged directly into the existing
mediatek,dp.yaml controller binding?
> + #phy-cells = <0>;
> + nvmem-cells = <&edp_glb_bias_trim>, <&edp_clktx_impsel>,
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707141931.191172-1-angelogioacchino.delregno@collabora.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 0/9] Add RZ/G3L USB2.0 host support
From: Biju @ 2026-07-07 14:36 UTC (permalink / raw)
To: Philipp Zabel, Vinod Koul, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Geert Uytterhoeven, Michael Turquette, Stephen Boyd,
Liam Girdwood, Mark Brown, Magnus Damm
Cc: Biju Das, Neil Armstrong, Yoshihiro Shimoda, linux-phy,
devicetree, linux-kernel, linux-clk, linux-renesas-soc,
Prabhakar Mahadev Lad, Biju Das
From: Biju Das <biju.das.jz@bp.renesas.com>
Add device tree binding support for the RZ/G3L (r9a08g046) USB PHY
controller. The RZ/G3L USB PHY block is similar to RZ/G3S, but each port
has an OTG controller, unlike RZ/G3S, which has an OTG controller only on
port 1.
v2->v3:
* Replaced items->enum in reset controller bindings.
* Collected the tag for reset binding patch.
* Dropped clock patch as it is accepted.
v1->v2:
* Dropped patch#6(Introduce helper for regulator registration)
* Passing pointer to an array of regulators to make it scalable.
* Dropped regulator1-vbus and added a regulators group node.
* Updated commit description for patch#1,#4,#6,#7,#8,#9 and #10.
* Added enum instead of const in the compatible section.
* Updated schema check.
* Collected tag for PHY binding patch.
in the regulator driver.
* Added regulators group node and its children in SoC dtsi.
Biju Das (9):
dt-bindings: reset: renesas,rzg2l-usbphy-ctrl: Document RZ/G3L support
dt-bindings: phy: renesas,usb2-phy: Document RZ/G3L PHY bindings
reset: rzg2l-usbphy-ctrl: Introduce info struct for match data
reset: rzg2l-usbphy-ctrl: Add RZ/G3L support
regulator: renesas-usb-vbus-regulator: Add RZ/G3L VBUS regulator
support
phy: renesas: phy-rcar-gen3-usb2: Add RZ/G3L support
phy: renesas: phy-rcar-gen3-usb2: Fix devm action registration for
disabled VBUS regulator
arm64: dts: renesas: r9a08g046: Add USB2.0 device nodes
arm64: dts: renesas: r9a08g046l48-smarc: Add USB2.0 support
.../bindings/phy/renesas,usb2-phy.yaml | 2 +
.../reset/renesas,rzg2l-usbphy-ctrl.yaml | 48 +++++++-
arch/arm64/boot/dts/renesas/r9a08g046.dtsi | 105 ++++++++++++++++++
.../boot/dts/renesas/r9a08g046l48-smarc.dts | 49 ++++++++
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 20 ++--
.../regulator/renesas-usb-vbus-regulator.c | 53 +++++++++
drivers/reset/reset-rzg2l-usbphy-ctrl.c | 44 +++++---
7 files changed, 296 insertions(+), 25 deletions(-)
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 2/9] dt-bindings: phy: renesas,usb2-phy: Document RZ/G3L PHY bindings
From: Biju @ 2026-07-07 14:36 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: Biju Das, Neil Armstrong, Yoshihiro Shimoda, linux-phy,
devicetree, linux-kernel, linux-renesas-soc,
Prabhakar Mahadev Lad, Biju Das, Krzysztof Kozlowski
In-Reply-To: <20260707143646.245177-1-biju.das.jz@bp.renesas.com>
From: Biju Das <biju.das.jz@bp.renesas.com>
Add device tree binding support for the RZ/G3L (r9a08g046) USB2 PHY.
The RZ/G3L USB PHY is almost identical to the RZ/G3S USB PHY, the
difference being 2 OTG blocks on RZ/G3L compared to 1 on RZ/G3S.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
v2->v3:
* No change
v1->v2:
* Collected tag.
---
Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml
index 9740e5b335f9..d6b9d08ceec6 100644
--- a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml
@@ -16,6 +16,7 @@ properties:
- enum:
- renesas,usb2-phy-r8a77470 # RZ/G1C
- renesas,usb2-phy-r9a08g045 # RZ/G3S
+ - renesas,usb2-phy-r9a08g046 # RZ/G3L
- renesas,usb2-phy-r9a09g057 # RZ/V2H(P)
- items:
@@ -132,6 +133,7 @@ allOf:
enum:
- renesas,usb2-phy-r9a09g057
- renesas,usb2-phy-r9a08g045
+ - renesas,usb2-phy-r9a08g046
- renesas,rzg2l-usb2-phy
then:
properties:
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v3 6/9] phy: renesas: phy-rcar-gen3-usb2: Add RZ/G3L support
From: Biju @ 2026-07-07 14:36 UTC (permalink / raw)
To: Yoshihiro Shimoda, Vinod Koul, Geert Uytterhoeven, Magnus Damm
Cc: Biju Das, Neil Armstrong, linux-renesas-soc, linux-phy,
linux-kernel, Prabhakar Mahadev Lad, Biju Das
In-Reply-To: <20260707143646.245177-1-biju.das.jz@bp.renesas.com>
From: Biju Das <biju.das.jz@bp.renesas.com>
Add renesas,usb2-phy-r9a08g046 to the OF match table, reusing
rz_g3s_phy_usb2_data as the PHY configuration is shared with RZ/G3S.
While the PHY data is shared, RZ/G3L differs from RZ/G3S in that it has
two OTG controllers, OTG interrupts on port 2, and a controllable
OTG_PERI bit in COMMCTRL for host/device switching on the port 2 USB
controller, which is fixed to host-only on RZ/G3S.
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
v2->v3:
* No change.
v1->v2:
* Updated commit description.
---
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index 9a45d840efeb..d06fb52ed5f1 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -721,6 +721,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
.compatible = "renesas,usb2-phy-r9a08g045",
.data = &rz_g3s_phy_usb2_data,
},
+ {
+ .compatible = "renesas,usb2-phy-r9a08g046",
+ .data = &rz_g3s_phy_usb2_data,
+ },
{
.compatible = "renesas,usb2-phy-r9a09g057",
.data = &rz_v2h_phy_usb2_data,
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v3 7/9] phy: renesas: phy-rcar-gen3-usb2: Fix devm action registration for disabled VBUS regulator
From: Biju @ 2026-07-07 14:36 UTC (permalink / raw)
To: Yoshihiro Shimoda, Vinod Koul, Geert Uytterhoeven, Magnus Damm
Cc: Biju Das, Neil Armstrong, Philipp Zabel, linux-renesas-soc,
linux-phy, linux-kernel, Prabhakar Mahadev Lad, Biju Das
In-Reply-To: <20260707143646.245177-1-biju.das.jz@bp.renesas.com>
From: Biju Das <biju.das.jz@bp.renesas.com>
devm_regulator_get_exclusive() initialises the regulator with
enable_count = 1, requiring the consumer to disable it before release.
The devm disable action was previously only registered when the caller
explicitly requested enable, so when the regulator was left in its initial
enabled state without an explicit enable call, the cleanup path skipped
decrementing enable_count, triggering a WARN_ON during regulator
release on device removal.
Fix this by always registering the devm disable action based on the actual
enabled state via regulator_is_enabled(), regardless of whether the
caller requested an explicit enable. This covers both the explicitly-enabled
case and the initial state set by devm_regulator_get_exclusive().
Fixes: 24843404efe4 ("phy: renesas: phy-rcar-gen3-usb2: Control VBUS for RZ/G2L SoCs")
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
v2->v3:
* No change.
v1->v2:
* Updated commit description.
---
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index d06fb52ed5f1..ef38c3b365d4 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -905,15 +905,17 @@ static int rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(struct rcar_ge
if (IS_ERR(channel->vbus))
return PTR_ERR(channel->vbus);
- if (!enable)
- return 0;
+ if (enable) {
+ ret = regulator_enable(channel->vbus);
+ if (ret)
+ return ret;
+ }
- ret = regulator_enable(channel->vbus);
- if (ret)
- return ret;
+ if (regulator_is_enabled(channel->vbus))
+ return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action,
+ channel->vbus);
- return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action,
- channel->vbus);
+ return 0;
}
static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *channel)
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v3 6/9] phy: renesas: phy-rcar-gen3-usb2: Add RZ/G3L support
From: sashiko-bot @ 2026-07-07 14:51 UTC (permalink / raw)
To: Biju; +Cc: vkoul, olteanv, neil.armstrong, linux-phy, wsa+renesas
In-Reply-To: <20260707143646.245177-7-biju.das.jz@bp.renesas.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Sleep-in-atomic bug due to calling regulator_hardware_enable() inside a spinlock and from a hardirq handler.
--
--- Patch [6]: [PATCH v3 6/9] phy: renesas: phy-rcar-gen3-usb2: Add RZ/G3L support ---
commit 502c90feba61fdb92ccdaff21a3e226e6113ab48
Author: Biju Das <biju.das.jz@bp.renesas.com>
phy: renesas: phy-rcar-gen3-usb2: Add RZ/G3L support
This commit adds support for the RZ/G3L SoC to the phy-rcar-gen3-usb2 driver
by adding the "renesas,usb2-phy-r9a08g046" compatible string. The PHY data
configuration is shared with RZ/G3S.
> diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> index 9a45d840efeb2..d06fb52ed5f1b 100644
> --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> @@ -721,6 +721,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
> .compatible = "renesas,usb2-phy-r9a08g045",
> .data = &rz_g3s_phy_usb2_data,
> },
> + {
> + .compatible = "renesas,usb2-phy-r9a08g046",
> + .data = &rz_g3s_phy_usb2_data,
> + },
[Severity: High]
This is a pre-existing issue, but since this patch exposes this hardware to
existing code paths, I wanted to ask about a potential sleep-in-atomic bug.
When a user writes to the role sysfs attribute, role_store() acquires a
spinlock and disables interrupts:
drivers/phy/renesas/phy-rcar-gen3-usb2.c:role_store() {
guard(spinlock_irqsave)(&ch->lock);
...
if (!is_b_device) /* A-Peripheral */
rcar_gen3_init_from_a_peri_to_a_host(ch);
}
This calls down to rcar_gen3_enable_vbus_ctrl(), which then triggers the
regulator operations:
drivers/phy/renesas/phy-rcar-gen3-usb2.c:rcar_gen3_enable_vbus_ctrl() {
...
if (ch->otg_internal_reg) {
regulator_hardware_enable(ch->vbus, vbus);
return;
}
if (ch->phy_data->no_adp_ctrl || ch->phy_data->vblvl_ctrl) {
if (ch->vbus)
regulator_hardware_enable(ch->vbus, vbus);
...
}
For SoCs using the internal regulator (such as RZ/G3L), the enable callback
invokes pm_runtime_resume_and_get():
drivers/phy/renesas/phy-rcar-gen3-usb2.c:rcar_gen3_phy_usb2_regulator_endisable() {
...
ret = pm_runtime_resume_and_get(dev);
...
}
Can this sequence cause a panic?
If the device state is RPM_SUSPENDING, pm_runtime_resume_and_get() might sleep
waiting for the asynchronous suspend to finish, but we are inside an atomic
section holding ch->lock with interrupts disabled.
Similarly, does the rcar_gen3_phy_usb2_irq() hardirq handler also reach
this regulator_hardware_enable() path on OTG plug/unplug events? If so,
would it hit the same sleep-in-atomic issue?
> {
> .compatible = "renesas,usb2-phy-r9a09g057",
> .data = &rz_v2h_phy_usb2_data,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707143646.245177-1-biju.das.jz@bp.renesas.com?part=6
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3 7/9] phy: renesas: phy-rcar-gen3-usb2: Fix devm action registration for disabled VBUS regulator
From: sashiko-bot @ 2026-07-07 15:01 UTC (permalink / raw)
To: Biju; +Cc: wsa+renesas, vkoul, neil.armstrong, olteanv, linux-phy
In-Reply-To: <20260707143646.245177-8-biju.das.jz@bp.renesas.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Medium] `regulator_is_enabled()` returns a negative error code on failure, which incorrectly evaluates to true and causes an unbalanced disable WARN_ON.
Pre-existing issues:
- [High] If the regulator was initially enabled in hardware AND explicitly enabled by the driver, only one disable action is registered, leading to a resource leak and WARN_ON.
- [High] If `regulator_enable()` fails, the function returns early without registering a disable action, leaking the implicit `enable_count` reference if the regulator was already enabled.
- [High] Missing cancellation of the pending workqueue task (`channel->work`) during device removal leads to a Use-After-Free.
--
--- Patch [7]: [PATCH v3 7/9] phy: renesas: phy-rcar-gen3-usb2: Fix devm action registration for disabled VBUS regulator ---
commit 30054990ee806fb8d79768ae79587b9acfe0f6a6
Author: Biju Das <biju.das.jz@bp.renesas.com>
phy: renesas: phy-rcar-gen3-usb2: Fix devm action registration for disabled VBUS regulator
This commit updates the VBUS regulator registration to always add a devm
disable action based on the actual enabled state, rather than only when
an explicit enable was requested. This ensures the regulator's enable_count
is correctly decremented during device removal even if it was left enabled
by the bootloader.
> diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> index d06fb52ed5f1b..ef38c3b365d42 100644
> --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> @@ -905,15 +905,17 @@ static int rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(struct rcar_ge
> if (IS_ERR(channel->vbus))
> return PTR_ERR(channel->vbus);
>
> - if (!enable)
> - return 0;
> + if (enable) {
> + ret = regulator_enable(channel->vbus);
> + if (ret)
> + return ret;
[Severity: High]
This is a pre-existing issue, but does this early return leak the implicit
enable_count reference if the regulator was already enabled?
If devm_regulator_get_exclusive() initializes enable_count to 1 (because the
hardware was already enabled), and this regulator_enable() call fails, we
return early and bypass registering the devm disable action. When the probe
fails and devres cleans up, regulator_put() will be called with
enable_count == 1, which triggers a WARN_ON(regulator->enable_count) and
leaks the underlying rdev->use_count.
> + }
>
> - ret = regulator_enable(channel->vbus);
> - if (ret)
> - return ret;
> + if (regulator_is_enabled(channel->vbus))
> + return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action,
> + channel->vbus);
[Severity: Medium]
Does regulator_is_enabled() return a negative error code on failure, and
will that be incorrectly evaluated as true here?
If a hardware or I2C error occurs, regulator_is_enabled() can return a
negative value. This condition would evaluate as true, queueing the
rcar_gen3_phy_usb2_vbus_disable_action even if the regulator is actually
disabled or its enable_count is 0.
On device removal, calling regulator_disable() with an enable_count of 0
triggers a WARN in the regulator core.
[Severity: High]
This is a pre-existing issue, but does this logic lead to a resource leak if
the regulator was initially enabled in hardware and explicitly enabled by the
driver?
If the bootloader leaves the regulator enabled,
devm_regulator_get_exclusive() initializes enable_count to 1. If the driver
also explicitly requests enable = true, regulator_enable() increments
enable_count to 2.
However, this logic only registers a single disable action. During unbind,
the action decrements enable_count to 1, and devm_regulator_release() calls
regulator_put() which triggers a WARN_ON and destroys the consumer struct
without fully decrementing the underlying rdev->use_count.
> - return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action,
> - channel->vbus);
> + return 0;
> }
[Severity: High]
This is a pre-existing issue, but does this device removal sequence miss
canceling the pending workqueue task?
drivers/phy/renesas/phy-rcar-gen3-usb2.c:rcar_gen3_phy_usb2_remove() {
struct rcar_gen3_chan *channel = platform_get_drvdata(pdev);
if (channel->is_otg_channel)
device_remove_file(&pdev->dev, &dev_attr_role);
}
The driver schedules channel->work from its IRQ handler. If the driver is
unbound while an IRQ is being processed, rcar_gen3_phy_usb2_remove()
executes but does not call cancel_work_sync(&channel->work).
As devres cleans up, it frees the channel structure. If the workqueue
executes after channel is freed, it will dereference freed memory leading
to a use-after-free.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707143646.245177-1-biju.das.jz@bp.renesas.com?part=7
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox