diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk-uclass.c | 75 | ||||
-rw-r--r-- | drivers/clk/sifive/fu540-prci.c | 2 | ||||
-rw-r--r-- | drivers/core/ofnode.c | 2 | ||||
-rw-r--r-- | drivers/misc/cros_ec.c | 2 | ||||
-rw-r--r-- | drivers/net/ldpaa_eth/ldpaa_eth.c | 1 | ||||
-rw-r--r-- | drivers/net/phy/Kconfig | 20 | ||||
-rw-r--r-- | drivers/net/phy/aquantia.c | 7 | ||||
-rw-r--r-- | drivers/net/phy/micrel_ksz90x1.c | 24 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 21 | ||||
-rw-r--r-- | drivers/net/phy/realtek.c | 19 | ||||
-rw-r--r-- | drivers/net/phy/ti.c | 130 | ||||
-rw-r--r-- | drivers/net/sun8i_emac.c | 74 | ||||
-rw-r--r-- | drivers/pci/pci_rom.c | 2 | ||||
-rw-r--r-- | drivers/phy/Kconfig | 8 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/meson-g12a-usb2.c | 216 | ||||
-rw-r--r-- | drivers/phy/meson-g12a-usb3-pcie.c | 345 | ||||
-rw-r--r-- | drivers/reset/reset-uclass.c | 53 | ||||
-rw-r--r-- | drivers/serial/serial_sifive.c | 2 | ||||
-rw-r--r-- | drivers/spi/atcspi200_spi.c | 2 | ||||
-rw-r--r-- | drivers/sysreset/sysreset_x86.c | 101 | ||||
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 8 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-meson-g12a.c | 456 |
24 files changed, 1396 insertions, 176 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 844b87cc33..79b3b0494c 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -54,28 +54,20 @@ static int clk_of_xlate_default(struct clk *clk, return 0; } -static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, - int index, struct clk *clk) +static int clk_get_by_index_tail(int ret, ofnode node, + struct ofnode_phandle_args *args, + const char *list_name, int index, + struct clk *clk) { - int ret; - struct ofnode_phandle_args args; struct udevice *dev_clk; const struct clk_ops *ops; - debug("%s(dev=%p, index=%d, clk=%p)\n", __func__, dev, index, clk); - assert(clk); clk->dev = NULL; + if (ret) + goto err; - ret = dev_read_phandle_with_args(dev, prop_name, "#clock-cells", 0, - index, &args); - if (ret) { - debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", - __func__, ret); - return ret; - } - - ret = uclass_get_device_by_ofnode(UCLASS_CLK, args.node, &dev_clk); + ret = uclass_get_device_by_ofnode(UCLASS_CLK, args->node, &dev_clk); if (ret) { debug("%s: uclass_get_device_by_of_offset failed: err=%d\n", __func__, ret); @@ -87,20 +79,67 @@ static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, ops = clk_dev_ops(dev_clk); if (ops->of_xlate) - ret = ops->of_xlate(clk, &args); + ret = ops->of_xlate(clk, args); else - ret = clk_of_xlate_default(clk, &args); + ret = clk_of_xlate_default(clk, args); if (ret) { debug("of_xlate() failed: %d\n", ret); return ret; } return clk_request(dev_clk, clk); +err: + debug("%s: Node '%s', property '%s', failed to request CLK index %d: %d\n", + __func__, ofnode_get_name(node), list_name, index, ret); + return ret; +} + +static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, + int index, struct clk *clk) +{ + int ret; + struct ofnode_phandle_args args; + + debug("%s(dev=%p, index=%d, clk=%p)\n", __func__, dev, index, clk); + + assert(clk); + clk->dev = NULL; + + ret = dev_read_phandle_with_args(dev, prop_name, "#clock-cells", 0, + index, &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", + __func__, ret); + return ret; + } + + + return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks", + index > 0, clk); } int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) { - return clk_get_by_indexed_prop(dev, "clocks", index, clk); + struct ofnode_phandle_args args; + int ret; + + ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0, + index, &args); + + return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks", + index > 0, clk); +} + +int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk) +{ + struct ofnode_phandle_args args; + int ret; + + ret = ofnode_parse_phandle_with_args(node, "clocks", "#clock-cells", 0, + index > 0, &args); + + return clk_get_by_index_tail(ret, node, &args, "clocks", + index > 0, clk); } int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk) diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c index e1b5f8e6a9..2d47ebc6b1 100644 --- a/drivers/clk/sifive/fu540-prci.c +++ b/drivers/clk/sifive/fu540-prci.c @@ -28,10 +28,10 @@ * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset" */ +#include <common.h> #include <asm/io.h> #include <clk-uclass.h> #include <clk.h> -#include <common.h> #include <div64.h> #include <dm.h> #include <errno.h> diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 785f5c3acf..cc0c031e0d 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -546,7 +546,7 @@ fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property, ns = of_n_size_cells(np); *sizep = of_read_number(prop + na, ns); - if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) + if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0) return of_translate_address(np, prop); else return of_read_number(prop, na); diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 565de040fe..382f826286 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -1482,7 +1482,7 @@ int cros_ec_set_lid_shutdown_mask(struct udevice *dev, int enable) UCLASS_DRIVER(cros_ec) = { .id = UCLASS_CROS_EC, - .name = "cros_ec", + .name = "cros-ec", .per_device_auto_alloc_size = sizeof(struct cros_ec_dev), .post_bind = dm_scan_fdt_dev, .flags = DM_UC_FLAG_ALLOC_PRIV_DMA, diff --git a/drivers/net/ldpaa_eth/ldpaa_eth.c b/drivers/net/ldpaa_eth/ldpaa_eth.c index 73b7ba29df..34253e3924 100644 --- a/drivers/net/ldpaa_eth/ldpaa_eth.c +++ b/drivers/net/ldpaa_eth/ldpaa_eth.c @@ -1074,6 +1074,7 @@ int ldpaa_eth_init(int dpmac_id, phy_interface_t enet_if) priv = (struct ldpaa_eth_priv *)malloc(sizeof(struct ldpaa_eth_priv)); if (!priv) { printf("ldpaa_eth_priv malloc() failed\n"); + free(net_dev); return -ENOMEM; } memset(priv, 0, sizeof(struct ldpaa_eth_priv)); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3dc0822d9c..631b52b1cf 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -202,6 +202,26 @@ config RTL8211X_PHY_FORCE_MASTER If unsure, say N. +config RTL8211F_PHY_FORCE_EEE_RXC_ON + bool "Ethernet PHY RTL8211F: do not stop receiving the xMII clock during LPI" + depends on PHY_REALTEK + default n + help + The IEEE 802.3az-2010 (EEE) standard provides a protocol to coordinate + transitions to/from a lower power consumption level (Low Power Idle + mode) based on link utilization. When no packets are being + transmitted, the system goes to Low Power Idle mode to save power. + + Under particular circumstances this setting can cause issues where + the PHY is unable to transmit or receive any packet when in LPI mode. + The problem is caused when the PHY is configured to stop receiving + the xMII clock while it is signaling LPI. For some PHYs the bit + configuring this behavior is set by the Linux kernel, causing the + issue in U-Boot on reboot if the PHY retains the register value. + + Default n, which means that the PHY state is not changed. To work + around the issues, change this setting to y. + config PHY_SMSC bool "Microchip(SMSC) Ethernet PHYs support" diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index 12df09877d..5c3298d612 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -303,9 +303,14 @@ int aquantia_config(struct phy_device *phydev) AQUANTIA_SYSTEM_INTERFACE_SR); /* If SI is USXGMII then start USXGMII autoneg */ if ((val & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII) { + reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS, + AQUANTIA_VENDOR_PROVISIONING_REG); + + reg_val1 |= AQUANTIA_USX_AUTONEG_CONTROL_ENA; + phy_write(phydev, MDIO_MMD_PHYXS, AQUANTIA_VENDOR_PROVISIONING_REG, - AQUANTIA_USX_AUTONEG_CONTROL_ENA); + reg_val1); printf("%s: system interface USXGMII\n", phydev->dev->name); } else { diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index 63e7b0242b..8dec9f2367 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -33,10 +33,14 @@ #define CTRL1000_CONFIG_MASTER (1 << 11) #define CTRL1000_MANUAL_CONFIG (1 << 12) +#define KSZ9021_PS_TO_REG 120 + /* KSZ9031 PHY Registers */ #define MII_KSZ9031_MMD_ACCES_CTRL 0x0d #define MII_KSZ9031_MMD_REG_DATA 0x0e +#define KSZ9031_PS_TO_REG 60 + static int ksz90xx_startup(struct phy_device *phydev) { unsigned phy_ctl; @@ -102,20 +106,28 @@ static const struct ksz90x1_reg_field ksz9031_clk_grp[] = { }; static int ksz90x1_of_config_group(struct phy_device *phydev, - struct ksz90x1_ofcfg *ofcfg) + struct ksz90x1_ofcfg *ofcfg, + int ps_to_regval) { struct udevice *dev = phydev->dev; struct phy_driver *drv = phydev->drv; - const int ps_to_regval = 60; int val[4]; int i, changed = 0, offset, max; u16 regval = 0; + ofnode node; if (!drv || !drv->writeext) return -EOPNOTSUPP; + /* Look for a PHY node under the Ethernet node */ + node = dev_read_subnode(dev, "ethernet-phy"); + if (!ofnode_valid(node)) { + /* No node found, look in the Ethernet node */ + node = dev_ofnode(dev); + } + for (i = 0; i < ofcfg->grpsz; i++) { - val[i] = dev_read_u32_default(dev, ofcfg->grp[i].name, ~0); + val[i] = ofnode_read_u32_default(node, ofcfg->grp[i].name, ~0); offset = ofcfg->grp[i].off; if (val[i] == -1) { /* Default register value for KSZ9021 */ @@ -148,7 +160,8 @@ static int ksz9021_of_config(struct phy_device *phydev) int i, ret = 0; for (i = 0; i < ARRAY_SIZE(ofcfg); i++) { - ret = ksz90x1_of_config_group(phydev, &(ofcfg[i])); + ret = ksz90x1_of_config_group(phydev, &ofcfg[i], + KSZ9021_PS_TO_REG); if (ret) return ret; } @@ -167,7 +180,8 @@ static int ksz9031_of_config(struct phy_device *phydev) int i, ret = 0; for (i = 0; i < ARRAY_SIZE(ofcfg); i++) { - ret = ksz90x1_of_config_group(phydev, &(ofcfg[i])); + ret = ksz90x1_of_config_group(phydev, &ofcfg[i], + KSZ9031_PS_TO_REG); if (ret) return ret; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4e8d2943ee..c1c1af9abd 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -462,6 +462,18 @@ static LIST_HEAD(phy_drivers); int phy_init(void) { +#ifdef CONFIG_NEEDS_MANUAL_RELOC + /* + * The pointers inside phy_drivers also needs to be updated incase of + * manual reloc, without which these points to some invalid + * pre reloc address and leads to invalid accesses, hangs. + */ + struct list_head *head = &phy_drivers; + + head->next = (void *)head->next + gd->reloc_off; + head->prev = (void *)head->prev + gd->reloc_off; +#endif + #ifdef CONFIG_B53_SWITCH phy_b53_init(); #endif @@ -549,6 +561,10 @@ int phy_register(struct phy_driver *drv) drv->readext += gd->reloc_off; if (drv->writeext) drv->writeext += gd->reloc_off; + if (drv->read_mmd) + drv->read_mmd += gd->reloc_off; + if (drv->write_mmd) + drv->write_mmd += gd->reloc_off; #endif return 0; } @@ -655,7 +671,10 @@ static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, dev->drv = get_phy_driver(dev, interface); - phy_probe(dev); + if (phy_probe(dev)) { + printf("%s, PHY probe failed\n", __func__); + return NULL; + } if (addr >= 0 && addr < PHY_MAX_ADDR) bus->phymap[addr] = dev; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index dd45e11b3a..8f1d759632 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -12,6 +12,7 @@ #define PHY_RTL8211x_FORCE_MASTER BIT(1) #define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2) +#define PHY_RTL8211F_FORCE_EEE_RXC_ON BIT(3) #define PHY_AUTONEGOTIATE_TIMEOUT 5000 @@ -102,6 +103,15 @@ static int rtl8211e_probe(struct phy_device *phydev) return 0; } +static int rtl8211f_probe(struct phy_device *phydev) +{ +#ifdef CONFIG_RTL8211F_PHY_FORCE_EEE_RXC_ON + phydev->flags |= PHY_RTL8211F_FORCE_EEE_RXC_ON; +#endif + + return 0; +} + /* RealTek RTL8211x */ static int rtl8211x_config(struct phy_device *phydev) { @@ -151,6 +161,14 @@ static int rtl8211f_config(struct phy_device *phydev) { u16 reg; + if (phydev->flags & PHY_RTL8211F_FORCE_EEE_RXC_ON) { + unsigned int reg; + + reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_PCS_CTRL1_CLKSTOP_EN; + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, reg); + } + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); phy_write(phydev, MDIO_DEVAD_NONE, @@ -360,6 +378,7 @@ static struct phy_driver RTL8211F_driver = { .uid = 0x1cc916, .mask = 0xffffff, .features = PHY_GBIT_FEATURES, + .probe = &rtl8211f_probe, .config = &rtl8211f_config, .startup = &rtl8211f_startup, .shutdown = &genphy_shutdown, diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index 6db6edd0d0..6ac890a7f5 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -73,16 +73,6 @@ #define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000 #define MII_DP83867_CFG2_MASK 0x003F -#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ -#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ - -/* MMD Access Control register fields */ -#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ -#define MII_MMD_CTRL_ADDR 0x0000 /* Address */ -#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ -#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ -#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ - /* User setting - can be taken from DTS */ #define DEFAULT_RX_ID_DELAY DP83867_RGMIIDCTL_2_25_NS #define DEFAULT_TX_ID_DELAY DP83867_RGMIIDCTL_2_75_NS @@ -116,88 +106,20 @@ struct dp83867_private { int clk_output_sel; }; -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * @addr: PHY address on the MII bus - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these registers we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr) -{ - int value = -1; - - /* Write the desired MMD Devad */ - phy_write(phydev, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - phy_write(phydev, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); - - /* Read the content of the MMD's selected register */ - value = phy_read(phydev, addr, MII_MMD_DATA); - return value; -} - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @addr: PHY address on the MII bus - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these registers we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr, u32 data) -{ - /* Write the desired MMD Devad */ - phy_write(phydev, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - phy_write(phydev, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); - - /* Write the data into MMD's selected register */ - phy_write(phydev, addr, MII_MMD_DATA, data); -} - static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = (struct dp83867_private *)phydev->priv; u16 val; - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, - phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); if (dp83867->port_mirroring == DP83867_PORT_MIRRORING_EN) val |= DP83867_CFG4_PORT_MIRROR_EN; else val &= ~DP83867_CFG4_PORT_MIRROR_EN; - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, - phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); return 0; } @@ -257,13 +179,13 @@ static int dp83867_of_init(struct phy_device *phydev) /* Clock output selection if muxing property is set */ if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) { - val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK; val |= (dp83867->clk_output_sel << DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT); - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG, val); } return 0; @@ -308,11 +230,11 @@ static int dp83867_config(struct phy_device *phydev) /* Mode 1 or 2 workaround */ if (dp83867->rxctrl_strap_quirk) { - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, - DP83867_DEVADDR, phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_CFG4); val &= ~BIT(7); - phy_write_mmd_indirect(phydev, DP83867_CFG4, - DP83867_DEVADDR, phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_CFG4, val); } if (phy_interface_is_rgmii(phydev)) { @@ -332,8 +254,8 @@ static int dp83867_config(struct phy_device *phydev) * register's bit 11 (marked as RESERVED). */ - bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1, - DP83867_DEVADDR, phydev->addr); + bs = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_STRAP_STS1); val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL); if (bs & DP83867_STRAP_STS1_RESERVED) { val &= ~DP83867_PHYCR_RESERVED_MASK; @@ -354,8 +276,8 @@ static int dp83867_config(struct phy_device *phydev) MII_DP83867_CFG2_SPEEDOPT_INTLOW); phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG2, cfg2); - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr, 0x0); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIICTL, 0x0); phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL, DP83867_PHYCTRL_SGMIIEN | @@ -367,8 +289,8 @@ static int dp83867_config(struct phy_device *phydev) } if (phy_interface_is_rgmii(phydev)) { - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIICTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | @@ -380,26 +302,24 @@ static int dp83867_config(struct phy_device *phydev) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIICTL, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); - phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, phydev->addr, delay); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIIDCTL, delay); if (dp83867->io_impedance >= 0) { - val = phy_read_mmd_indirect(phydev, - DP83867_IO_MUX_CFG, - DP83867_DEVADDR, - phydev->addr); + val = phy_read_mmd(phydev, + DP83867_DEVADDR, + DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; val |= dp83867->io_impedance & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, phydev->addr, - val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG, val); } } diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 98bd7a5823..c0a440886e 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -138,7 +138,9 @@ struct emac_eth_dev { struct phy_device *phydev; struct mii_dev *bus; struct clk tx_clk; + struct clk ephy_clk; struct reset_ctl tx_rst; + struct reset_ctl ephy_rst; #ifdef CONFIG_DM_GPIO struct gpio_desc reset_gpio; #endif @@ -653,7 +655,6 @@ static int sun8i_eth_write_hwaddr(struct udevice *dev) static int sun8i_emac_board_setup(struct emac_eth_dev *priv) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; int ret; ret = clk_enable(&priv->tx_clk); @@ -670,16 +671,20 @@ static int sun8i_emac_board_setup(struct emac_eth_dev *priv) } } - if (priv->variant == H3_EMAC) { - /* Only H3/H5 have clock controls for internal EPHY */ - if (priv->use_internal_phy) { - /* Set clock gating for ephy */ - setbits_le32(&ccm->bus_gate4, - BIT(AHB_GATE_OFFSET_EPHY)); - - /* Deassert EPHY */ - setbits_le32(&ccm->ahb_reset2_cfg, - BIT(AHB_RESET_OFFSET_EPHY)); + /* Only H3/H5 have clock controls for internal EPHY */ + if (clk_valid(&priv->ephy_clk)) { + ret = clk_enable(&priv->ephy_clk); + if (ret) { + dev_err(dev, "failed to enable EPHY TX clock\n"); + return ret; + } + } + + if (reset_valid(&priv->ephy_rst)) { + ret = reset_deassert(&priv->ephy_rst); + if (ret) { + dev_err(dev, "failed to deassert EPHY TX clock\n"); + return ret; } } @@ -839,6 +844,44 @@ static const struct eth_ops sun8i_emac_eth_ops = { .stop = sun8i_emac_eth_stop, }; +static int sun8i_get_ephy_nodes(struct emac_eth_dev *priv) +{ + int node, ret; + + /* look for mdio-mux node for internal PHY node */ + node = fdt_path_offset(gd->fdt_blob, + "/soc/ethernet@1c30000/mdio-mux/mdio@1/ethernet-phy@1"); + if (node < 0) { + debug("failed to get mdio-mux with internal PHY\n"); + return node; + } + + ret = fdt_node_check_compatible(gd->fdt_blob, node, + "allwinner,sun8i-h3-mdio-internal"); + if (ret < 0) { + debug("failed to find mdio-internal node\n"); + return ret; + } + + ret = clk_get_by_index_nodev(offset_to_ofnode(node), 0, + &priv->ephy_clk); + if (ret) { + dev_err(dev, "failed to get EPHY TX clock\n"); + return ret; + } + + ret = reset_get_by_index_nodev(offset_to_ofnode(node), 0, + &priv->ephy_rst); + if (ret) { + dev_err(dev, "failed to get EPHY TX reset\n"); + return ret; + } + + priv->use_internal_phy = true; + + return 0; +} + static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) { struct sun8i_eth_pdata *sun8i_pdata = dev_get_platdata(dev); @@ -920,12 +963,9 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) } if (priv->variant == H3_EMAC) { - int parent = fdt_parent_offset(gd->fdt_blob, offset); - - if (parent >= 0 && - !fdt_node_check_compatible(gd->fdt_blob, parent, - "allwinner,sun8i-h3-mdio-internal")) - priv->use_internal_phy = true; + ret = sun8i_get_ephy_nodes(priv); + if (ret) + return ret; } priv->interface = pdata->phy_interface; diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c index 7d9b75c2c4..2cede1211b 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -306,7 +306,7 @@ int dm_pci_run_vga_bios(struct udevice *dev, int (*int15_handler)(void), goto err; #endif } else { -#if defined(CONFIG_X86) && CONFIG_IS_ENABLED(X86_32BIT_INIT) +#if defined(CONFIG_X86) && (CONFIG_IS_ENABLED(X86_32BIT_INIT) || CONFIG_TPL) bios_set_interrupt_handler(0x15, int15_handler); bios_run_on_x86(dev, (unsigned long)ram, vesa_mode, diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 102fb91fff..957efb3984 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -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 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b55917bce1..90646ca55b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -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 diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c new file mode 100644 index 0000000000..ad1a77fcfc --- /dev/null +++ b/drivers/phy/meson-g12a-usb2.c @@ -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), +}; diff --git a/drivers/phy/meson-g12a-usb3-pcie.c b/drivers/phy/meson-g12a-usb3-pcie.c new file mode 100644 index 0000000000..920675dc99 --- /dev/null +++ b/drivers/phy/meson-g12a-usb3-pcie.c @@ -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), +}; diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index 89e39c6b5a..ee1a423ffb 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -29,41 +29,34 @@ static int reset_of_xlate_default(struct reset_ctl *reset_ctl, return 0; } -int reset_get_by_index(struct udevice *dev, int index, - struct reset_ctl *reset_ctl) +static int reset_get_by_index_tail(int ret, ofnode node, + struct ofnode_phandle_args *args, + const char *list_name, int index, + struct reset_ctl *reset_ctl) { - struct ofnode_phandle_args args; - int ret; struct udevice *dev_reset; struct reset_ops *ops; - debug("%s(dev=%p, index=%d, reset_ctl=%p)\n", __func__, dev, index, - reset_ctl); + assert(reset_ctl); reset_ctl->dev = NULL; - - ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, - index, &args); - if (ret) { - debug("%s: fdtdec_parse_phandle_with_args() failed: %d\n", - __func__, ret); + if (ret) return ret; - } - ret = uclass_get_device_by_ofnode(UCLASS_RESET, args.node, + ret = uclass_get_device_by_ofnode(UCLASS_RESET, args->node, &dev_reset); if (ret) { debug("%s: uclass_get_device_by_ofnode() failed: %d\n", __func__, ret); - debug("%s %d\n", ofnode_get_name(args.node), args.args[0]); + debug("%s %d\n", ofnode_get_name(args->node), args->args[0]); return ret; } ops = reset_dev_ops(dev_reset); reset_ctl->dev = dev_reset; if (ops->of_xlate) - ret = ops->of_xlate(reset_ctl, &args); + ret = ops->of_xlate(reset_ctl, args); else - ret = reset_of_xlate_default(reset_ctl, &args); + ret = reset_of_xlate_default(reset_ctl, args); if (ret) { debug("of_xlate() failed: %d\n", ret); return ret; @@ -78,6 +71,32 @@ int reset_get_by_index(struct udevice *dev, int index, return 0; } +int reset_get_by_index(struct udevice *dev, int index, + struct reset_ctl *reset_ctl) +{ + struct ofnode_phandle_args args; + int ret; + + ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, + index, &args); + + return reset_get_by_index_tail(ret, dev_ofnode(dev), &args, "resets", + index > 0, reset_ctl); +} + +int reset_get_by_index_nodev(ofnode node, int index, + struct reset_ctl *reset_ctl) +{ + struct ofnode_phandle_args args; + int ret; + + ret = ofnode_parse_phandle_with_args(node, "resets", "#reset-cells", 0, + index > 0, &args); + + return reset_get_by_index_tail(ret, node, &args, "resets", + index > 0, reset_ctl); +} + int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk) { int i, ret, err, count; diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c index 537bc7a975..fdfef69aaa 100644 --- a/drivers/serial/serial_sifive.c +++ b/drivers/serial/serial_sifive.c @@ -3,8 +3,8 @@ * Copyright (C) 2018 Anup Patel <anup@brainfault.org> */ -#include <clk.h> #include <common.h> +#include <clk.h> #include <debug_uart.h> #include <dm.h> #include <errno.h> diff --git a/drivers/spi/atcspi200_spi.c b/drivers/spi/atcspi200_spi.c index af96c6d21e..e0cc323444 100644 --- a/drivers/spi/atcspi200_spi.c +++ b/drivers/spi/atcspi200_spi.c @@ -6,8 +6,8 @@ * Author: Rick Chen (rick@andestech.com) */ -#include <clk.h> #include <common.h> +#include <clk.h> #include <malloc.h> #include <spi.h> #include <asm/io.h> diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index 009f376602..072f7948ef 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -7,15 +7,75 @@ #include <common.h> #include <dm.h> +#include <efi_loader.h> +#include <pch.h> #include <sysreset.h> +#include <asm/acpi_s3.h> #include <asm/io.h> #include <asm/processor.h> -#include <efi_loader.h> -static __efi_runtime int x86_sysreset_request(struct udevice *dev, - enum sysreset_t type) +struct x86_sysreset_platdata { + struct udevice *pch; +}; + +/* + * Power down the machine by using the power management sleep control + * of the chipset. This will currently only work on Intel chipsets. + * However, adapting it to new chipsets is fairly simple. You will + * have to find the IO address of the power management register block + * in your southbridge, and look up the appropriate SLP_TYP_S5 value + * from your southbridge's data sheet. + * + * This function never returns. + */ +int pch_sysreset_power_off(struct udevice *dev) +{ + struct x86_sysreset_platdata *plat = dev_get_platdata(dev); + struct pch_pmbase_info pm; + u32 reg32; + int ret; + + if (!plat->pch) + return -ENOENT; + ret = pch_ioctl(plat->pch, PCH_REQ_PMBASE_INFO, &pm, sizeof(pm)); + if (ret) + return ret; + + /* + * Mask interrupts or system might stay in a coma, not executing code + * anymore, but not powered off either. + */ + asm("cli"); + + /* + * Avoid any GPI waking the system from S5* or the system might stay in + * a coma + */ + outl(0x00000000, pm.base + pm.gpio0_en_ofs); + + /* Clear Power Button Status */ + outw(PWRBTN_STS, pm.base + pm.pm1_sts_ofs); + + /* PMBASE + 4, Bit 10-12, Sleeping Type, * set to 111 -> S5, soft_off */ + reg32 = inl(pm.base + pm.pm1_cnt_ofs); + + /* Set Sleeping Type to S5 (poweroff) */ + reg32 &= ~(SLP_EN | SLP_TYP); + reg32 |= SLP_TYP_S5; + outl(reg32, pm.base + pm.pm1_cnt_ofs); + + /* Now set the Sleep Enable bit */ + reg32 |= SLP_EN; + outl(reg32, pm.base + pm.pm1_cnt_ofs); + + for (;;) + asm("hlt"); +} + +static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type) { int value; + int ret; switch (type) { case SYSRESET_WARM: @@ -24,6 +84,11 @@ static __efi_runtime int x86_sysreset_request(struct udevice *dev, case SYSRESET_COLD: value = SYS_RST | RST_CPU | FULL_RST; break; + case SYSRESET_POWER_OFF: + ret = pch_sysreset_power_off(dev); + if (ret) + return ret; + return -EINPROGRESS; default: return -ENOSYS; } @@ -33,17 +98,29 @@ static __efi_runtime int x86_sysreset_request(struct udevice *dev, return -EINPROGRESS; } +static int x86_sysreset_get_last(struct udevice *dev) +{ + return SYSRESET_POWER; +} + #ifdef CONFIG_EFI_LOADER void __efi_runtime EFIAPI efi_reset_system( enum efi_reset_type reset_type, efi_status_t reset_status, unsigned long data_size, void *reset_data) { + int value; + + /* + * inline this code since we are not caused in the context of a + * udevice and passing NULL to x86_sysreset_request() is too horrible. + */ if (reset_type == EFI_RESET_COLD || reset_type == EFI_RESET_PLATFORM_SPECIFIC) - x86_sysreset_request(NULL, SYSRESET_COLD); - else if (reset_type == EFI_RESET_WARM) - x86_sysreset_request(NULL, SYSRESET_WARM); + value = SYS_RST | RST_CPU | FULL_RST; + else /* assume EFI_RESET_WARM since we cannot return an error */ + value = SYS_RST | RST_CPU; + outb(value, IO_PORT_RESET); /* TODO EFI_RESET_SHUTDOWN */ @@ -51,6 +128,15 @@ void __efi_runtime EFIAPI efi_reset_system( } #endif +static int x86_sysreset_probe(struct udevice *dev) +{ + struct x86_sysreset_platdata *plat = dev_get_platdata(dev); + + /* Locate the PCH if there is one. It isn't essential */ + uclass_first_device(UCLASS_PCH, &plat->pch); + + return 0; +} static const struct udevice_id x86_sysreset_ids[] = { { .compatible = "x86,reset" }, @@ -59,6 +145,7 @@ static const struct udevice_id x86_sysreset_ids[] = { static struct sysreset_ops x86_sysreset_ops = { .request = x86_sysreset_request, + .get_last = x86_sysreset_get_last, }; U_BOOT_DRIVER(x86_sysreset) = { @@ -66,4 +153,6 @@ U_BOOT_DRIVER(x86_sysreset) = { .id = UCLASS_SYSRESET, .of_match = x86_sysreset_ids, .ops = &x86_sysreset_ops, + .probe = x86_sysreset_probe, + .platdata_auto_alloc_size = sizeof(struct x86_sysreset_platdata), }; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index bbd8105c06..25e1a38aee 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -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 diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 60b5515a67..0b652a6f36 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -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 diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 0000000000..832bcd70ff --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -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), + +}; |