Re: [PATCH v2 8/8] phy: samsung: add Exynos2200 usb phy controller

Dmitry Baryshkov
2025-02-23T23:55:00Z

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@gmail.com>
> ---
> 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@gmail.com>
> + */
> +
> +#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