- Add USB PHY drivers for Amlogic G12A
- Add USB Complex Glue driver for Amlogic G12A - Add USB Gadget support for Amlogic G12A -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJc0+dtAAoJEHfc29rIyEnRAVgP/1fcyaNiq2kv3sMaWqxQGuWZ Nct5LxZFjKuWfQiWT3T1TzDMHWrt0TYYcK3gxRZF5qCaSVCR+OBgu6r8z6uyNQ/s 4QxlDzFbIIx4O2hAVWu0XuNsmrAD6+B5QkJRGFNMhmiToJHCkpIqwa+O5s/5bI9w tztC4lYsJuNf9yYRHxBqA5daOeh1YG2ZBqVv9PZhK5wgo6ELtK2u8ZDixFvdIemu XabBAuRf4sqwtj8PDia9VSSiy8z8uXaTdTCJ7P5Z2IG7wM9kfHJ4HDGsrOUkx5l3 9Hsrs+QkgXBNWe9lIJpsmN3uMDZ3jEwaTfVMa7e7gWEauOzXqWW5h+ecxHXxC/F0 UlhAYxyDyhsELKCRbUCZeBCekgLWo2316qrCVBnMA9W7Ee2VCiWsRQn1iw1C88t/ 67ts+NVsjdfB5S0qn+FtpuIjlUM6z5mXvvxkHIpsLKNJNiMQXxNRWNnmoFtsr5z8 X0fsGGOO+cf1q5X6Cy3ZlTIM5dB/SH6Z4gJ1RwXCzhy8Vq9yQergMjnrIj8vDIIn x2e3xB4VEKYiJQWVs4eobpwtLlQRbzp26IpxU6mDY3/oK5Tob0CgrILE7tL7eVnf fw0rB5ldY/Ya/63s5vEfi3SZ+Ag9XyY+gn2hJ/k5uTk8mnqewt3VcDFYt3oNavez OxLRZzU/vZp0+gd8KZY2 =mn/6 -----END PGP SIGNATURE----- Merge tag 'u-boot-amlogic-20190509' of git://git.denx.de/u-boot-amlogic - Add USB PHY drivers for Amlogic G12A - Add USB Complex Glue driver for Amlogic G12A - Add USB Gadget support for Amlogic G12A
This commit is contained in:
commit
7aaf2af0e0
12
arch/arm/include/asm/arch-meson/usb.h
Normal file
12
arch/arm/include/asm/arch-meson/usb.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_USB_H__
|
||||
#define __MESON_USB_H__
|
||||
|
||||
int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode);
|
||||
|
||||
#endif /* __MESON_USB_H__ */
|
@ -12,7 +12,12 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/armv8/mmu.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <usb.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <asm/arch/usb.h>
|
||||
#include <usb/dwc2_udc.h>
|
||||
#include <phy.h>
|
||||
#include <clk.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -148,3 +153,124 @@ void meson_eth_init(phy_interface_t mode, unsigned int flags)
|
||||
/* Enable power gate */
|
||||
clrbits_le32(G12A_MEM_PD_REG_0, G12A_MEM_PD_REG_0_ETH_MASK);
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(USB_DWC3_MESON_G12A) && \
|
||||
CONFIG_IS_ENABLED(USB_GADGET_DWC2_OTG)
|
||||
static struct dwc2_plat_otg_data meson_g12a_dwc2_data;
|
||||
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
struct fdtdec_phandle_args args;
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node, dwc2_node;
|
||||
struct udevice *dev, *clk_dev;
|
||||
struct clk clk;
|
||||
int ret;
|
||||
|
||||
/* find the usb glue node */
|
||||
node = fdt_node_offset_by_compatible(blob, -1,
|
||||
"amlogic,meson-g12a-usb-ctrl");
|
||||
if (node < 0) {
|
||||
debug("Not found usb-control node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!fdtdec_get_is_enabled(blob, node)) {
|
||||
debug("usb is disabled in the device tree\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
|
||||
if (ret) {
|
||||
debug("Not found usb-control device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* find the dwc2 node */
|
||||
dwc2_node = fdt_node_offset_by_compatible(blob, node,
|
||||
"amlogic,meson-g12a-usb");
|
||||
if (dwc2_node < 0) {
|
||||
debug("Not found dwc2 node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!fdtdec_get_is_enabled(blob, dwc2_node)) {
|
||||
debug("dwc2 is disabled in the device tree\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
meson_g12a_dwc2_data.regs_otg = fdtdec_get_addr(blob, dwc2_node, "reg");
|
||||
if (meson_g12a_dwc2_data.regs_otg == FDT_ADDR_T_NONE) {
|
||||
debug("usbotg: can't get base address\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* Enable clock */
|
||||
ret = fdtdec_parse_phandle_with_args(blob, dwc2_node, "clocks",
|
||||
"#clock-cells", 0, 0, &args);
|
||||
if (ret) {
|
||||
debug("usbotg has no clocks defined in the device tree\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &clk_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (args.args_count != 1) {
|
||||
debug("Can't find clock ID in the device tree\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
clk.dev = clk_dev;
|
||||
clk.id = args.args[0];
|
||||
|
||||
ret = clk_enable(&clk);
|
||||
if (ret) {
|
||||
debug("Failed to enable usbotg clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
meson_g12a_dwc2_data.rx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
|
||||
"g-rx-fifo-size", 0);
|
||||
meson_g12a_dwc2_data.np_tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
|
||||
"g-np-tx-fifo-size", 0);
|
||||
meson_g12a_dwc2_data.tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
|
||||
"g-tx-fifo-size", 0);
|
||||
|
||||
/* Switch to peripheral mode */
|
||||
ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_PERIPHERAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc2_udc_probe(&meson_g12a_dwc2_data);
|
||||
}
|
||||
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct udevice *dev;
|
||||
int node;
|
||||
int ret;
|
||||
|
||||
/* find the usb glue node */
|
||||
node = fdt_node_offset_by_compatible(blob, -1,
|
||||
"amlogic,meson-g12a-usb-ctrl");
|
||||
if (node < 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (!fdtdec_get_is_enabled(blob, node))
|
||||
return -ENODEV;
|
||||
|
||||
ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Switch to OTG mode */
|
||||
ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_HOST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -147,6 +147,14 @@ config MESON_GXL_USB_PHY
|
||||
This is the generic phy driver for the Amlogic Meson GXL
|
||||
USB2 and USB3 PHYS.
|
||||
|
||||
config MESON_G12A_USB_PHY
|
||||
bool "Amlogic Meson G12A USB PHYs"
|
||||
depends on PHY && ARCH_MESON && MESON_G12A
|
||||
imply REGMAP
|
||||
help
|
||||
This is the generic phy driver for the Amlogic Meson G12A
|
||||
USB2 and USB3 PHYS.
|
||||
|
||||
config MSM8916_USB_PHY
|
||||
bool "Qualcomm MSM8916 USB PHY support"
|
||||
depends on PHY
|
||||
|
@ -16,6 +16,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_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.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
|
||||
obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o
|
||||
|
216
drivers/phy/meson-g12a-usb2.c
Normal file
216
drivers/phy/meson-g12a-usb2.c
Normal file
@ -0,0 +1,216 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Meson G12A USB2 PHY driver
|
||||
*
|
||||
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstron@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <bitfield.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <generic-phy.h>
|
||||
#include <regmap.h>
|
||||
#include <power/regulator.h>
|
||||
#include <reset.h>
|
||||
#include <clk.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#define PHY_CTRL_R0 0x0
|
||||
#define PHY_CTRL_R1 0x4
|
||||
#define PHY_CTRL_R2 0x8
|
||||
#define PHY_CTRL_R3 0xc
|
||||
#define PHY_CTRL_R4 0x10
|
||||
#define PHY_CTRL_R5 0x14
|
||||
#define PHY_CTRL_R6 0x18
|
||||
#define PHY_CTRL_R7 0x1c
|
||||
#define PHY_CTRL_R8 0x20
|
||||
#define PHY_CTRL_R9 0x24
|
||||
#define PHY_CTRL_R10 0x28
|
||||
#define PHY_CTRL_R11 0x2c
|
||||
#define PHY_CTRL_R12 0x30
|
||||
#define PHY_CTRL_R13 0x34
|
||||
#define PHY_CTRL_R14 0x38
|
||||
#define PHY_CTRL_R15 0x3c
|
||||
#define PHY_CTRL_R16 0x40
|
||||
#define PHY_CTRL_R17 0x44
|
||||
#define PHY_CTRL_R18 0x48
|
||||
#define PHY_CTRL_R19 0x4c
|
||||
#define PHY_CTRL_R20 0x50
|
||||
#define PHY_CTRL_R21 0x54
|
||||
#define PHY_CTRL_R22 0x58
|
||||
#define PHY_CTRL_R23 0x5c
|
||||
|
||||
#define RESET_COMPLETE_TIME 1000
|
||||
#define PLL_RESET_COMPLETE_TIME 100
|
||||
|
||||
struct phy_meson_g12a_usb2_priv {
|
||||
struct regmap *regmap;
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
struct udevice *phy_supply;
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
struct clk clk;
|
||||
#endif
|
||||
struct reset_ctl reset;
|
||||
};
|
||||
|
||||
|
||||
static int phy_meson_g12a_usb2_power_on(struct phy *phy)
|
||||
{
|
||||
struct udevice *dev = phy->dev;
|
||||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
if (priv->phy_supply) {
|
||||
int ret = regulator_set_enable(priv->phy_supply, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_g12a_usb2_power_off(struct phy *phy)
|
||||
{
|
||||
struct udevice *dev = phy->dev;
|
||||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
if (priv->phy_supply) {
|
||||
int ret = regulator_set_enable(priv->phy_supply, false);
|
||||
if (ret) {
|
||||
pr_err("Error disabling PHY supply\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_g12a_usb2_init(struct phy *phy)
|
||||
{
|
||||
struct udevice *dev = phy->dev;
|
||||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = reset_assert(&priv->reset);
|
||||
udelay(1);
|
||||
ret |= reset_deassert(&priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(RESET_COMPLETE_TIME);
|
||||
|
||||
/* usb2_otg_aca_en == 0 */
|
||||
regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
|
||||
|
||||
/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
|
||||
|
||||
udelay(PLL_RESET_COMPLETE_TIME);
|
||||
|
||||
/* UnReset PLL */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
|
||||
|
||||
/* PHY Tuning */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff);
|
||||
|
||||
/* Tuning Disconnect Threshold */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R3, 0x34);
|
||||
|
||||
/* Analog Settings */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R14, 0);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_g12a_usb2_exit(struct phy *phy)
|
||||
{
|
||||
struct udevice *dev = phy->dev;
|
||||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = reset_assert(&priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct phy_ops meson_g12a_usb2_phy_ops = {
|
||||
.init = phy_meson_g12a_usb2_init,
|
||||
.exit = phy_meson_g12a_usb2_exit,
|
||||
.power_on = phy_meson_g12a_usb2_power_on,
|
||||
.power_off = phy_meson_g12a_usb2_power_off,
|
||||
};
|
||||
|
||||
int meson_g12a_usb2_phy_probe(struct udevice *dev)
|
||||
{
|
||||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_get_by_index(dev, 0, &priv->reset);
|
||||
if (ret == -ENOTSUPP)
|
||||
return 0;
|
||||
else if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_deassert(&priv->reset);
|
||||
if (ret) {
|
||||
reset_release_all(&priv->reset, 1);
|
||||
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
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
ret = device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply);
|
||||
if (ret && ret != -ENOENT) {
|
||||
pr_err("Failed to get PHY regulator\n");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id meson_g12a_usb2_phy_ids[] = {
|
||||
{ .compatible = "amlogic,g12a-usb2-phy" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(meson_g12a_usb2_phy) = {
|
||||
.name = "meson_g12a_usb2_phy",
|
||||
.id = UCLASS_PHY,
|
||||
.of_match = meson_g12a_usb2_phy_ids,
|
||||
.probe = meson_g12a_usb2_phy_probe,
|
||||
.ops = &meson_g12a_usb2_phy_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct phy_meson_g12a_usb2_priv),
|
||||
};
|
345
drivers/phy/meson-g12a-usb3-pcie.c
Normal file
345
drivers/phy/meson-g12a-usb3-pcie.c
Normal file
@ -0,0 +1,345 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Meson G12A USB3+PCIE Combo PHY driver
|
||||
*
|
||||
* Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstron@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <reset.h>
|
||||
#include <bitfield.h>
|
||||
#include <generic-phy.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#define PHY_R0 0x00
|
||||
#define PHY_R0_PCIE_POWER_STATE GENMASK(4, 0)
|
||||
#define PHY_R0_PCIE_USB3_SWITCH GENMASK(6, 5)
|
||||
|
||||
#define PHY_R1 0x04
|
||||
#define PHY_R1_PHY_TX1_TERM_OFFSET GENMASK(4, 0)
|
||||
#define PHY_R1_PHY_TX0_TERM_OFFSET GENMASK(9, 5)
|
||||
#define PHY_R1_PHY_RX1_EQ GENMASK(12, 10)
|
||||
#define PHY_R1_PHY_RX0_EQ GENMASK(15, 13)
|
||||
#define PHY_R1_PHY_LOS_LEVEL GENMASK(20, 16)
|
||||
#define PHY_R1_PHY_LOS_BIAS GENMASK(23, 21)
|
||||
#define PHY_R1_PHY_REF_CLKDIV2 BIT(24)
|
||||
#define PHY_R1_PHY_MPLL_MULTIPLIER GENMASK(31, 25)
|
||||
|
||||
#define PHY_R2 0x08
|
||||
#define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB GENMASK(5, 0)
|
||||
#define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB GENMASK(11, 6)
|
||||
#define PHY_R2_PCS_TX_DEEMPH_GEN1 GENMASK(17, 12)
|
||||
#define PHY_R2_PHY_TX_VBOOST_LVL GENMASK(20, 18)
|
||||
|
||||
#define PHY_R4 0x10
|
||||
#define PHY_R4_PHY_CR_WRITE BIT(0)
|
||||
#define PHY_R4_PHY_CR_READ BIT(1)
|
||||
#define PHY_R4_PHY_CR_DATA_IN GENMASK(17, 2)
|
||||
#define PHY_R4_PHY_CR_CAP_DATA BIT(18)
|
||||
#define PHY_R4_PHY_CR_CAP_ADDR BIT(19)
|
||||
|
||||
#define PHY_R5 0x14
|
||||
#define PHY_R5_PHY_CR_DATA_OUT GENMASK(15, 0)
|
||||
#define PHY_R5_PHY_CR_ACK BIT(16)
|
||||
#define PHY_R5_PHY_BS_OUT BIT(17)
|
||||
|
||||
struct phy_g12a_usb3_pcie_priv {
|
||||
struct regmap *regmap;
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
struct clk clk;
|
||||
#endif
|
||||
struct reset_ctl_bulk resets;
|
||||
};
|
||||
|
||||
static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv,
|
||||
unsigned int addr)
|
||||
{
|
||||
unsigned int val, reg;
|
||||
int ret;
|
||||
|
||||
reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
!(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
phy_g12a_usb3_pcie_cr_bus_read(struct phy_g12a_usb3_pcie_priv *priv,
|
||||
unsigned int addr, unsigned int *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, 0);
|
||||
regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, 0);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
!(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
phy_g12a_usb3_pcie_cr_bus_write(struct phy_g12a_usb3_pcie_priv *priv,
|
||||
unsigned int addr, unsigned int data)
|
||||
{
|
||||
unsigned int val, reg;
|
||||
int ret;
|
||||
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK) == 0,
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK) == 0,
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
phy_g12a_usb3_pcie_cr_bus_update_bits(struct phy_g12a_usb3_pcie_priv *priv,
|
||||
uint offset, uint mask, uint val)
|
||||
{
|
||||
uint reg;
|
||||
int ret;
|
||||
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_read(priv, offset, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg &= ~mask;
|
||||
|
||||
return phy_g12a_usb3_pcie_cr_bus_write(priv, offset, reg | val);
|
||||
}
|
||||
|
||||
static int phy_meson_g12a_usb3_init(struct phy *phy)
|
||||
{
|
||||
struct udevice *dev = phy->dev;
|
||||
struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
|
||||
unsigned int data;
|
||||
int ret;
|
||||
|
||||
/* TOFIX Handle PCIE mode */
|
||||
|
||||
ret = reset_assert_bulk(&priv->resets);
|
||||
udelay(1);
|
||||
ret |= reset_deassert_bulk(&priv->resets);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Switch PHY to USB3 */
|
||||
regmap_update_bits(priv->regmap, PHY_R0,
|
||||
PHY_R0_PCIE_USB3_SWITCH,
|
||||
PHY_R0_PCIE_USB3_SWITCH);
|
||||
|
||||
/*
|
||||
* WORKAROUND: There is SSPHY suspend bug due to
|
||||
* which USB enumerates
|
||||
* in HS mode instead of SS mode. Workaround it by asserting
|
||||
* LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus
|
||||
* mode
|
||||
*/
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x102d,
|
||||
BIT(7), BIT(7));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x1010, 0xff0, 20);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Fix RX Equalization setting as follows
|
||||
* LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
|
||||
* LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
|
||||
* LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
|
||||
* LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
|
||||
*/
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1006, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data &= ~BIT(6);
|
||||
data |= BIT(7);
|
||||
data &= ~(0x7 << 8);
|
||||
data |= (0x3 << 8);
|
||||
data |= (1 << 11);
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1006, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set EQ and TX launch amplitudes as follows
|
||||
* LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
|
||||
* LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
|
||||
* LANE0.TX_OVRD_DRV_LO.EN set to 1.
|
||||
*/
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1002, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data &= ~0x3f80;
|
||||
data |= (0x16 << 7);
|
||||
data &= ~0x7f;
|
||||
data |= (0x7f | BIT(14));
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1002, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* MPLL_LOOP_CTL.PROP_CNTRL = 8
|
||||
*/
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x30,
|
||||
0xf << 4, 8 << 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(priv->regmap, PHY_R2,
|
||||
PHY_R2_PHY_TX_VBOOST_LVL,
|
||||
FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4));
|
||||
|
||||
regmap_update_bits(priv->regmap, PHY_R1,
|
||||
PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL,
|
||||
FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) |
|
||||
FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int phy_meson_g12a_usb3_exit(struct phy *phy)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(phy->dev);
|
||||
|
||||
return reset_assert_bulk(&priv->resets);
|
||||
}
|
||||
|
||||
struct phy_ops meson_g12a_usb3_pcie_phy_ops = {
|
||||
.init = phy_meson_g12a_usb3_init,
|
||||
.exit = phy_meson_g12a_usb3_exit,
|
||||
};
|
||||
|
||||
int meson_g12a_usb3_pcie_phy_probe(struct udevice *dev)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_get_bulk(dev, &priv->resets);
|
||||
if (ret == -ENOTSUPP)
|
||||
return 0;
|
||||
else 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 != -ENOENT && 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_g12a_usb3_pcie_phy_ids[] = {
|
||||
{ .compatible = "amlogic,g12a-usb3-pcie-phy" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(meson_g12a_usb3_pcie_phy) = {
|
||||
.name = "meson_g12a_usb3_pcie_phy",
|
||||
.id = UCLASS_PHY,
|
||||
.of_match = meson_g12a_usb3_pcie_phy_ids,
|
||||
.probe = meson_g12a_usb3_pcie_phy_probe,
|
||||
.ops = &meson_g12a_usb3_pcie_phy_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct phy_g12a_usb3_pcie_priv),
|
||||
};
|
@ -44,6 +44,14 @@ config USB_DWC3_GENERIC
|
||||
Select this for Xilinx ZynqMP and similar Platforms.
|
||||
This wrapper supports Host and Peripheral operation modes.
|
||||
|
||||
config USB_DWC3_MESON_G12A
|
||||
bool "Amlogic Meson G12A USB wrapper"
|
||||
depends on DM_USB && USB_DWC3 && ARCH_MESON
|
||||
imply PHY
|
||||
help
|
||||
Select this for Amlogic Meson G12A 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
|
||||
|
@ -7,6 +7,7 @@ dwc3-y := core.o
|
||||
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_GENERIC) += dwc3-generic.o
|
||||
obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o
|
||||
obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o
|
||||
|
456
drivers/usb/dwc3/dwc3-meson-g12a.c
Normal file
456
drivers/usb/dwc3/dwc3-meson-g12a.c
Normal file
@ -0,0 +1,456 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Amlogic G12A DWC3 Glue layer
|
||||
*
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
/* USB2 Ports Control Registers */
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
|
||||
#define U2P_R0 0x0
|
||||
#define U2P_R0_HOST_DEVICE BIT(0)
|
||||
#define U2P_R0_POWER_OK BIT(1)
|
||||
#define U2P_R0_HAST_MODE BIT(2)
|
||||
#define U2P_R0_POWER_ON_RESET BIT(3)
|
||||
#define U2P_R0_ID_PULLUP BIT(4)
|
||||
#define U2P_R0_DRV_VBUS BIT(5)
|
||||
|
||||
#define U2P_R1 0x4
|
||||
#define U2P_R1_PHY_READY BIT(0)
|
||||
#define U2P_R1_ID_DIG BIT(1)
|
||||
#define U2P_R1_OTG_SESSION_VALID BIT(2)
|
||||
#define U2P_R1_VBUS_VALID BIT(3)
|
||||
|
||||
/* USB Glue Control Registers */
|
||||
|
||||
#define USB_R0 0x80
|
||||
#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 0x84
|
||||
#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(4, 2)
|
||||
#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7)
|
||||
#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 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 0x88
|
||||
#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 0x8c
|
||||
#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_R4 0x90
|
||||
#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 0x94
|
||||
#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)
|
||||
|
||||
enum {
|
||||
USB2_HOST_PHY = 0,
|
||||
USB2_OTG_PHY,
|
||||
USB3_HOST_PHY,
|
||||
PHY_COUNT,
|
||||
};
|
||||
|
||||
static const char *phy_names[PHY_COUNT] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a {
|
||||
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;
|
||||
unsigned int usb3_ports;
|
||||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
struct udevice *vbus_supply;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
#define USB_REG_OFFSET 0x80
|
||||
|
||||
static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, 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, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_HOST_DEVICE,
|
||||
U2P_R0_HOST_DEVICE);
|
||||
break;
|
||||
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_HOST_DEVICE, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
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 < USB3_HOST_PHY ; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET,
|
||||
U2P_R0_POWER_ON_RESET);
|
||||
|
||||
if (i == USB2_OTG_PHY) {
|
||||
regmap_update_bits(priv->regmap,
|
||||
U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
priv->otg_phy_mode);
|
||||
} else
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
USB_DR_MODE_HOST);
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
regmap_update_bits(priv->regmap, USB_R3,
|
||||
USB_R3_P30_SSC_RANGE_MASK |
|
||||
USB_R3_P30_REF_SSP_EN,
|
||||
USB_R3_P30_SSC_ENABLE |
|
||||
FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
|
||||
USB_R3_P30_REF_SSP_EN);
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
|
||||
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
|
||||
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
if (priv->otg_phy_mode == 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);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc3_meson_g12a_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));
|
||||
|
||||
/* If we have an actual SuperSpeed port, initialize it */
|
||||
if (priv->usb3_ports)
|
||||
dwc3_meson_g12a_usb3_init(priv);
|
||||
|
||||
dwc3_meson_g12a_usb_init_mode(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode)
|
||||
{
|
||||
struct dwc3_meson_g12a *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_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;
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
|
||||
|
||||
dwc3_meson_g12a_usb_init_mode(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *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)
|
||||
continue;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (i == USB3_HOST_PHY)
|
||||
priv->usb3_ports++;
|
||||
else
|
||||
priv->usb2_ports++;
|
||||
}
|
||||
|
||||
debug("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
|
||||
debug("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_reset_init(struct dwc3_meson_g12a *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_g12a_clk_init(struct dwc3_meson_g12a *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_g12a_probe(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *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_g12a_clk_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_reset_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_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
|
||||
|
||||
priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
|
||||
|
||||
ret = dwc3_meson_g12a_usb_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_g12a_remove(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *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_exit(&priv->phys[i]);
|
||||
}
|
||||
|
||||
return dm_scan_fdt_dev(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id dwc3_meson_g12a_ids[] = {
|
||||
{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(dwc3_generic_wrapper) = {
|
||||
.name = "dwc3-meson-g12a",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = dwc3_meson_g12a_ids,
|
||||
.probe = dwc3_meson_g12a_probe,
|
||||
.remove = dwc3_meson_g12a_remove,
|
||||
.platdata_auto_alloc_size = sizeof(struct dwc3_meson_g12a),
|
||||
|
||||
};
|
Loading…
Reference in New Issue
Block a user