From 588d5d20ca8defa5ba5d1b536ff3695f6ab7aa87 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Thu, 10 Apr 2025 14:01:14 +0530 Subject: phy: exynos5-usbdrd: add exynos7870 USBDRD support Implement support for Exynos7870 USB DRD on top of the existing exynos5-usbdrd driver. Exynos7870 has a single USB 2.0 DRD PHY controller and no 3.0 PHYs. Thus, it only supports the UTMI interface. Moreover, the PMU register offset for enabling the PHY controller is different for SoCs such as Exynos7870, where BIT(0) is for the 3.0 PHY and BIT(1) is for the 2.0 PHY. The phy_isol function for Exynos7870 uses the appropriate register offsets. Signed-off-by: Kaustabh Chakraborty Link: https://lore.kernel.org/r/20250410-exynos7870-usbphy-v2-3-2eb005987455@disroot.org Signed-off-by: Vinod Koul --- include/linux/soc/samsung/exynos-regs-pmu.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h index ce1a3790d6fb..cde299a85384 100644 --- a/include/linux/soc/samsung/exynos-regs-pmu.h +++ b/include/linux/soc/samsung/exynos-regs-pmu.h @@ -55,6 +55,8 @@ #define EXYNOS4_MIPI_PHY_SRESETN (1 << 1) #define EXYNOS4_MIPI_PHY_MRESETN (1 << 2) #define EXYNOS4_MIPI_PHY_RESET_MASK (3 << 1) +/* USB PHY enable bit, valid for Exynos7870 */ +#define EXYNOS7870_USB2PHY_ENABLE (1 << 1) #define S5P_INFORM0 0x0800 #define S5P_INFORM1 0x0804 -- cgit v1.2.3 From 10ed34d6eaaf86e301a8f2dd190d26dfbc9799bd Mon Sep 17 00:00:00 2001 From: Sandor Yu Date: Tue, 18 Mar 2025 14:35:35 +0200 Subject: phy: Add HDMI configuration options Allow HDMI PHYs to be configured through the generic functions through a custom structure added to the generic union. The parameters added here are based on HDMI PHY implementation practices. The current set of parameters should cover the potential users. Signed-off-by: Sandor Yu Reviewed-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Acked-by: Vinod Koul Link: https://lore.kernel.org/r/d1cff6c03ec3732d2244022029245ab2d954d997.1734340233.git.Sandor.yu@nxp.com Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-1-8cb1678e7663@collabora.com Signed-off-by: Vinod Koul --- include/linux/phy/phy-hdmi.h | 19 +++++++++++++++++++ include/linux/phy/phy.h | 7 ++++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 include/linux/phy/phy-hdmi.h (limited to 'include/linux') diff --git a/include/linux/phy/phy-hdmi.h b/include/linux/phy/phy-hdmi.h new file mode 100644 index 000000000000..6a696922bc7f --- /dev/null +++ b/include/linux/phy/phy-hdmi.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2022,2024 NXP + */ + +#ifndef __PHY_HDMI_H_ +#define __PHY_HDMI_H_ + +/** + * struct phy_configure_opts_hdmi - HDMI configuration set + * @tmds_char_rate: HDMI TMDS Character Rate in Hertz. + * + * This structure is used to represent the configuration state of a HDMI phy. + */ +struct phy_configure_opts_hdmi { + unsigned long long tmds_char_rate; +}; + +#endif /* __PHY_HDMI_H_ */ diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index e63e6e70e860..437769e061b7 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -42,7 +43,8 @@ enum phy_mode { PHY_MODE_MIPI_DPHY, PHY_MODE_SATA, PHY_MODE_LVDS, - PHY_MODE_DP + PHY_MODE_DP, + PHY_MODE_HDMI, }; enum phy_media { @@ -60,11 +62,14 @@ enum phy_media { * the DisplayPort protocol. * @lvds: Configuration set applicable for phys supporting * the LVDS phy mode. + * @hdmi: Configuration set applicable for phys supporting + * the HDMI phy mode. */ union phy_configure_opts { struct phy_configure_opts_mipi_dphy mipi_dphy; struct phy_configure_opts_dp dp; struct phy_configure_opts_lvds lvds; + struct phy_configure_opts_hdmi hdmi; }; /** -- cgit v1.2.3 From 3bb9286f4ece6acbc1fbaa9f192a82645d30efbf Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 18 Mar 2025 14:35:36 +0200 Subject: phy: hdmi: Add color depth configuration Extend the HDMI configuration options to allow managing bits per color channel. This is required by some PHY drivers such as rockchip-samsung-hdptx. Reviewed-by: Dmitry Baryshkov Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20250318-phy-sam-hdptx-bpc-v6-2-8cb1678e7663@collabora.com Signed-off-by: Vinod Koul --- include/linux/phy/phy-hdmi.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/phy/phy-hdmi.h b/include/linux/phy/phy-hdmi.h index 6a696922bc7f..f0ec963c6e84 100644 --- a/include/linux/phy/phy-hdmi.h +++ b/include/linux/phy/phy-hdmi.h @@ -9,11 +9,13 @@ /** * struct phy_configure_opts_hdmi - HDMI configuration set * @tmds_char_rate: HDMI TMDS Character Rate in Hertz. + * @bpc: Bits per color channel. * * This structure is used to represent the configuration state of a HDMI phy. */ struct phy_configure_opts_hdmi { unsigned long long tmds_char_rate; + unsigned int bpc; }; #endif /* __PHY_HDMI_H_ */ -- cgit v1.2.3 From cc52a697f87e8b2d88298827aca3f81398385572 Mon Sep 17 00:00:00 2001 From: Ivaylo Ivanov Date: Sun, 4 May 2025 17:45:27 +0300 Subject: phy: exynos5-usbdrd: support Exynos USBDRD 3.2 4nm controller Add support for the Exynos USB 3.2 DRD 4nm controller. It's used in recent 4nm SoCs like Exynos2200 and Exynos2400. This device consists of 3 underlying and independent phys: SEC link control phy, Synopsys eUSB 2.0 and Synopsys USBDP/SS combophy. Unlike older device designs, where the internal phy blocks were all IP of Samsung, Synopsys phys are present. This means that the link controller is now mapped differently to account for missing bits and registers. The Synopsys phys also have separate register bases. As there are non-SEC PHYs present now, it doesn't make much sense to implement them in this driver. They are expected to be configured by external drivers, so pass phandles to them. USBDRD3.2 link controller set up is still required beforehand. This commit adds the necessary changes for USB HS to work. USB SS and DisplayPort are out of scope in this commit and will be introduced in the future. Signed-off-by: Ivaylo Ivanov Link: https://lore.kernel.org/r/20250504144527.1723980-11-ivo.ivanov.ivanov1@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 227 ++++++++++++++++++++++++++-- include/linux/soc/samsung/exynos-regs-pmu.h | 3 + 2 files changed, 215 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 6cbe563a7bd0..917a76d584f0 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -36,6 +36,21 @@ #define EXYNOS5_FSEL_26MHZ 0x6 #define EXYNOS5_FSEL_50MHZ 0x7 +/* USB 3.2 DRD 4nm PHY link controller registers */ +#define EXYNOS2200_DRD_CLKRST 0x0c +#define EXYNOS2200_CLKRST_LINK_PCLK_SEL BIT(1) + +#define EXYNOS2200_DRD_UTMI 0x10 +#define EXYNOS2200_UTMI_FORCE_VBUSVALID BIT(1) +#define EXYNOS2200_UTMI_FORCE_BVALID BIT(0) + +#define EXYNOS2200_DRD_HSP_MISC 0x114 +#define HSP_MISC_SET_REQ_IN2 BIT(4) +#define HSP_MISC_RES_TUNE GENMASK(1, 0) +#define RES_TUNE_PHY1_PHY2 0x1 +#define RES_TUNE_PHY1 0x2 +#define RES_TUNE_PHY2 0x3 + /* Exynos5: USB 3.0 DRD PHY registers */ #define EXYNOS5_DRD_LINKSYSTEM 0x04 #define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) @@ -431,6 +446,7 @@ struct exynos5_usbdrd_phy_drvdata { * @clks: clocks for register access * @core_clks: core clocks for phy (ref, pipe3, utmi+, ITP, etc. as required) * @drv_data: pointer to SoC level driver data structure + * @hs_phy: pointer to non-Samsung IP high-speed phy controller * @phy_mutex: mutex protecting phy_init/exit & TCPC callbacks * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY * instances each with its 'phy' and 'phy_cfg'. @@ -448,6 +464,7 @@ struct exynos5_usbdrd_phy { struct clk_bulk_data *clks; struct clk_bulk_data *core_clks; const struct exynos5_usbdrd_phy_drvdata *drv_data; + struct phy *hs_phy; struct mutex phy_mutex; struct phy_usb_instance { struct phy *phy; @@ -1285,6 +1302,149 @@ static const struct phy_ops exynos7870_usbdrd_phy_ops = { .owner = THIS_MODULE, }; +static void exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) +{ + /* Configure non-Samsung IP PHY, responsible for UTMI */ + phy_init(phy_drd->hs_phy); +} + +static void exynos2200_usbdrd_link_init(struct exynos5_usbdrd_phy *phy_drd) +{ + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + /* + * Disable HWACG (hardware auto clock gating control). This will force + * QACTIVE signal in Q-Channel interface to HIGH level, to make sure + * the PHY clock is not gated by the hardware. + */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= LINKCTRL_FORCE_QACT; + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + /* De-assert link reset */ + reg = readl(regs_base + EXYNOS2200_DRD_CLKRST); + reg &= ~CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS2200_DRD_CLKRST); + + /* Set link VBUS Valid */ + reg = readl(regs_base + EXYNOS2200_DRD_UTMI); + reg |= EXYNOS2200_UTMI_FORCE_BVALID | EXYNOS2200_UTMI_FORCE_VBUSVALID; + writel(reg, regs_base + EXYNOS2200_DRD_UTMI); +} + +static void +exynos2200_usbdrd_link_attach_detach_pipe3_phy(struct phy_usb_instance *inst) +{ + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) { + /* force pipe3 signal for link */ + reg &= ~LINKCTRL_FORCE_PHYSTATUS; + reg |= LINKCTRL_FORCE_PIPE_EN | LINKCTRL_FORCE_RXELECIDLE; + } else { + /* disable forcing pipe interface */ + reg &= ~LINKCTRL_FORCE_PIPE_EN; + } + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + reg = readl(regs_base + EXYNOS2200_DRD_HSP_MISC); + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) { + /* calibrate only eUSB phy */ + reg |= FIELD_PREP(HSP_MISC_RES_TUNE, RES_TUNE_PHY1); + reg |= HSP_MISC_SET_REQ_IN2; + } else { + /* calibrate for dual phy */ + reg |= FIELD_PREP(HSP_MISC_RES_TUNE, RES_TUNE_PHY1_PHY2); + reg &= ~HSP_MISC_SET_REQ_IN2; + } + writel(reg, regs_base + EXYNOS2200_DRD_HSP_MISC); + + reg = readl(regs_base + EXYNOS2200_DRD_CLKRST); + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) + reg &= ~EXYNOS2200_CLKRST_LINK_PCLK_SEL; + else + reg |= EXYNOS2200_CLKRST_LINK_PCLK_SEL; + + writel(reg, regs_base + EXYNOS2200_DRD_CLKRST); +} + +static int exynos2200_usbdrd_phy_init(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + int ret; + + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) { + /* Power-on PHY ... */ + ret = regulator_bulk_enable(phy_drd->drv_data->n_regulators, + phy_drd->regulators); + if (ret) { + dev_err(phy_drd->dev, + "Failed to enable PHY regulator(s)\n"); + return ret; + } + } + /* + * ... and ungate power via PMU. Without this here, we get an SError + * trying to access PMA registers + */ + exynos5_usbdrd_phy_isol(inst, false); + + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); + if (ret) + return ret; + + /* Set up the link controller */ + exynos2200_usbdrd_link_init(phy_drd); + + /* UTMI or PIPE3 link preparation */ + exynos2200_usbdrd_link_attach_detach_pipe3_phy(inst); + + /* UTMI or PIPE3 specific init */ + inst->phy_cfg->phy_init(phy_drd); + + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); + + return 0; +} + +static int exynos2200_usbdrd_phy_exit(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + int ret; + + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); + if (ret) + return ret; + + reg = readl(regs_base + EXYNOS2200_DRD_UTMI); + reg &= ~(EXYNOS2200_UTMI_FORCE_BVALID | EXYNOS2200_UTMI_FORCE_VBUSVALID); + writel(reg, regs_base + EXYNOS2200_DRD_UTMI); + + reg = readl(regs_base + EXYNOS2200_DRD_CLKRST); + reg |= CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS2200_DRD_CLKRST); + + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); + + exynos5_usbdrd_phy_isol(inst, true); + return regulator_bulk_disable(phy_drd->drv_data->n_regulators, + phy_drd->regulators); +} + +static const struct phy_ops exynos2200_usbdrd_phy_ops = { + .init = exynos2200_usbdrd_phy_init, + .exit = exynos2200_usbdrd_phy_exit, + .owner = THIS_MODULE, +}; + static void exynos5_usbdrd_usb_v3p1_pipe_override(struct exynos5_usbdrd_phy *phy_drd) { @@ -1594,27 +1754,37 @@ static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) return dev_err_probe(phy_drd->dev, ret, "failed to get phy core clock(s)\n"); - ref_clk = NULL; - for (int i = 0; i < phy_drd->drv_data->n_core_clks; ++i) { - if (!strcmp(phy_drd->core_clks[i].id, "ref")) { - ref_clk = phy_drd->core_clks[i].clk; - break; + if (phy_drd->drv_data->n_core_clks) { + ref_clk = NULL; + for (int i = 0; i < phy_drd->drv_data->n_core_clks; ++i) { + if (!strcmp(phy_drd->core_clks[i].id, "ref")) { + ref_clk = phy_drd->core_clks[i].clk; + break; + } } - } - if (!ref_clk) - return dev_err_probe(phy_drd->dev, -ENODEV, - "failed to find phy reference clock\n"); + if (!ref_clk) + return dev_err_probe(phy_drd->dev, -ENODEV, + "failed to find phy reference clock\n"); - ref_rate = clk_get_rate(ref_clk); - ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); - if (ret) - return dev_err_probe(phy_drd->dev, ret, - "clock rate (%ld) not supported\n", - ref_rate); + ref_rate = clk_get_rate(ref_clk); + ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (ret) + return dev_err_probe(phy_drd->dev, ret, + "clock rate (%ld) not supported\n", + ref_rate); + } return 0; } +static const struct exynos5_usbdrd_phy_config phy_cfg_exynos2200[] = { + { + .id = EXYNOS5_DRDPHY_UTMI, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos2200_usbdrd_utmi_init, + }, +}; + static int exynos5_usbdrd_orien_sw_set(struct typec_switch_dev *sw, enum typec_orientation orientation) { @@ -1767,6 +1937,19 @@ static const char * const exynos5_regulator_names[] = { "vbus", "vbus-boost", }; +static const struct exynos5_usbdrd_phy_drvdata exynos2200_usb32drd_phy = { + .phy_cfg = phy_cfg_exynos2200, + .phy_ops = &exynos2200_usbdrd_phy_ops, + .pmu_offset_usbdrd0_phy = EXYNOS2200_PHY_CTRL_USB20, + .clk_names = exynos5_clk_names, + .n_clks = ARRAY_SIZE(exynos5_clk_names), + /* clocks and regulators are specific to the underlying PHY blocks */ + .core_clk_names = NULL, + .n_core_clks = 0, + .regulator_names = NULL, + .n_regulators = 0, +}; + static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .phy_ops = &exynos5_usbdrd_phy_ops, @@ -2024,6 +2207,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { { .compatible = "google,gs101-usb31drd-phy", .data = &gs101_usbd31rd_phy + }, { + .compatible = "samsung,exynos2200-usb32drd-phy", + .data = &exynos2200_usb32drd_phy, }, { .compatible = "samsung,exynos5250-usbdrd-phy", .data = &exynos5250_usbdrd_phy @@ -2099,6 +2285,17 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) return PTR_ERR(phy_drd->reg_phy); } + /* + * USB32DRD 4nm controller implements Synopsys eUSB2.0 PHY + * and Synopsys SS/USBDP COMBOPHY, managed by external code. + */ + if (of_property_present(dev->of_node, "phy-names")) { + phy_drd->hs_phy = devm_of_phy_get(dev, dev->of_node, "hs"); + if (IS_ERR(phy_drd->hs_phy)) + return dev_err_probe(dev, PTR_ERR(phy_drd->hs_phy), + "failed to get hs_phy\n"); + } + ret = exynos5_usbdrd_phy_clk_handle(phy_drd); if (ret) return ret; diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h index cde299a85384..e921e7fe9be7 100644 --- a/include/linux/soc/samsung/exynos-regs-pmu.h +++ b/include/linux/soc/samsung/exynos-regs-pmu.h @@ -187,6 +187,9 @@ /* Only for S5Pv210 */ #define S5PV210_EINT_WAKEUP_MASK 0xC004 +/* Only for Exynos2200 */ +#define EXYNOS2200_PHY_CTRL_USB20 0x72C + /* Only for Exynos4210 */ #define S5P_CMU_CLKSTOP_LCD1_LOWPWR 0x1154 #define S5P_CMU_RESET_LCD1_LOWPWR 0x1174 -- cgit v1.2.3