[PATCH v3 12/19] phy: at91: Add support for the USB 2.0 PHY's of SAMA7

Claudiu.Beznea at microchip.com Claudiu.Beznea at microchip.com
Mon Dec 12 15:31:07 CET 2022


On 12.12.2022 15:39, Sergiu Moga wrote:
> In order to have USB functionality, drivers for SAMA7's
> USB 2.0 PHY's have been added. There is one driver
> for UTMI clock's SFR and RESET required functionalities and
> one for its three possible subclocks of the phy's themselves.
> In order for this layout to properly work in conjunction with
> CCF and DT, the former driver will also act as a clock provider
> for the three phy's with the help of a custom hook into the
> driver's of_xlate method.
> 
> Signed-off-by: Sergiu Moga <sergiu.moga at microchip.com>
> Tested-by: Mihai Sain <mihai.sain at microchip.com>
> ---
> 
> v1 -> v3:
> - No change
> 
> 
>  drivers/phy/Kconfig              |  10 ++
>  drivers/phy/Makefile             |   1 +
>  drivers/phy/phy-sama7-usb.c      |  92 ++++++++++++++
>  drivers/phy/phy-sama7-utmi-clk.c | 202 +++++++++++++++++++++++++++++++
>  4 files changed, 305 insertions(+)
>  create mode 100644 drivers/phy/phy-sama7-usb.c
>  create mode 100644 drivers/phy/phy-sama7-utmi-clk.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index cf4d5908d7..9fbb956783 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -281,6 +281,16 @@ config PHY_XILINX_ZYNQMP
>  	  Enable this to support ZynqMP High Speed Gigabit Transceiver
>  	  that is part of ZynqMP SoC.
>  
> +config PHY_MICROCHIP_SAMA7_USB
> +       tristate "Microchip SAMA7 USB 2.0 PHY"
> +       depends on PHY && ARCH_AT91
> +       help
> +	 Enable this to support SAMA7 USB 2.0 PHY.
> +
> +	 The USB 2.0 PHY integrates high-speed, full-speed and low-speed
> +	 termination and signal switching. With a single resistor, it
> +	 requires minimal external components.
> +
>  source "drivers/phy/rockchip/Kconfig"
>  source "drivers/phy/cadence/Kconfig"
>  source "drivers/phy/ti/Kconfig"
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index a3b9f3c5b1..9d50affd47 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
>  obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o
>  obj-$(CONFIG_PHY_IMX8MQ_USB) += phy-imx8mq-usb.o
>  obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o
> +obj-$(CONFIG_PHY_MICROCHIP_SAMA7_USB)  += phy-sama7-utmi-clk.o phy-sama7-usb.o
>  obj-y += cadence/
>  obj-y += ti/
>  obj-y += qcom/
> diff --git a/drivers/phy/phy-sama7-usb.c b/drivers/phy/phy-sama7-usb.c
> new file mode 100644
> index 0000000000..b6fe40ecc1
> --- /dev/null
> +++ b/drivers/phy/phy-sama7-usb.c
> @@ -0,0 +1,92 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Support for Atmel/Microchip USB PHY's.
> + *
> + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
> + *
> + * Author: Sergiu Moga <sergiu.moga at microchip.com>
> + */
> +
> +#include <clk.h>
> +#include <dm.h>
> +#include <generic-phy.h>
> +#include <syscon.h>
> +#include <regmap.h>
> +#include <mach/sama7-sfr.h>
> +
> +struct sama7_usb_phy {
> +	struct clk *uclk;
> +	struct regmap *sfr;
> +	int port;
> +};
> +
> +int sama7_usb_phy_init(struct phy *phy)
> +{
> +	struct sama7_usb_phy *sama7_phy = dev_get_priv(phy->dev);
> +	int port = sama7_phy->port;
> +
> +	regmap_update_bits(sama7_phy->sfr, SAMA7_SFR_UTMI0R(port),
> +			   SAMA7_SFR_UTMI_RX_TX_PREEM_AMP_TUNE_1X,
> +			   SAMA7_SFR_UTMI_RX_TX_PREEM_AMP_TUNE_1X);
> +
> +	regmap_update_bits(sama7_phy->sfr, SAMA7_SFR_UTMI0R(port),
> +			   SAMA7_SFR_UTMI_RX_VBUS,
> +			   SAMA7_SFR_UTMI_RX_VBUS);
> +
> +	return 0;
> +}
> +
> +int sama7_phy_power_on(struct phy *phy)
> +{
> +	struct sama7_usb_phy *sama7_phy = dev_get_priv(phy->dev);
> +
> +	clk_prepare_enable(sama7_phy->uclk);
> +
> +	return 0;
> +}
> +
> +int sama7_phy_power_off(struct phy *phy)
> +{
> +	struct sama7_usb_phy *sama7_phy = dev_get_priv(phy->dev);
> +
> +	clk_disable_unprepare(sama7_phy->uclk);
> +
> +	return 0;
> +}
> +
> +int sama7_usb_phy_probe(struct udevice *dev)
> +{
> +	struct sama7_usb_phy *sama7_phy = dev_get_priv(dev);
> +
> +	sama7_phy->uclk = devm_clk_get(dev, "utmi_clk");
> +	if (IS_ERR(sama7_phy->uclk))
> +		return PTR_ERR(sama7_phy->uclk);
> +
> +	sama7_phy->sfr = syscon_regmap_lookup_by_phandle(dev, "sfr-phandle");
> +	if (IS_ERR(sama7_phy->sfr)) {
> +		sama7_phy->sfr = NULL;

Is this needed?

> +		return PTR_ERR(sama7_phy->sfr);
> +	}
> +
> +	return dev_read_u32(dev, "reg", &sama7_phy->port);
> +}
> +
> +static const struct phy_ops sama7_usb_phy_ops = {
> +	.init = sama7_usb_phy_init,
> +	.power_on = sama7_phy_power_on,
> +	.power_off = sama7_phy_power_off,
> +};
> +
> +static const struct udevice_id sama7_usb_phy_of_match[] = {
> +	{ .compatible = "microchip,sama7g5-usb-phy", },
> +	{ },
> +};
> +
> +U_BOOT_DRIVER(sama7_usb_phy_driver) = {
> +	.name = "sama7-usb-phy",
> +	.id = UCLASS_PHY,
> +	.of_match = sama7_usb_phy_of_match,
> +	.ops = &sama7_usb_phy_ops,
> +	.probe = sama7_usb_phy_probe,
> +	.priv_auto = sizeof(struct sama7_usb_phy),
> +};
> diff --git a/drivers/phy/phy-sama7-utmi-clk.c b/drivers/phy/phy-sama7-utmi-clk.c
> new file mode 100644
> index 0000000000..ab9fddccf6
> --- /dev/null
> +++ b/drivers/phy/phy-sama7-utmi-clk.c
> @@ -0,0 +1,202 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Support for Atmel/Microchip USB PHY's.
> + *
> + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
> + *
> + * Author: Sergiu Moga <sergiu.moga at microchip.com>
> + */
> +
> +#include <dm.h>
> +#include <linux/clk-provider.h>
> +#include <syscon.h>
> +#include <regmap.h>
> +#include <mach/sama7-sfr.h>
> +#include <reset.h>
> +#include <dt-bindings/clk/at91.h>
> +
> +struct sama7_utmi_clk {
> +	struct clk		uclk;
> +	struct regmap		*regmap_sfr;
> +	struct reset_ctl	*reset;
> +	u8 id;
> +};
> +
> +#define to_sama7_utmi_clk(_c) container_of(_c, struct sama7_utmi_clk, uclk)
> +
> +#define UBOOT_DM_CLK_MICROCHIP_SAMA7G5_UTMI "sama7-utmi-clk"
> +#define UBOOT_DM_MICROCHIP_SAMA7G5_UTMI "sama7-utmi"
> +
> +#define AT91_TO_CLK_ID(_t, _i)		(((_t) << 8) | ((_i) & 0xff))
> +
> +/*
> + * UTMI clock description
> + * @n:	clock name
> + * @p:	clock parent name
> + * @id: clock id in RSTC_GRSTR
> + */
> +static struct {
> +	const char *n;
> +	const char *p;
> +	u8 id;
> +} sama7_utmick[] = {
> +	{ .n = "utmi1",	.p = "utmick", .id = 0, },
> +	{ .n = "utmi2",	.p = "utmi1",  .id = 1, },
> +	{ .n = "utmi3",	.p = "utmi1",  .id = 2, },
> +};
> +
> +static int sama7_utmi_clk_enable(struct clk *clk)
> +{
> +	int ret;
> +
> +	struct sama7_utmi_clk *utmi = to_sama7_utmi_clk(clk);
> +	u8 id = utmi->id;
> +
> +	ret = reset_assert(utmi->reset);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(utmi->regmap_sfr, SAMA7_SFR_UTMI0R(id),
> +				 SAMA7_SFR_UTMI_COMMONON, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = reset_deassert(utmi->reset);
> +	if (ret)
> +		return ret;
> +
> +	/* Datasheet states a minimum of 45 us before any USB operation */
> +	udelay(50);
> +
> +	return 0;
> +}
> +
> +static int sama7_utmi_clk_disable(struct clk *clk)
> +{
> +	int ret;
> +	struct sama7_utmi_clk *utmi = to_sama7_utmi_clk(clk);
> +	u8 id = utmi->id;
> +
> +	ret = reset_assert(utmi->reset);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(utmi->regmap_sfr, SAMA7_SFR_UTMI0R(id),
> +			   SAMA7_SFR_UTMI_COMMONON, SAMA7_SFR_UTMI_COMMONON);
> +
> +	return 0;
> +}
> +
> +static ulong sama7_utmi_clk_get_rate(struct clk *clk)
> +{
> +	/* Return utmick's rate: 480MHz */
> +	return clk_get_parent_rate(clk);
> +}
> +
> +static const struct clk_ops sama7_utmi_clk_ops = {
> +	.enable = sama7_utmi_clk_enable,
> +	.disable = sama7_utmi_clk_disable,
> +	.get_rate = sama7_utmi_clk_get_rate,
> +};
> +
> +static struct clk*
> +sama7_utmi_clk_register(struct regmap *regmap_sfr, struct reset_ctl *reset,
> +			const char *name, const char *parent_name, u8 id)
> +{
> +	struct clk *clk;
> +	struct sama7_utmi_clk *utmi_clk;
> +	int ret;
> +
> +	if (!regmap_sfr || !reset || !name || !parent_name)
> +		return ERR_PTR(-EINVAL);
> +
> +	utmi_clk = kzalloc(sizeof(*utmi_clk), GFP_KERNEL);
> +	if (!utmi_clk)
> +		return ERR_PTR(-ENOMEM);
> +
> +	utmi_clk->reset = reset;
> +	utmi_clk->regmap_sfr = regmap_sfr;
> +	utmi_clk->id = id;
> +
> +	clk = &utmi_clk->uclk;
> +	ret = clk_register(clk, UBOOT_DM_CLK_MICROCHIP_SAMA7G5_UTMI,
> +			   name, parent_name);
> +	if (ret) {
> +		kfree(utmi_clk);
> +		clk = ERR_PTR(ret);
> +	}
> +
> +	clk_dm(AT91_TO_CLK_ID(UTMI, utmi_clk->id), clk);
> +
> +	return clk;
> +}
> +
> +static int sama7_utmi_probe(struct udevice *dev)
> +{
> +	struct clk *utmi_parent_clk, *utmi_clk;
> +	struct regmap *regmap_sfr;
> +	struct reset_ctl *phy_reset;
> +	int i;
> +	char name[16];
> +
> +	utmi_parent_clk = devm_clk_get(dev, "utmi_clk");
> +	if (IS_ERR(utmi_parent_clk))
> +		return PTR_ERR(utmi_parent_clk);
> +
> +	regmap_sfr = syscon_regmap_lookup_by_phandle(dev, "sfr-phandle");
> +	if (IS_ERR(regmap_sfr))
> +		return PTR_ERR(regmap_sfr);
> +
> +	for (i = 0; i < ARRAY_SIZE(sama7_utmick); i++) {
> +		snprintf(name, sizeof(name), "usb%d_reset", i);
> +		phy_reset = devm_reset_control_get(dev, name);
> +		if (IS_ERR(phy_reset))
> +			return PTR_ERR(phy_reset);
> +
> +		utmi_clk = sama7_utmi_clk_register(regmap_sfr, phy_reset,
> +						   sama7_utmick[i].n,
> +						   sama7_utmick[i].p,
> +						   sama7_utmick[i].id);
> +		if (IS_ERR(utmi_clk))
> +			return PTR_ERR(utmi_clk);
> +	}
> +
> +	return 0;
> +};
> +
> +static const struct udevice_id sama7_utmi_clk_dt_ids[] = {
> +	{ .compatible = "microchip,sama7g5-utmi-clk", },
> +	{ /* sentinel */},
> +};
> +
> +static int utmi_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
> +{
> +	if (args->args_count != 1) {
> +		debug("UTMI: clk: Invalid args_count: %d\n", args->args_count);
> +		return -EINVAL;
> +	}
> +
> +	clk->id = AT91_TO_CLK_ID(UTMI, args->args[0]);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops sama7_utmi_ops = {
> +	.of_xlate	= utmi_clk_of_xlate,
> +	.enable		= ccf_clk_enable,
> +	.disable	= ccf_clk_disable,
> +};
> +
> +U_BOOT_DRIVER(microhip_sama7g5_utmi_clk) = {
> +	.name = UBOOT_DM_CLK_MICROCHIP_SAMA7G5_UTMI,
> +	.id = UCLASS_CLK,
> +	.ops = &sama7_utmi_clk_ops,
> +};
> +
> +U_BOOT_DRIVER(microhip_sama7g5_utmi) = {
> +	.name = UBOOT_DM_MICROCHIP_SAMA7G5_UTMI,
> +	.of_match = sama7_utmi_clk_dt_ids,
> +	.id = UCLASS_CLK,
> +	.ops = &sama7_utmi_ops,
> +	.probe = sama7_utmi_probe,
> +};



More information about the U-Boot mailing list