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, > +};