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.m...@microchip.com>
> Tested-by: Mihai Sain <mihai.s...@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.m...@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.m...@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,
> +};

Reply via email to