[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