aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk-uclass.c75
-rw-r--r--drivers/clk/sifive/fu540-prci.c2
-rw-r--r--drivers/core/ofnode.c2
-rw-r--r--drivers/misc/cros_ec.c2
-rw-r--r--drivers/net/ldpaa_eth/ldpaa_eth.c1
-rw-r--r--drivers/net/phy/Kconfig20
-rw-r--r--drivers/net/phy/aquantia.c7
-rw-r--r--drivers/net/phy/micrel_ksz90x1.c24
-rw-r--r--drivers/net/phy/phy.c21
-rw-r--r--drivers/net/phy/realtek.c19
-rw-r--r--drivers/net/phy/ti.c130
-rw-r--r--drivers/net/sun8i_emac.c74
-rw-r--r--drivers/pci/pci_rom.c2
-rw-r--r--drivers/phy/Kconfig8
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/meson-g12a-usb2.c216
-rw-r--r--drivers/phy/meson-g12a-usb3-pcie.c345
-rw-r--r--drivers/reset/reset-uclass.c53
-rw-r--r--drivers/serial/serial_sifive.c2
-rw-r--r--drivers/spi/atcspi200_spi.c2
-rw-r--r--drivers/sysreset/sysreset_x86.c101
-rw-r--r--drivers/usb/dwc3/Kconfig8
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c456
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, &reg);
+ 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),
+
+};