On Sun, Feb 23, 2025 at 02:22:27PM +0200, Ivaylo Ivanov wrote: > The Exynos2200 SoC comes with a TI external repeater and 3 USB PHYs: > - snps eUSB2 for UTMI > - snps USBDP combophy for PIPE3 and DP > - samsung USBCON phy > > The USBCON phy is an intermediary between the USB controller (DWC3) > and the underlying PHYs. Add a new driver for it, modelled to take > a phandle to the high-speed PHY for now. > > Signed-off-by: Ivaylo Ivanov <ivo.ivanov.ivanov1@xxxxxxxxx> > --- > drivers/phy/samsung/Kconfig | 12 + > drivers/phy/samsung/Makefile | 1 + > drivers/phy/samsung/phy-exynos2200-usbcon.c | 250 ++++++++++++++++++++ > 3 files changed, 263 insertions(+) > create mode 100644 drivers/phy/samsung/phy-exynos2200-usbcon.c > > diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig > index e2330b089..0f809a382 100644 > --- a/drivers/phy/samsung/Kconfig > +++ b/drivers/phy/samsung/Kconfig > @@ -77,6 +77,18 @@ config PHY_S5PV210_USB2 > particular SoC is compiled in the driver. In case of S5PV210 two phys > are available - device and host. > > +config PHY_EXYNOS2200_USBCON > + tristate "Exynos2200 USBCON PHY driver" > + depends on (ARCH_EXYNOS && OF) || COMPILE_TEST > + depends on HAS_IOMEM > + select GENERIC_PHY > + select MFD_SYSCON > + default y > + help > + Enable USBCON PHY support for Exynos2200 SoC. > + This driver provides PHY interface for the USBCON phy, which acts as > + an intermediary between the USB controller and underlying PHYs. > + > config PHY_EXYNOS5_USBDRD > tristate "Exynos5 SoC series USB DRD PHY driver" > depends on (ARCH_EXYNOS && OF) || COMPILE_TEST > diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile > index fea1f96d0..e2686a3f5 100644 > --- a/drivers/phy/samsung/Makefile > +++ b/drivers/phy/samsung/Makefile > @@ -14,5 +14,6 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o > phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o > phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o > phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o > +obj-$(CONFIG_PHY_EXYNOS2200_USBCON) += phy-exynos2200-usbcon.o > obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o > obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o > diff --git a/drivers/phy/samsung/phy-exynos2200-usbcon.c b/drivers/phy/samsung/phy-exynos2200-usbcon.c > new file mode 100644 > index 000000000..aad59349e > --- /dev/null > +++ b/drivers/phy/samsung/phy-exynos2200-usbcon.c > @@ -0,0 +1,250 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2025, Ivaylo Ivanov <ivo.ivanov.ivanov1@xxxxxxxxx> > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/mfd/syscon.h> > +#include <linux/mod_devicetable.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/regulator/consumer.h> > +#include <linux/soc/samsung/exynos-regs-pmu.h> > + > +#define EXYNOS2200_USBCON_LINKCTRL 0x4 > +#define LINKCTRL_FORCE_QACT BIT(8) > + > +#define EXYNOS2200_USBCON_UTMI_CTRL 0x10 > +#define UTMI_CTRL_FORCESLEEP BIT(13) > +#define UTMI_CTRL_FORCESUSPEND BIT(12) > +#define UTMI_CTRL_FORCE_VBUSVALID BIT(1) > +#define UTMI_CTRL_FORCE_BVALID BIT(0) > + > +#define EXYNOS2200_USBCON_LINK_CLKRST 0xc > +#define LINK_CLKRST_SW_RST BIT(0) > + > +struct exynos2200_usbcon_phy_drvdata { > + const char * const *clk_names; > + int num_clks; > +}; > + > +struct exynos2200_usbcon_phy { > + struct phy *phy; > + void __iomem *base; > + struct regmap *reg_pmu; > + struct clk_bulk_data *clks; > + const struct exynos2200_usbcon_phy_drvdata *drv_data; > + u32 pmu_offset; > + struct phy *hs_phy; > +}; > + > +static void exynos2200_usbcon_phy_isol(struct exynos2200_usbcon_phy *inst, > + bool isolate) > +{ > + unsigned int val; > + > + if (!inst->reg_pmu) > + return; > + > + val = isolate ? 0 : EXYNOS4_PHY_ENABLE; > + > + regmap_update_bits(inst->reg_pmu, inst->pmu_offset, > + EXYNOS4_PHY_ENABLE, val); > +} > + > +static void exynos2200_usbcon_phy_write_mask(void __iomem *base, u32 offset, > + u32 mask, u32 val) > +{ > + u32 reg; > + > + reg = readl(base + offset); > + reg &= ~mask; > + reg |= val & mask; > + writel(reg, base + offset); Does this need any kind of locking? Maybe you can use regmap for this access too? > + > + /* Ensure above write is completed */ > + readl(base + offset); > +} [...] > + > + generic_phy = devm_phy_create(dev, NULL, &exynos2200_usbcon_phy_ops); > + if (IS_ERR(generic_phy)) > + return dev_err_probe(dev, PTR_ERR(generic_phy), > + "failed to create phy %d\n", ret); > + > + dev_set_drvdata(dev, phy); Nit: unused. LGTM otherwise. > + phy_set_drvdata(generic_phy, phy); > + > + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(phy_provider)) > + return dev_err_probe(dev, PTR_ERR(phy_provider), > + "failed to register phy provider\n"); > + > + return 0; > +} > + > +static const char * const exynos2200_clk_names[] = { > + "bus", > +}; > + > +static const struct exynos2200_usbcon_phy_drvdata exynos2200_usbcon_phy = { > + .clk_names = exynos2200_clk_names, > + .num_clks = ARRAY_SIZE(exynos2200_clk_names), > +}; > + > +static const struct of_device_id exynos2200_usbcon_phy_of_match_table[] = { > + { > + .compatible = "samsung,exynos2200-usbcon-phy", > + .data = &exynos2200_usbcon_phy, > + }, { }, > +}; > +MODULE_DEVICE_TABLE(of, exynos2200_usbcon_phy_of_match_table); > + > +static struct platform_driver exynos2200_usbcon_phy_driver = { > + .probe = exynos2200_usbcon_phy_probe, > + .driver = { > + .name = "exynos2200-usbcon-phy", > + .of_match_table = exynos2200_usbcon_phy_of_match_table, > + }, > +}; > + > +module_platform_driver(exynos2200_usbcon_phy_driver); > +MODULE_DESCRIPTION("Exynos2200 USBCON PHY driver"); > +MODULE_LICENSE("GPL"); > -- > 2.43.0 > -- With best wishes Dmitry