diff options
author | Tom Rini <trini@konsulko.com> | 2020-10-06 15:36:10 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2020-10-06 15:36:10 +0300 |
commit | 987ab49366f3fcd25039eab431bf099b587b3265 (patch) | |
tree | 75defe86fd35339b1b2e695ea1beebc0bbf1bbaf /drivers | |
parent | b24550accd7e3a62c6da773a9096dfd1471403d5 (diff) | |
parent | 2d481b2e3e22f7be854d381a7bafd92a65e18b23 (diff) | |
download | u-boot-987ab49366f3fcd25039eab431bf099b587b3265.tar.xz |
Merge tag 'u-boot-amlogic-20201005' of https://gitlab.denx.de/u-boot/custodians/u-boot-amlogic
- generate unique mac address from SoC serial on S400 board
- Add USB support for GXL and AXG SoCs
- Update Gadget code to use the new GXL and AXG USB glue driver
- Add a VIM3 board support to add dynamic PCIe enable in OS DT
- Fix AXG pinmux with requesting GPIOs
- Add missing GPIOA_18 for AXG pinctrl
- Add Amlogic PWM driver
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/phy/Kconfig | 2 | ||||
-rw-r--r-- | drivers/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/phy/meson-gxl-usb3.c | 219 | ||||
-rw-r--r-- | drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c | 5 | ||||
-rw-r--r-- | drivers/pinctrl/meson/pinctrl-meson-axg.c | 1 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 7 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-meson.c | 528 | ||||
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 8 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-meson-gxl.c | 425 |
11 files changed, 977 insertions, 222 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index d66aa07392..d12a6b02ad 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -177,7 +177,7 @@ config MESON_GXBB_USB_PHY config MESON_GXL_USB_PHY bool "Amlogic Meson GXL USB PHYs" - depends on PHY && ARCH_MESON && (MESON_GXL || MESON_GXM) + depends on PHY && ARCH_MESON && (MESON_GXL || MESON_GXM || MESON_AXG) imply REGMAP help This is the generic phy driver for the Amlogic Meson GXL diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 8dabefd776..45a7fe5b56 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -19,7 +19,7 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o obj-$(CONFIG_MESON_GXBB_USB_PHY) += meson-gxbb-usb2.o -obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o +obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o diff --git a/drivers/phy/meson-gxl-usb3.c b/drivers/phy/meson-gxl-usb3.c deleted file mode 100644 index 9de55bb5df..0000000000 --- a/drivers/phy/meson-gxl-usb3.c +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Meson GXL USB3 PHY driver - * - * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> - * Copyright (C) 2018 BayLibre, SAS - * Author: Neil Armstrong <narmstron@baylibre.com> - */ - -#include <common.h> -#include <malloc.h> -#include <asm/io.h> -#include <bitfield.h> -#include <dm.h> -#include <errno.h> -#include <generic-phy.h> -#include <regmap.h> -#include <clk.h> -#include <linux/usb/otg.h> - -#include <asm/arch/usb-gx.h> - -#include <linux/bitops.h> -#include <linux/compat.h> -#include <linux/bitfield.h> - -#define USB_R0 0x00 - #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) - #define USB_R0_P30_PHY_RESET BIT(6) - #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) - #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) - #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) - #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) - #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) - #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) - #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) - #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) - #define USB_R0_U2D_ACT BIT(31) - -#define USB_R1 0x04 - #define USB_R1_U3H_BIGENDIAN_GS BIT(0) - #define USB_R1_U3H_PME_ENABLE BIT(1) - #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) - #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) - #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) - #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) - #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) - #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) - #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) - #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) - -#define USB_R2 0x08 - #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) - #define USB_R2_P30_CR_READ BIT(16) - #define USB_R2_P30_CR_WRITE BIT(17) - #define USB_R2_P30_CR_CAP_ADDR BIT(18) - #define USB_R2_P30_CR_CAP_DATA BIT(19) - #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) - #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) - -#define USB_R3 0x0c - #define USB_R3_P30_SSC_ENABLE BIT(0) - #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) - #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) - #define USB_R3_P30_REF_SSP_EN BIT(13) - #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) - #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) - #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) - -#define USB_R4 0x10 - #define USB_R4_P21_PORT_RESET_0 BIT(0) - #define USB_R4_P21_SLEEP_M0 BIT(1) - #define USB_R4_MEM_PD_MASK GENMASK(3, 2) - #define USB_R4_P21_ONLY BIT(4) - -#define USB_R5 0x14 - #define USB_R5_ID_DIG_SYNC BIT(0) - #define USB_R5_ID_DIG_REG BIT(1) - #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) - #define USB_R5_ID_DIG_EN_0 BIT(4) - #define USB_R5_ID_DIG_EN_1 BIT(5) - #define USB_R5_ID_DIG_CURR BIT(6) - #define USB_R5_ID_DIG_IRQ BIT(7) - #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) - #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) - -/* read-only register */ -#define USB_R6 0x18 - #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) - #define USB_R6_P30_CR_ACK BIT(16) - -struct phy_meson_gxl_usb3_priv { - struct regmap *regmap; -#if CONFIG_IS_ENABLED(CLK) - struct clk clk; -#endif -}; - -void phy_meson_gxl_usb3_set_mode(struct phy *phy, enum usb_dr_mode mode) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - switch (mode) { - case USB_DR_MODE_UNKNOWN: - case USB_DR_MODE_HOST: - case USB_DR_MODE_OTG: - regmap_read(priv->regmap, USB_R0, &val); - val &= ~USB_R0_U2D_ACT; - regmap_write(priv->regmap, USB_R0, val); - - regmap_read(priv->regmap, USB_R4, &val); - val &= ~USB_R4_P21_SLEEP_M0; - regmap_write(priv->regmap, USB_R4, val); - break; - - case USB_DR_MODE_PERIPHERAL: - regmap_read(priv->regmap, USB_R0, &val); - val |= USB_R0_U2D_ACT; - regmap_write(priv->regmap, USB_R0, val); - - regmap_read(priv->regmap, USB_R4, &val); - val |= USB_R4_P21_SLEEP_M0; - regmap_write(priv->regmap, USB_R4, val); - break; - } -} - -static int phy_meson_gxl_usb3_power_on(struct phy *phy) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - regmap_read(priv->regmap, USB_R5, &val); - val |= USB_R5_ID_DIG_EN_0; - val |= USB_R5_ID_DIG_EN_1; - val &= ~USB_R5_ID_DIG_TH_MASK; - val |= FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff); - regmap_write(priv->regmap, USB_R5, val); - - phy_meson_gxl_usb3_set_mode(phy, USB_DR_MODE_HOST); - - return 0; -} - -static int phy_meson_gxl_usb3_power_off(struct phy *phy) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - regmap_read(priv->regmap, USB_R5, &val); - val &= ~USB_R5_ID_DIG_EN_0; - val &= ~USB_R5_ID_DIG_EN_1; - regmap_write(priv->regmap, USB_R5, val); - - return 0; -} - -static int phy_meson_gxl_usb3_init(struct phy *phy) -{ - struct udevice *dev = phy->dev; - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - uint val; - - regmap_read(priv->regmap, USB_R1, &val); - val &= ~USB_R1_U3H_FLADJ_30MHZ_REG_MASK; - val |= FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20); - regmap_write(priv->regmap, USB_R1, val); - - return 0; -} - -struct phy_ops meson_gxl_usb3_phy_ops = { - .init = phy_meson_gxl_usb3_init, - .power_on = phy_meson_gxl_usb3_power_on, - .power_off = phy_meson_gxl_usb3_power_off, -}; - -int meson_gxl_usb3_phy_probe(struct udevice *dev) -{ - struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); - int ret; - - ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); - if (ret) - return ret; - -#if CONFIG_IS_ENABLED(CLK) - ret = clk_get_by_index(dev, 0, &priv->clk); - if (ret < 0) - return ret; - - ret = clk_enable(&priv->clk); - if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { - pr_err("failed to enable PHY clock\n"); - clk_free(&priv->clk); - return ret; - } -#endif - - return 0; -} - -static const struct udevice_id meson_gxl_usb3_phy_ids[] = { - { .compatible = "amlogic,meson-gxl-usb3-phy" }, - { } -}; - -U_BOOT_DRIVER(meson_gxl_usb3_phy) = { - .name = "meson_gxl_usb3_phy", - .id = UCLASS_PHY, - .of_match = meson_gxl_usb3_phy_ids, - .probe = meson_gxl_usb3_phy_probe, - .ops = &meson_gxl_usb3_phy_ops, - .priv_auto_alloc_size = sizeof(struct phy_meson_gxl_usb3_priv), -}; diff --git a/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c b/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c index c6cb941d0a..cfe94cf9e1 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c +++ b/drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c @@ -165,7 +165,10 @@ const struct pinctrl_ops meson_axg_pinctrl_ops = { static int meson_axg_gpio_request(struct udevice *dev, unsigned int offset, const char *label) { - return meson_axg_pmx_update_function(dev->parent, offset, 0); + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + + return meson_axg_pmx_update_function(dev->parent, + offset + priv->data->pin_base, 0); } static const struct dm_gpio_ops meson_axg_gpio_ops = { diff --git a/drivers/pinctrl/meson/pinctrl-meson-axg.c b/drivers/pinctrl/meson/pinctrl-meson-axg.c index 8f23c8cef1..11809b29f3 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-axg.c +++ b/drivers/pinctrl/meson/pinctrl-meson-axg.c @@ -298,6 +298,7 @@ static struct meson_pmx_group meson_axg_periphs_groups[] = { GPIO_GROUP(GPIOA_15, EE_OFF), GPIO_GROUP(GPIOA_16, EE_OFF), GPIO_GROUP(GPIOA_17, EE_OFF), + GPIO_GROUP(GPIOA_18, EE_OFF), GPIO_GROUP(GPIOA_19, EE_OFF), GPIO_GROUP(GPIOA_20, EE_OFF), diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 61eb468cde..b3bd5c6bb7 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -23,6 +23,13 @@ config PWM_IMX help This PWM is found i.MX27 and later i.MX SoCs. +config PWM_MESON + bool "Enable support for Amlogic Meson SoCs PWM" + depends on DM_PWM + help + This PWM is found on Amlogic Meson SoCs. It supports a + programmable period and duty cycle for 2 independant channels. + config PWM_MTK bool "Enable support for MediaTek PWM" depends on DM_PWM diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0f4e84b04d..f21ae7d76e 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_DM_PWM) += pwm-uclass.o obj-$(CONFIG_PWM_EXYNOS) += exynos_pwm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o +obj-$(CONFIG_PWM_MESON) += pwm-meson.o obj-$(CONFIG_PWM_MTK) += pwm-mtk.o obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c new file mode 100644 index 0000000000..cafb571f16 --- /dev/null +++ b/drivers/pwm/pwm-meson.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Amlogic, Inc. + * + * This PWM is only a set of Gates, Dividers and Counters: + * PWM output is achieved by calculating a clock that permits calculating + * two periods (low and high). The counter then has to be set to switch after + * N cycles for the first half period. + * The hardware has no "polarity" setting. This driver reverses the period + * cycles (the low length is inverted with the high length) for + * PWM_POLARITY_INVERSED. + * Setting the polarity will disable and re-enable the PWM output. + * Disabling the PWM stops the output immediately (without waiting for the + * current period to complete first). + */ + +#include <common.h> +#include <clk.h> +#include <div64.h> +#include <dm.h> +#include <pwm.h> +#include <regmap.h> +#include <linux/io.h> +#include <linux/math64.h> +#include <linux/bitfield.h> +#include <linux/clk-provider.h> + +#define NSEC_PER_SEC 1000000000L + +#define REG_PWM_A 0x0 +#define REG_PWM_B 0x4 +#define PWM_LOW_MASK GENMASK(15, 0) +#define PWM_HIGH_MASK GENMASK(31, 16) + +#define REG_MISC_AB 0x8 +#define MISC_B_CLK_EN BIT(23) +#define MISC_A_CLK_EN BIT(15) +#define MISC_CLK_DIV_MASK 0x7f +#define MISC_B_CLK_DIV_SHIFT 16 +#define MISC_A_CLK_DIV_SHIFT 8 +#define MISC_B_CLK_SEL_SHIFT 6 +#define MISC_A_CLK_SEL_SHIFT 4 +#define MISC_CLK_SEL_MASK 0x3 +#define MISC_B_EN BIT(1) +#define MISC_A_EN BIT(0) + +#define MESON_NUM_PWMS 2 + +static struct meson_pwm_channel_data { + u8 reg_offset; + u8 clk_sel_shift; + u8 clk_div_shift; + u32 clk_en_mask; + u32 pwm_en_mask; +} meson_pwm_per_channel_data[MESON_NUM_PWMS] = { + { + .reg_offset = REG_PWM_A, + .clk_sel_shift = MISC_A_CLK_SEL_SHIFT, + .clk_div_shift = MISC_A_CLK_DIV_SHIFT, + .clk_en_mask = MISC_A_CLK_EN, + .pwm_en_mask = MISC_A_EN, + }, + { + .reg_offset = REG_PWM_B, + .clk_sel_shift = MISC_B_CLK_SEL_SHIFT, + .clk_div_shift = MISC_B_CLK_DIV_SHIFT, + .clk_en_mask = MISC_B_CLK_EN, + .pwm_en_mask = MISC_B_EN, + } +}; + +struct meson_pwm_channel { + unsigned int hi; + unsigned int lo; + u8 pre_div; + uint period_ns; + uint duty_ns; + bool configured; + bool enabled; + bool polarity; + struct clk clk; +}; + +struct meson_pwm_data { + const long *parent_ids; + unsigned int num_parents; +}; + +struct meson_pwm { + const struct meson_pwm_data *data; + struct meson_pwm_channel channels[MESON_NUM_PWMS]; + void __iomem *base; +}; + +static int meson_pwm_set_enable(struct udevice *dev, uint channel, bool enable); + +static int meson_pwm_set_config(struct udevice *dev, uint channeln, + uint period_ns, uint duty_ns) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_channel *channel; + struct meson_pwm_channel_data *channel_data; + unsigned int duty, period, pre_div, cnt, duty_cnt; + unsigned long fin_freq; + + if (channeln >= MESON_NUM_PWMS) + return -ENODEV; + + channel = &priv->channels[channeln]; + channel_data = &meson_pwm_per_channel_data[channeln]; + + period = period_ns; + if (channel->polarity) + duty = period_ns - duty_ns; + else + duty = duty_ns; + + debug("%s%d: polarity %s duty %d period %d\n", __func__, channeln, + channel->polarity ? "true" : "false", duty, period); + + fin_freq = clk_get_rate(&channel->clk); + if (fin_freq == 0) { + printf("%s%d: invalid source clock frequency\n", __func__, channeln); + return -EINVAL; + } + + debug("%s%d: fin_freq: %lu Hz\n", __func__, channeln, fin_freq); + + pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL); + if (pre_div > MISC_CLK_DIV_MASK) { + printf("%s%d: unable to get period pre_div\n", __func__, channeln); + return -EINVAL; + } + + cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1)); + if (cnt > 0xffff) { + printf("%s%d: unable to get period cnt\n", __func__, channeln); + return -EINVAL; + } + + debug("%s%d: period=%u pre_div=%u cnt=%u\n", __func__, channeln, period, pre_div, cnt); + + if (duty == period) { + channel->pre_div = pre_div; + channel->hi = cnt; + channel->lo = 0; + } else if (duty == 0) { + channel->pre_div = pre_div; + channel->hi = 0; + channel->lo = cnt; + } else { + /* Then check is we can have the duty with the same pre_div */ + duty_cnt = div64_u64(fin_freq * (u64)duty, NSEC_PER_SEC * (pre_div + 1)); + if (duty_cnt > 0xffff) { + printf("%s%d: unable to get duty cycle\n", __func__, channeln); + return -EINVAL; + } + + debug("%s%d: duty=%u pre_div=%u duty_cnt=%u\n", + __func__, channeln, duty, pre_div, duty_cnt); + + channel->pre_div = pre_div; + channel->hi = duty_cnt; + channel->lo = cnt - duty_cnt; + } + + channel->period_ns = period_ns; + channel->duty_ns = duty_ns; + channel->configured = true; + + if (channel->enabled) { + meson_pwm_set_enable(dev, channeln, false); + meson_pwm_set_enable(dev, channeln, true); + } + + return 0; +} + +static int meson_pwm_set_enable(struct udevice *dev, uint channeln, bool enable) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_channel *channel; + struct meson_pwm_channel_data *channel_data; + u32 value; + + if (channeln >= MESON_NUM_PWMS) + return -ENODEV; + + channel = &priv->channels[channeln]; + channel_data = &meson_pwm_per_channel_data[channeln]; + + if (!channel->configured) + return -EINVAL; + + if (enable) { + if (channel->enabled) + return 0; + + value = readl(priv->base + REG_MISC_AB); + value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift); + value |= channel->pre_div << channel_data->clk_div_shift; + value |= channel_data->clk_en_mask; + writel(value, priv->base + REG_MISC_AB); + + value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | + FIELD_PREP(PWM_LOW_MASK, channel->lo); + writel(value, priv->base + channel_data->reg_offset); + + value = readl(priv->base + REG_MISC_AB); + value |= channel_data->pwm_en_mask; + writel(value, priv->base + REG_MISC_AB); + + debug("%s%d: enabled\n", __func__, channeln); + channel->enabled = true; + } else { + if (!channel->enabled) + return 0; + + value = readl(priv->base + REG_MISC_AB); + value &= channel_data->pwm_en_mask; + writel(value, priv->base + REG_MISC_AB); + + debug("%s%d: disabled\n", __func__, channeln); + channel->enabled = false; + } + + return 0; +} + +static int meson_pwm_set_invert(struct udevice *dev, uint channeln, bool polarity) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_channel *channel; + + if (channeln >= MESON_NUM_PWMS) + return -ENODEV; + + debug("%s%d: set invert %s\n", __func__, channeln, polarity ? "true" : "false"); + + channel = &priv->channels[channeln]; + + channel->polarity = polarity; + + if (!channel->configured) + return 0; + + return meson_pwm_set_config(dev, channeln, channel->period_ns, channel->duty_ns); +} + +static int meson_pwm_ofdata_to_platdata(struct udevice *dev) +{ + struct meson_pwm *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev); + + return 0; +} + +static int meson_pwm_probe(struct udevice *dev) +{ + struct meson_pwm *priv = dev_get_priv(dev); + struct meson_pwm_data *data; + unsigned int i, p; + char name[255]; + int err; + u32 reg; + + data = (struct meson_pwm_data *)dev_get_driver_data(dev); + if (!data) + return -EINVAL; + + for (i = 0; i < MESON_NUM_PWMS; i++) { + struct meson_pwm_channel *channel = &priv->channels[i]; + struct meson_pwm_channel_data *channel_data = &meson_pwm_per_channel_data[i]; + + snprintf(name, sizeof(name), "clkin%u", i); + + err = clk_get_by_name(dev, name, &channel->clk); + /* If clock is not specified, use the already set clock */ + if (err == -ENODATA) { + struct udevice *cdev; + struct uclass *uc; + + /* Get parent from mux */ + p = (readl(priv->base + REG_MISC_AB) >> channel_data->clk_sel_shift) & + MISC_CLK_SEL_MASK; + + if (p >= data->num_parents) { + printf("%s%d: hw parent is invalid\n", __func__, i); + return -EINVAL; + } + + if (data->parent_ids[p] == -1) { + /* Search for xtal clk */ + const char *str; + + err = uclass_get(UCLASS_CLK, &uc); + if (err) + return err; + + uclass_foreach_dev(cdev, uc) { + if (strcmp(cdev->driver->name, "fixed_rate_clock")) + continue; + + str = ofnode_read_string(cdev->node, "clock-output-names"); + if (!str) + continue; + + if (!strcmp(str, "xtal")) { + err = uclass_get_device_by_ofnode(UCLASS_CLK, + cdev->node, + &cdev); + if (err) { + printf("%s%d: Failed to get xtal clk\n", __func__, i); + return err; + } + + break; + } + } + + if (!cdev) { + printf("%s%d: Failed to find xtal clk device\n", __func__, i); + return -EINVAL; + } + + channel->clk.dev = cdev; + channel->clk.id = 0; + channel->clk.data = 0; + } else { + /* Look for parent clock */ + err = uclass_get(UCLASS_CLK, &uc); + if (err) + return err; + + uclass_foreach_dev(cdev, uc) { + if (strstr(cdev->driver->name, "meson_clk")) + break; + } + + if (!cdev) { + printf("%s%d: Failed to find clk device\n", __func__, i); + return -EINVAL; + } + + err = uclass_get_device_by_ofnode(UCLASS_CLK, cdev->node, &cdev); + if (err) { + printf("%s%d: Failed to get clk controller\n", __func__, i); + return err; + } + + channel->clk.dev = cdev; + channel->clk.id = data->parent_ids[p]; + channel->clk.data = 0; + } + + /* We have our source clock, do not alter HW clock mux */ + continue; + } else + return err; + + /* Get id in list */ + for (p = 0 ; p < data->num_parents ; ++p) { + if (!strcmp(channel->clk.dev->driver->name, "fixed_rate_clock")) { + if (data->parent_ids[p] == -1) + break; + } else { + if (data->parent_ids[p] == channel->clk.id) + break; + } + } + + /* Invalid clock ID */ + if (p == data->num_parents) { + printf("%s%d: source clock is invalid\n", __func__, i); + return -EINVAL; + } + + /* switch parent in mux */ + reg = readl(priv->base + REG_MISC_AB); + + debug("%s%d: switching parent %d to %d\n", __func__, i, + (reg >> channel_data->clk_sel_shift) & MISC_CLK_SEL_MASK, p); + + reg &= MISC_CLK_SEL_MASK << channel_data->clk_sel_shift; + reg |= (p & MISC_CLK_SEL_MASK) << channel_data->clk_sel_shift; + writel(reg, priv->base + REG_MISC_AB); + } + + return 0; +} + +static const struct pwm_ops meson_pwm_ops = { + .set_config = meson_pwm_set_config, + .set_enable = meson_pwm_set_enable, + .set_invert = meson_pwm_set_invert, +}; + +#define XTAL -1 + +/* Local clock ids aliases to avoid define conflicts */ +#define GXBB_CLKID_HDMI_PLL 2 +#define GXBB_CLKID_FCLK_DIV3 5 +#define GXBB_CLKID_FCLK_DIV4 6 +#define GXBB_CLKID_CLK81 12 + +static const long pwm_gxbb_parent_ids[] = { + XTAL, GXBB_CLKID_HDMI_PLL, GXBB_CLKID_FCLK_DIV4, GXBB_CLKID_FCLK_DIV3 +}; + +static const struct meson_pwm_data pwm_gxbb_data = { + .parent_ids = pwm_gxbb_parent_ids, + .num_parents = ARRAY_SIZE(pwm_gxbb_parent_ids), +}; + +/* + * Only the 2 first inputs of the GXBB AO PWMs are valid + * The last 2 are grounded + */ +static const long pwm_gxbb_ao_parent_ids[] = { + XTAL, GXBB_CLKID_CLK81 +}; + +static const struct meson_pwm_data pwm_gxbb_ao_data = { + .parent_ids = pwm_gxbb_ao_parent_ids, + .num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_ids), +}; + +/* Local clock ids aliases to avoid define conflicts */ +#define AXG_CLKID_FCLK_DIV3 3 +#define AXG_CLKID_FCLK_DIV4 4 +#define AXG_CLKID_FCLK_DIV5 5 +#define AXG_CLKID_CLK81 10 + +static const long pwm_axg_ee_parent_ids[] = { + XTAL, AXG_CLKID_FCLK_DIV5, AXG_CLKID_FCLK_DIV4, AXG_CLKID_FCLK_DIV3 +}; + +static const struct meson_pwm_data pwm_axg_ee_data = { + .parent_ids = pwm_axg_ee_parent_ids, + .num_parents = ARRAY_SIZE(pwm_axg_ee_parent_ids), +}; + +static const long pwm_axg_ao_parent_ids[] = { + AXG_CLKID_CLK81, XTAL, AXG_CLKID_FCLK_DIV4, AXG_CLKID_FCLK_DIV5 +}; + +static const struct meson_pwm_data pwm_axg_ao_data = { + .parent_ids = pwm_axg_ao_parent_ids, + .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_ids), +}; + +/* Local clock ids aliases to avoid define conflicts */ +#define G12A_CLKID_FCLK_DIV3 3 +#define G12A_CLKID_FCLK_DIV4 4 +#define G12A_CLKID_FCLK_DIV5 5 +#define G12A_CLKID_CLK81 10 +#define G12A_CLKID_HDMI_PLL 128 + +static const long pwm_g12a_ao_ab_parent_ids[] = { + XTAL, G12A_CLKID_CLK81, G12A_CLKID_FCLK_DIV4, G12A_CLKID_FCLK_DIV5 +}; + +static const struct meson_pwm_data pwm_g12a_ao_ab_data = { + .parent_ids = pwm_g12a_ao_ab_parent_ids, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_ids), +}; + +static const long pwm_g12a_ao_cd_parent_ids[] = { + XTAL, G12A_CLKID_CLK81, +}; + +static const struct meson_pwm_data pwm_g12a_ao_cd_data = { + .parent_ids = pwm_g12a_ao_cd_parent_ids, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_ids), +}; + +static const long pwm_g12a_ee_parent_ids[] = { + XTAL, G12A_CLKID_HDMI_PLL, G12A_CLKID_FCLK_DIV4, G12A_CLKID_FCLK_DIV3 +}; + +static const struct meson_pwm_data pwm_g12a_ee_data = { + .parent_ids = pwm_g12a_ee_parent_ids, + .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_ids), +}; + +static const struct udevice_id meson_pwm_ids[] = { + { + .compatible = "amlogic,meson-gxbb-pwm", + .data = (ulong)&pwm_gxbb_data + }, + { + .compatible = "amlogic,meson-gxbb-ao-pwm", + .data = (ulong)&pwm_gxbb_ao_data + }, + { + .compatible = "amlogic,meson-axg-ee-pwm", + .data = (ulong)&pwm_axg_ee_data + }, + { + .compatible = "amlogic,meson-axg-ao-pwm", + .data = (ulong)&pwm_axg_ao_data + }, + { + .compatible = "amlogic,meson-g12a-ee-pwm", + .data = (ulong)&pwm_g12a_ee_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-ab", + .data = (ulong)&pwm_g12a_ao_ab_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-cd", + .data = (ulong)&pwm_g12a_ao_cd_data + }, +}; + +U_BOOT_DRIVER(meson_pwm) = { + .name = "meson_pwm", + .id = UCLASS_PWM, + .of_match = meson_pwm_ids, + .ops = &meson_pwm_ops, + .ofdata_to_platdata = meson_pwm_ofdata_to_platdata, + .probe = meson_pwm_probe, + .priv_auto_alloc_size = sizeof(struct meson_pwm), +}; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index c302486291..802ee508d9 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -38,6 +38,14 @@ config USB_DWC3_MESON_G12A Select this for Amlogic Meson G12A Platforms. This wrapper supports Host and Peripheral operation modes. +config USB_DWC3_MESON_GXL + bool "Amlogic Meson GXL USB wrapper" + depends on DM_USB && USB_DWC3 && ARCH_MESON + imply PHY + help + Select this for Amlogic Meson GXL and GXM Platforms. + This wrapper supports Host and Peripheral operation modes. + config USB_DWC3_UNIPHIER bool "DesignWare USB3 Host Support on UniPhier Platforms" depends on ARCH_UNIPHIER && USB_XHCI_DWC3 diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 0b652a6f36..6e3e024e97 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o +obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o obj-$(CONFIG_USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o diff --git a/drivers/usb/dwc3/dwc3-meson-gxl.c b/drivers/usb/dwc3/dwc3-meson-gxl.c new file mode 100644 index 0000000000..92ee0866f7 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-gxl.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic GXL DWC3 Glue layer + * + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#define DEBUG +#include <common.h> +#include <asm-generic/io.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dwc3-uboot.h> +#include <generic-phy.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <malloc.h> +#include <regmap.h> +#include <usb.h> +#include "core.h" +#include "gadget.h" +#include <reset.h> +#include <clk.h> +#include <power/regulator.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/compat.h> +#include <asm/arch/usb-gx.h> + +/* USB Glue Control Registers */ + +#define USB_R0 0x00 + #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) + #define USB_R0_P30_PHY_RESET BIT(6) + #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) + #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) + #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) + #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x04 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x08 + #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) + #define USB_R2_P30_CR_READ BIT(16) + #define USB_R2_P30_CR_WRITE BIT(17) + #define USB_R2_P30_CR_CAP_ADDR BIT(18) + #define USB_R2_P30_CR_CAP_DATA BIT(19) + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x0c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) + #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) + #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) + +#define USB_R4 0x10 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x14 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +/* read-only register */ +#define USB_R6 0x18 + #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) + #define USB_R6_P30_CR_ACK BIT(16) + +enum { + USB2_HOST_PHY0 = 0, + USB2_OTG_PHY1, + USB2_HOST_PHY2, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb2-phy2", +}; + +struct dwc3_meson_gxl { + struct udevice *dev; + struct regmap *regmap; + struct clk clk; + struct reset_ctl reset; + struct phy phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum usb_dr_mode otg_phy_mode; + unsigned int usb2_ports; +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *vbus_supply; +#endif +}; + +#define U2P_REG_SIZE 0x20 +#define USB_REG_OFFSET 0x80 + +#define USB2_OTG_PHY USB2_OTG_PHY1 + +static void dwc3_meson_gxl_usb2_set_mode(struct dwc3_meson_gxl *priv, enum usb_dr_mode mode) +{ + switch (mode) { + case USB_DR_MODE_HOST: + case USB_DR_MODE_OTG: + case USB_DR_MODE_UNKNOWN: + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, 0); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + break; + } +} + +static int dwc3_meson_gxl_usb2_init(struct dwc3_meson_gxl *priv) +{ + int i; + + for (i = 0; i < PHY_COUNT; ++i) { + if (!priv->phys[i].dev) + continue; + + phy_meson_gxl_usb2_set_mode(&priv->phys[i], + (i == USB2_OTG_PHY) ? USB_DR_MODE_PERIPHERAL + : USB_DR_MODE_HOST); + } + + return 0; +} + +static int dwc3_meson_gxl_usb_init(struct dwc3_meson_gxl *priv) +{ + int ret; + + ret = dwc3_meson_gxl_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + dwc3_meson_gxl_usb2_set_mode(priv, priv->otg_phy_mode); + + return 0; +} + +int dwc3_meson_gxl_force_mode(struct udevice *dev, enum usb_dr_mode mode) +{ + struct dwc3_meson_gxl *priv = dev_get_platdata(dev); + + if (!priv) + return -EINVAL; + + if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL) + return -EINVAL; + + if (!priv->phys[USB2_OTG_PHY].dev) + return -EINVAL; + + if (mode == priv->otg_phy_mode) + return 0; + + if (mode == USB_DR_MODE_HOST) + debug("%s: switching to Host Mode\n", __func__); + else + debug("%s: switching to Device Mode\n", __func__); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->vbus_supply) { + int ret = regulator_set_enable(priv->vbus_supply, + (mode == USB_DR_MODE_PERIPHERAL)); + if (ret) + return ret; + } +#endif + priv->otg_phy_mode = mode; + + phy_meson_gxl_usb2_set_mode(&priv->phys[USB2_OTG_PHY], mode); + + dwc3_meson_gxl_usb2_set_mode(priv, mode); + + return 0; +} + +static int dwc3_meson_gxl_get_phys(struct dwc3_meson_gxl *priv) +{ + int i, ret; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = generic_phy_get_by_name(priv->dev, phy_names[i], + &priv->phys[i]); + if (ret == -ENOENT || ret == -ENODATA) { + priv->phys[i].dev = NULL; + continue; + } + + if (ret) + return ret; + + priv->usb2_ports++; + } + + debug("%s: usb2 ports: %d\n", __func__, priv->usb2_ports); + + return 0; +} + +static int dwc3_meson_gxl_reset_init(struct dwc3_meson_gxl *priv) +{ + int ret; + + ret = reset_get_by_index(priv->dev, 0, &priv->reset); + if (ret) + return ret; + + ret = reset_assert(&priv->reset); + udelay(1); + ret |= reset_deassert(&priv->reset); + if (ret) { + reset_free(&priv->reset); + return ret; + } + + return 0; +} + +static int dwc3_meson_gxl_clk_init(struct dwc3_meson_gxl *priv) +{ + int ret; + + ret = clk_get_by_index(priv->dev, 0, &priv->clk); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->clk); + if (ret) { + clk_free(&priv->clk); + return ret; + } +#endif + + return 0; +} + +static int dwc3_meson_gxl_probe(struct udevice *dev) +{ + struct dwc3_meson_gxl *priv = dev_get_platdata(dev); + int ret, i; + + priv->dev = dev; + + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); + if (ret) + return ret; + + ret = dwc3_meson_gxl_clk_init(priv); + if (ret) + return ret; + + ret = dwc3_meson_gxl_reset_init(priv); + if (ret) + return ret; + + ret = dwc3_meson_gxl_get_phys(priv); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + ret = device_get_supply_regulator(dev, "vbus-supply", + &priv->vbus_supply); + if (ret && ret != -ENOENT) { + pr_err("Failed to get PHY regulator\n"); + return ret; + } + + if (priv->vbus_supply) { + ret = regulator_set_enable(priv->vbus_supply, true); + if (ret) + return ret; + } +#endif + + /* On GXL PHY must be started in device mode for DWC2 init */ + priv->otg_mode = USB_DR_MODE_PERIPHERAL; + + ret = dwc3_meson_gxl_usb_init(priv); + if (ret) + return ret; + + priv->otg_mode = usb_get_dr_mode(dev->node); + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL; + else + priv->otg_phy_mode = USB_DR_MODE_HOST; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + ret = generic_phy_init(&priv->phys[i]); + if (ret) + goto err_phy_init; + } + + for (i = 0; i < PHY_COUNT; ++i) { + if (!priv->phys[i].dev) + continue; + + ret = generic_phy_power_on(&priv->phys[i]); + if (ret) + goto err_phy_init; + } + + if (priv->phys[USB2_OTG_PHY].dev) + phy_meson_gxl_usb2_set_mode(&priv->phys[USB2_OTG_PHY], + priv->otg_phy_mode); + + dwc3_meson_gxl_usb2_set_mode(priv, priv->otg_phy_mode); + + return 0; + +err_phy_init: + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_exit(&priv->phys[i]); + } + + return ret; +} + +static int dwc3_meson_gxl_remove(struct udevice *dev) +{ + struct dwc3_meson_gxl *priv = dev_get_platdata(dev); + int i; + + reset_release_all(&priv->reset, 1); + + clk_release_all(&priv->clk, 1); + + for (i = 0; i < PHY_COUNT; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_power_off(&priv->phys[i]); + } + + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_exit(&priv->phys[i]); + } + + return dm_scan_fdt_dev(dev); +} + +static const struct udevice_id dwc3_meson_gxl_ids[] = { + { .compatible = "amlogic,meson-gxl-usb-ctrl" }, + { .compatible = "amlogic,meson-gxm-usb-ctrl" }, + { } +}; + +U_BOOT_DRIVER(dwc3_generic_wrapper) = { + .name = "dwc3-meson-gxl", + .id = UCLASS_SIMPLE_BUS, + .of_match = dwc3_meson_gxl_ids, + .probe = dwc3_meson_gxl_probe, + .remove = dwc3_meson_gxl_remove, + .platdata_auto_alloc_size = sizeof(struct dwc3_meson_gxl), + +}; |