From 2808cf6c60b5484296651e23066c4b36594f0240 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:32 +0100 Subject: [PATCH 01/29] net: sun8i-emac: Bail out on PHY error When phy_startup() returns with an error, because there is no link or the user interrupted the process, we shall stop the _start() routine and return with an error, instead of proceeding anyway. This fixes pointless operations when there is no Ethernet cable connected, for instance. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 1dae81c7bf89..ddbcf2634707 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -435,6 +435,7 @@ static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) { u32 reg, v; int timeout = 100; + int ret; reg = readl((priv->mac_reg + EMAC_CTL1)); @@ -473,7 +474,9 @@ static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) tx_descs_init(priv); /* PHY Start Up */ - phy_startup(priv->phydev); + ret = phy_startup(priv->phydev); + if (ret) + return ret; sun8i_adjust_link(priv, priv->phydev); From c35380c756a607f857b57aaaf93c53648ca10e9e Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:33 +0100 Subject: [PATCH 02/29] net: sun8i_emac: Don't hand out TX descriptor too early When initialising the TX DMA descriptors, we mostly chain them up, but of course don't know about any data or its length yet. That means they are still invalid, and the OWN bit should NOT be set yet. In fact when we later tell the MAC about the beginning of the chain, and enable TX DMA in the start() routine, the MAC will start fetching TX descriptors prematurely, as it can be seen by dumping the TX_DMA_STA and TX_DMA_CUR_DESC registers. Clear the owner bit, to not give the MAC the wrong illusion that it owns the descriptors already. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index ddbcf2634707..9f31e6de6c7b 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -415,7 +415,7 @@ static void tx_descs_init(struct emac_eth_dev *priv) desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE] ; desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; - desc_p->status = (1 << 31); + desc_p->status = 0; desc_p->st = 0; } From f20f9465dbbee7df134ea2b1a8466799234afdf1 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:34 +0100 Subject: [PATCH 03/29] net: sun8i_emac: Simplify mdio_read/mdio_write functions When preparing the register value for the MDIO command register, we start with a zeroed register, so there is no need to mask off certain bits before setting them. Simplify the sequence, and rename the variable to a more matching mii_cmd on the way. Also the open-coded time-out routine can be replaced with a much safer and easier-to-read call to wait_for_bit_le32(). Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 63 ++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 9f31e6de6c7b..f11033ce3b9b 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -29,6 +29,7 @@ #include #include #include +#include #if CONFIG_IS_ENABLED(DM_GPIO) #include #endif @@ -166,32 +167,25 @@ static int sun8i_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) { struct udevice *dev = bus->priv; struct emac_eth_dev *priv = dev_get_priv(dev); - ulong start; - u32 miiaddr = 0; - int timeout = CONFIG_MDIO_TIMEOUT; + u32 mii_cmd; + int ret; - miiaddr &= ~MDIO_CMD_MII_WRITE; - miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; - miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & MDIO_CMD_MII_PHY_REG_ADDR_MASK; - - miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK; - - miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & MDIO_CMD_MII_PHY_ADDR_MASK; - miiaddr |= MDIO_CMD_MII_BUSY; + mii_cmd |= MDIO_CMD_MII_BUSY; - writel(miiaddr, priv->mac_reg + EMAC_MII_CMD); + writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD); - start = get_timer(0); - while (get_timer(start) < timeout) { - if (!(readl(priv->mac_reg + EMAC_MII_CMD) & MDIO_CMD_MII_BUSY)) - return readl(priv->mac_reg + EMAC_MII_DATA); - udelay(10); - }; + ret = wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD, + MDIO_CMD_MII_BUSY, false, + CONFIG_MDIO_TIMEOUT, true); + if (ret < 0) + return ret; - return -1; + return readl(priv->mac_reg + EMAC_MII_DATA); } static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, @@ -199,35 +193,22 @@ static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, { struct udevice *dev = bus->priv; struct emac_eth_dev *priv = dev_get_priv(dev); - ulong start; - u32 miiaddr = 0; - int ret = -1, timeout = CONFIG_MDIO_TIMEOUT; + u32 mii_cmd; - miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; - miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & MDIO_CMD_MII_PHY_REG_ADDR_MASK; - - miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK; - miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & MDIO_CMD_MII_PHY_ADDR_MASK; - miiaddr |= MDIO_CMD_MII_WRITE; - miiaddr |= MDIO_CMD_MII_BUSY; + mii_cmd |= MDIO_CMD_MII_WRITE; + mii_cmd |= MDIO_CMD_MII_BUSY; writel(val, priv->mac_reg + EMAC_MII_DATA); - writel(miiaddr, priv->mac_reg + EMAC_MII_CMD); + writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD); - start = get_timer(0); - while (get_timer(start) < timeout) { - if (!(readl(priv->mac_reg + EMAC_MII_CMD) & - MDIO_CMD_MII_BUSY)) { - ret = 0; - break; - } - udelay(10); - }; - - return ret; + return wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD, + MDIO_CMD_MII_BUSY, false, + CONFIG_MDIO_TIMEOUT, true); } static int _sun8i_write_hwaddr(struct emac_eth_dev *priv, u8 *mac_id) From a5b2a991b388b7f42b0e264dfe2dfefc265f43ca Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 21 Oct 2020 23:21:42 +0530 Subject: [PATCH 04/29] net: sun8i_emac: Remove pointless wrapper functions Apparently due to copying from some older or converted driver, the sun8i_emac driver contains pointless wrapper functions to bridge between a legacy driver and the driver model. Since sun8i_emac is (and always was) driver model only, there is no reason to have those confusing wrappers. Just remove them, and use the driver model prototypes directly. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 62 +++++++++++----------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index f11033ce3b9b..5a19a44d067d 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -211,8 +211,11 @@ static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, CONFIG_MDIO_TIMEOUT, true); } -static int _sun8i_write_hwaddr(struct emac_eth_dev *priv, u8 *mac_id) +static int sun8i_eth_write_hwaddr(struct udevice *dev) { + struct emac_eth_dev *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + uchar *mac_id = pdata->enetaddr; u32 macid_lo, macid_hi; macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) + @@ -412,8 +415,9 @@ static void tx_descs_init(struct emac_eth_dev *priv) priv->tx_currdescnum = 0; } -static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) +static int sun8i_emac_eth_start(struct udevice *dev) { + struct emac_eth_dev *priv = dev_get_priv(dev); u32 reg, v; int timeout = 100; int ret; @@ -433,7 +437,7 @@ static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) } /* Rewrite mac address after reset */ - _sun8i_write_hwaddr(priv, enetaddr); + sun8i_eth_write_hwaddr(dev); v = readl(priv->mac_reg + EMAC_TX_CTL1); /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ @@ -542,8 +546,9 @@ static int parse_phy_pins(struct udevice *dev) return 0; } -static int _sun8i_eth_recv(struct emac_eth_dev *priv, uchar **packetp) +static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) { + struct emac_eth_dev *priv = dev_get_priv(dev); u32 status, desc_num = priv->rx_currdescnum; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; int length = -EAGAIN; @@ -589,9 +594,9 @@ static int _sun8i_eth_recv(struct emac_eth_dev *priv, uchar **packetp) return length; } -static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, - int len) +static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) { + struct emac_eth_dev *priv = dev_get_priv(dev); u32 v, desc_num = priv->tx_currdescnum; struct emac_dma_desc *desc_p = &priv->tx_chain[desc_num]; uintptr_t desc_start = (uintptr_t)desc_p; @@ -600,16 +605,16 @@ static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, uintptr_t data_start = (uintptr_t)desc_p->buf_addr; uintptr_t data_end = data_start + - roundup(len, ARCH_DMA_MINALIGN); + roundup(length, ARCH_DMA_MINALIGN); /* Invalidate entire buffer descriptor */ invalidate_dcache_range(desc_start, desc_end); - desc_p->st = len; + desc_p->st = length; /* Mandatory undocumented bit */ desc_p->st |= BIT(24); - memcpy((void *)data_start, packet, len); + memcpy((void *)data_start, packet, length); /* Flush data to be sent */ flush_dcache_range(data_start, data_end); @@ -639,14 +644,6 @@ static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, return 0; } -static int sun8i_eth_write_hwaddr(struct udevice *dev) -{ - struct eth_pdata *pdata = dev_get_platdata(dev); - struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_write_hwaddr(priv, pdata->enetaddr); -} - static int sun8i_emac_board_setup(struct udevice *dev, struct emac_eth_dev *priv) { @@ -744,29 +741,10 @@ static int sun8i_mdio_init(const char *name, struct udevice *priv) return mdio_register(bus); } -static int sun8i_emac_eth_start(struct udevice *dev) -{ - struct eth_pdata *pdata = dev_get_platdata(dev); - - return _sun8i_emac_eth_init(dev->priv, pdata->enetaddr); -} - -static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) -{ - struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_emac_eth_send(priv, packet, length); -} - -static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) +static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, + int length) { struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_eth_recv(priv, packetp); -} - -static int _sun8i_free_pkt(struct emac_eth_dev *priv) -{ u32 desc_num = priv->rx_currdescnum; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; uintptr_t desc_start = (uintptr_t)desc_p; @@ -787,14 +765,6 @@ static int _sun8i_free_pkt(struct emac_eth_dev *priv) return 0; } -static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, - int length) -{ - struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_free_pkt(priv); -} - static void sun8i_emac_eth_stop(struct udevice *dev) { struct emac_eth_dev *priv = dev_get_priv(dev); From 4fe8641260d005f24b0ecca8b21d7c51e639c647 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:36 +0100 Subject: [PATCH 05/29] net: sun8i_emac: Name magic bits and simplify read-modify-write calls The EMAC driver contains a lot of magic bits, although the manuals and the Linux driver have all names for them. Define those names and use them when programming the registers. Also this replaces a lot of readl/mask/writel operations with the much easier-to-read setbits_le32() macro. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 113 +++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 5a19a44d067d..7144f96aa301 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -85,15 +85,30 @@ /* H3/A64 EMAC Register's offset */ #define EMAC_CTL0 0x00 +#define EMAC_CTL0_FULL_DUPLEX BIT(0) +#define EMAC_CTL0_SPEED_MASK GENMASK(3, 2) +#define EMAC_CTL0_SPEED_10 (0x2 << 2) +#define EMAC_CTL0_SPEED_100 (0x3 << 2) +#define EMAC_CTL0_SPEED_1000 (0x0 << 2) #define EMAC_CTL1 0x04 +#define EMAC_CTL1_SOFT_RST BIT(0) +#define EMAC_CTL1_BURST_LEN_SHIFT 24 #define EMAC_INT_STA 0x08 #define EMAC_INT_EN 0x0c #define EMAC_TX_CTL0 0x10 +#define EMAC_TX_CTL0_TX_EN BIT(31) #define EMAC_TX_CTL1 0x14 +#define EMAC_TX_CTL1_TX_MD BIT(1) +#define EMAC_TX_CTL1_TX_DMA_EN BIT(30) +#define EMAC_TX_CTL1_TX_DMA_START BIT(31) #define EMAC_TX_FLOW_CTL 0x1c #define EMAC_TX_DMA_DESC 0x20 #define EMAC_RX_CTL0 0x24 +#define EMAC_RX_CTL0_RX_EN BIT(31) #define EMAC_RX_CTL1 0x28 +#define EMAC_RX_CTL1_RX_MD BIT(1) +#define EMAC_RX_CTL1_RX_DMA_EN BIT(30) +#define EMAC_RX_CTL1_RX_DMA_START BIT(31) #define EMAC_RX_DMA_DESC 0x34 #define EMAC_MII_CMD 0x48 #define EMAC_MII_DATA 0x4c @@ -105,6 +120,11 @@ #define EMAC_RX_DMA_STA 0xc0 #define EMAC_RX_CUR_DESC 0xc4 +#define EMAC_DESC_OWN_DMA BIT(31) +#define EMAC_DESC_LAST_DESC BIT(30) +#define EMAC_DESC_FIRST_DESC BIT(29) +#define EMAC_DESC_CHAIN_SECOND BIT(24) + DECLARE_GLOBAL_DATA_PTR; enum emac_variant { @@ -117,7 +137,7 @@ enum emac_variant { struct emac_dma_desc { u32 status; - u32 st; + u32 ctl_size; u32 buf_addr; u32 next; } __aligned(ARCH_DMA_MINALIGN); @@ -236,21 +256,21 @@ static void sun8i_adjust_link(struct emac_eth_dev *priv, v = readl(priv->mac_reg + EMAC_CTL0); if (phydev->duplex) - v |= BIT(0); + v |= EMAC_CTL0_FULL_DUPLEX; else - v &= ~BIT(0); + v &= ~EMAC_CTL0_FULL_DUPLEX; - v &= ~0x0C; + v &= ~EMAC_CTL0_SPEED_MASK; switch (phydev->speed) { case 1000: + v |= EMAC_CTL0_SPEED_1000; break; case 100: - v |= BIT(2); - v |= BIT(3); + v |= EMAC_CTL0_SPEED_100; break; case 10: - v |= BIT(3); + v |= EMAC_CTL0_SPEED_10; break; } writel(v, priv->mac_reg + EMAC_CTL0); @@ -372,8 +392,8 @@ static void rx_descs_init(struct emac_eth_dev *priv) desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE] ; desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; - desc_p->st |= CONFIG_ETH_RXSIZE; - desc_p->status = BIT(31); + desc_p->ctl_size |= CONFIG_ETH_RXSIZE; + desc_p->status = EMAC_DESC_OWN_DMA; } /* Correcting the last pointer of the chain */ @@ -399,8 +419,8 @@ static void tx_descs_init(struct emac_eth_dev *priv) desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE] ; desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; + desc_p->ctl_size = 0; desc_p->status = 0; - desc_p->st = 0; } /* Correcting the last pointer of the chain */ @@ -418,7 +438,7 @@ static void tx_descs_init(struct emac_eth_dev *priv) static int sun8i_emac_eth_start(struct udevice *dev) { struct emac_eth_dev *priv = dev_get_priv(dev); - u32 reg, v; + u32 reg; int timeout = 100; int ret; @@ -439,20 +459,17 @@ static int sun8i_emac_eth_start(struct udevice *dev) /* Rewrite mac address after reset */ sun8i_eth_write_hwaddr(dev); - v = readl(priv->mac_reg + EMAC_TX_CTL1); - /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ - v |= BIT(1); - writel(v, priv->mac_reg + EMAC_TX_CTL1); + /* transmission starts after the full frame arrived in TX DMA FIFO */ + setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_MD); - v = readl(priv->mac_reg + EMAC_RX_CTL1); - /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a + /* + * RX DMA reads data from RX DMA FIFO to host memory after a * complete frame has been written to RX DMA FIFO */ - v |= BIT(1); - writel(v, priv->mac_reg + EMAC_RX_CTL1); + setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_MD); - /* DMA */ - writel(8 << 24, priv->mac_reg + EMAC_CTL1); + /* DMA burst length */ + writel(8 << EMAC_CTL1_BURST_LEN_SHIFT, priv->mac_reg + EMAC_CTL1); /* Initialize rx/tx descriptors */ rx_descs_init(priv); @@ -465,18 +482,13 @@ static int sun8i_emac_eth_start(struct udevice *dev) sun8i_adjust_link(priv, priv->phydev); - /* Start RX DMA */ - v = readl(priv->mac_reg + EMAC_RX_CTL1); - v |= BIT(30); - writel(v, priv->mac_reg + EMAC_RX_CTL1); - /* Start TX DMA */ - v = readl(priv->mac_reg + EMAC_TX_CTL1); - v |= BIT(30); - writel(v, priv->mac_reg + EMAC_TX_CTL1); + /* Start RX/TX DMA */ + setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN); + setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN); /* Enable RX/TX */ - setbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); - setbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); + setbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN); + setbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN); return 0; } @@ -566,7 +578,7 @@ static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) status = desc_p->status; /* Check for DMA own bit */ - if (!(status & BIT(31))) { + if (!(status & EMAC_DESC_OWN_DMA)) { length = (desc_p->status >> 16) & 0x3FFF; if (length < 0x40) { @@ -597,7 +609,7 @@ static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) { struct emac_eth_dev *priv = dev_get_priv(dev); - u32 v, desc_num = priv->tx_currdescnum; + u32 desc_num = priv->tx_currdescnum; struct emac_dma_desc *desc_p = &priv->tx_chain[desc_num]; uintptr_t desc_start = (uintptr_t)desc_p; uintptr_t desc_end = desc_start + @@ -610,22 +622,16 @@ static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) /* Invalidate entire buffer descriptor */ invalidate_dcache_range(desc_start, desc_end); - desc_p->st = length; - /* Mandatory undocumented bit */ - desc_p->st |= BIT(24); + desc_p->ctl_size = length | EMAC_DESC_CHAIN_SECOND; memcpy((void *)data_start, packet, length); /* Flush data to be sent */ flush_dcache_range(data_start, data_end); - /* frame end */ - desc_p->st |= BIT(30); - desc_p->st |= BIT(31); - - /*frame begin */ - desc_p->st |= BIT(29); - desc_p->status = BIT(31); + /* frame begin and end */ + desc_p->ctl_size |= EMAC_DESC_LAST_DESC | EMAC_DESC_FIRST_DESC; + desc_p->status = EMAC_DESC_OWN_DMA; /*Descriptors st and status field has changed, so FLUSH it */ flush_dcache_range(desc_start, desc_end); @@ -636,10 +642,12 @@ static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) priv->tx_currdescnum = desc_num; /* Start the DMA */ - v = readl(priv->mac_reg + EMAC_TX_CTL1); - v |= BIT(31);/* mandatory */ - v |= BIT(30);/* mandatory */ - writel(v, priv->mac_reg + EMAC_TX_CTL1); + setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_START); + + /* + * Since we copied the data above, we return here without waiting + * for the packet to be actually send out. + */ return 0; } @@ -752,7 +760,7 @@ static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, roundup(sizeof(u32), ARCH_DMA_MINALIGN); /* Make the current descriptor valid again */ - desc_p->status |= BIT(31); + desc_p->status |= EMAC_DESC_OWN_DMA; /* Flush Status field of descriptor */ flush_dcache_range(desc_start, desc_end); @@ -770,11 +778,12 @@ static void sun8i_emac_eth_stop(struct udevice *dev) struct emac_eth_dev *priv = dev_get_priv(dev); /* Stop Rx/Tx transmitter */ - clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); - clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); + clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN); + clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN); - /* Stop TX DMA */ - clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, BIT(30)); + /* Stop RX/TX DMA */ + clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN); + clrbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN); phy_shutdown(priv->phydev); } From 69853123c513cf478992ca44e8a39afd651811a9 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:37 +0100 Subject: [PATCH 06/29] net: sun8i_emac: Improve cache maintenance on RX descriptor init Before we initialise the RX descriptors, there is no need to *clean* them from the cache, as we touch them for the first time. However we should cover the case that those buffers contain dirty cache lines, which could be evicted and written back to DRAM any time later, in the worst case *after* the MAC has transferred a packet into them. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 7144f96aa301..c567b9e08274 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -383,16 +383,21 @@ static void rx_descs_init(struct emac_eth_dev *priv) struct emac_dma_desc *desc_p; u32 idx; - /* flush Rx buffers */ - flush_dcache_range((uintptr_t)rxbuffs, (ulong)rxbuffs + - RX_TOTAL_BUFSIZE); + /* + * Make sure we don't have dirty cache lines around, which could + * be cleaned to DRAM *after* the MAC has already written data to it. + */ + invalidate_dcache_range((uintptr_t)desc_table_p, + (uintptr_t)desc_table_p + sizeof(priv->rx_chain)); + invalidate_dcache_range((uintptr_t)rxbuffs, + (uintptr_t)rxbuffs + sizeof(priv->rxbuffer)); for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE] ; desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; - desc_p->ctl_size |= CONFIG_ETH_RXSIZE; + desc_p->ctl_size = CONFIG_ETH_RXSIZE; desc_p->status = EMAC_DESC_OWN_DMA; } From ed909de5d3300f473e5ee5a520267c3e04b43b76 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:38 +0100 Subject: [PATCH 07/29] net: sun8i_emac: Reduce cache maintenance on TX descriptor init When we initialise the TX descriptors, there is no need yet to clean them all to memory, as they don't contain any data yet. Later we will touch and clean each descriptor anyway. However we tell the MAC about the beginning of the chain, so we have to clean at least the first descriptor, to make it clear that this is empty and there are no packets to transfer yet. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index c567b9e08274..335c85f3c263 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -431,10 +431,10 @@ static void tx_descs_init(struct emac_eth_dev *priv) /* Correcting the last pointer of the chain */ desc_p->next = (uintptr_t)&desc_table_p[0]; - /* Flush all Tx buffer descriptors */ + /* Flush the first TX buffer descriptor we will tell the MAC about. */ flush_dcache_range((uintptr_t)priv->tx_chain, (uintptr_t)priv->tx_chain + - sizeof(priv->tx_chain)); + sizeof(priv->tx_chain[0])); writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC); priv->tx_currdescnum = 0; From eaeadb50ea4d65e6fdbde7224b089dca6a41ff1e Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:39 +0100 Subject: [PATCH 08/29] net: sun8i_emac: Drop unneeded cache invalidation before sending There is no reason to invalidate a TX descriptor before we are setting it up, as we will only write to a field. Remove the not needed invalidate_dcache_range() call. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 335c85f3c263..45130e1ba1b1 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -624,9 +624,6 @@ static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) uintptr_t data_end = data_start + roundup(length, ARCH_DMA_MINALIGN); - /* Invalidate entire buffer descriptor */ - invalidate_dcache_range(desc_start, desc_end); - desc_p->ctl_size = length | EMAC_DESC_CHAIN_SECOND; memcpy((void *)data_start, packet, length); From 8c274ec0924e81f60f154a17465f48dccbae3639 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:40 +0100 Subject: [PATCH 09/29] net: sun8i_emac: Wrap and simplify cache maintenance operations To meet the current alignment requirements for our cache maintenance functions, we were explicitly aligning the *arguments* to those calls. This is not only ugly to read, but also wrong, as we need to make sure we are not accidentally stepping on other data. Provide wrapper functions for the common case of cleaning or invalidating a descriptor, to make the cache maintenance calls more readable. This fixes a good deal of the problematic calls. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 45130e1ba1b1..c005b98ae83b 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -376,6 +376,14 @@ static int sun8i_phy_init(struct emac_eth_dev *priv, void *dev) return 0; } +#define cache_clean_descriptor(desc) \ + flush_dcache_range((uintptr_t)(desc), \ + (uintptr_t)(desc) + sizeof(struct emac_dma_desc)) + +#define cache_inv_descriptor(desc) \ + invalidate_dcache_range((uintptr_t)(desc), \ + (uintptr_t)(desc) + sizeof(struct emac_dma_desc)) + static void rx_descs_init(struct emac_eth_dev *priv) { struct emac_dma_desc *desc_table_p = &priv->rx_chain[0]; @@ -432,9 +440,7 @@ static void tx_descs_init(struct emac_eth_dev *priv) desc_p->next = (uintptr_t)&desc_table_p[0]; /* Flush the first TX buffer descriptor we will tell the MAC about. */ - flush_dcache_range((uintptr_t)priv->tx_chain, - (uintptr_t)priv->tx_chain + - sizeof(priv->tx_chain[0])); + cache_clean_descriptor(desc_table_p); writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC); priv->tx_currdescnum = 0; @@ -570,15 +576,11 @@ static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; int length = -EAGAIN; int good_packet = 1; - uintptr_t desc_start = (uintptr_t)desc_p; - uintptr_t desc_end = desc_start + - roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); - ulong data_start = (uintptr_t)desc_p->buf_addr; ulong data_end; /* Invalidate entire buffer descriptor */ - invalidate_dcache_range(desc_start, desc_end); + cache_inv_descriptor(desc_p); status = desc_p->status; @@ -616,10 +618,6 @@ static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) struct emac_eth_dev *priv = dev_get_priv(dev); u32 desc_num = priv->tx_currdescnum; struct emac_dma_desc *desc_p = &priv->tx_chain[desc_num]; - uintptr_t desc_start = (uintptr_t)desc_p; - uintptr_t desc_end = desc_start + - roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); - uintptr_t data_start = (uintptr_t)desc_p->buf_addr; uintptr_t data_end = data_start + roundup(length, ARCH_DMA_MINALIGN); @@ -635,8 +633,8 @@ static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) desc_p->ctl_size |= EMAC_DESC_LAST_DESC | EMAC_DESC_FIRST_DESC; desc_p->status = EMAC_DESC_OWN_DMA; - /*Descriptors st and status field has changed, so FLUSH it */ - flush_dcache_range(desc_start, desc_end); + /* make sure the MAC reads the actual data from DRAM */ + cache_clean_descriptor(desc_p); /* Move to next Descriptor and wrap around */ if (++desc_num >= CONFIG_TX_DESCR_NUM) @@ -757,15 +755,12 @@ static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, struct emac_eth_dev *priv = dev_get_priv(dev); u32 desc_num = priv->rx_currdescnum; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; - uintptr_t desc_start = (uintptr_t)desc_p; - uintptr_t desc_end = desc_start + - roundup(sizeof(u32), ARCH_DMA_MINALIGN); - /* Make the current descriptor valid again */ + /* give the current descriptor back to the MAC */ desc_p->status |= EMAC_DESC_OWN_DMA; /* Flush Status field of descriptor */ - flush_dcache_range(desc_start, desc_end); + cache_clean_descriptor(desc_p); /* Move to next desc and wrap-around condition. */ if (++desc_num >= CONFIG_RX_DESCR_NUM) From 09501ff32eee22469daf9279a46bb3b9a1fe7625 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:41 +0100 Subject: [PATCH 10/29] net: sun8i_emac: Fix overlong lines When iterating over all RX/TX buffers, we were using a rather long "idx" control variable, which lead to a nasty overlong line. Replace "idx" with "i" to avoid this. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index c005b98ae83b..1ae5bfd38832 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -389,7 +389,7 @@ static void rx_descs_init(struct emac_eth_dev *priv) struct emac_dma_desc *desc_table_p = &priv->rx_chain[0]; char *rxbuffs = &priv->rxbuffer[0]; struct emac_dma_desc *desc_p; - u32 idx; + int i; /* * Make sure we don't have dirty cache lines around, which could @@ -400,11 +400,10 @@ static void rx_descs_init(struct emac_eth_dev *priv) invalidate_dcache_range((uintptr_t)rxbuffs, (uintptr_t)rxbuffs + sizeof(priv->rxbuffer)); - for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { - desc_p = &desc_table_p[idx]; - desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE] - ; - desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; + for (i = 0; i < CONFIG_RX_DESCR_NUM; i++) { + desc_p = &desc_table_p[i]; + desc_p->buf_addr = (uintptr_t)&rxbuffs[i * CONFIG_ETH_BUFSIZE]; + desc_p->next = (uintptr_t)&desc_table_p[i + 1]; desc_p->ctl_size = CONFIG_ETH_RXSIZE; desc_p->status = EMAC_DESC_OWN_DMA; } @@ -425,13 +424,12 @@ static void tx_descs_init(struct emac_eth_dev *priv) struct emac_dma_desc *desc_table_p = &priv->tx_chain[0]; char *txbuffs = &priv->txbuffer[0]; struct emac_dma_desc *desc_p; - u32 idx; + int i; - for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { - desc_p = &desc_table_p[idx]; - desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE] - ; - desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; + for (i = 0; i < CONFIG_TX_DESCR_NUM; i++) { + desc_p = &desc_table_p[i]; + desc_p->buf_addr = (uintptr_t)&txbuffs[i * CONFIG_ETH_BUFSIZE]; + desc_p->next = (uintptr_t)&desc_table_p[i + 1]; desc_p->ctl_size = 0; desc_p->status = 0; } From 2c5600c38ce642e1763afe4f3b5fb6dd8f442aa5 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:42 +0100 Subject: [PATCH 11/29] net: sun8i_emac: Fix MAC soft reset The EMAC soft reset routine was subtly broken, using an open coded timeout routine without any actual delay. Remove the unneeded initial reset bit read, and call wait_for_bit_le32() to handle the timeout correctly. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 1ae5bfd38832..f7c55b3c961a 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -447,22 +447,15 @@ static void tx_descs_init(struct emac_eth_dev *priv) static int sun8i_emac_eth_start(struct udevice *dev) { struct emac_eth_dev *priv = dev_get_priv(dev); - u32 reg; - int timeout = 100; int ret; - reg = readl((priv->mac_reg + EMAC_CTL1)); - - if (!(reg & 0x1)) { - /* Soft reset MAC */ - setbits_le32((priv->mac_reg + EMAC_CTL1), 0x1); - do { - reg = readl(priv->mac_reg + EMAC_CTL1); - } while ((reg & 0x01) != 0 && (--timeout)); - if (!timeout) { - printf("%s: Timeout\n", __func__); - return -1; - } + /* Soft reset MAC */ + writel(EMAC_CTL1_SOFT_RST, priv->mac_reg + EMAC_CTL1); + ret = wait_for_bit_le32(priv->mac_reg + EMAC_CTL1, + EMAC_CTL1_SOFT_RST, false, 10, true); + if (ret) { + printf("%s: Timeout\n", __func__); + return ret; } /* Rewrite mac address after reset */ From 7edcb4e288efb9eb4412373638dab87cd78d92ba Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:43 +0100 Subject: [PATCH 12/29] net: sun8i_emac: Simplify and fix error handling for RX The error handling in recv() is somewhat broken, for instance good_packet isn't really used, and it's hardly readable. Also we try to check for short or too big packets, but those are actually filtered out by the hardware. Simplify the whole routine and improve the error handling: - Bail out early if the current RX descriptor is not ready. - Enable propagation of runt, huge and broken packets. - Check for runt and huge packets, and return 0 to indicate this. This will force the framework to call free_pkt for cleanup. - Avoid aligning the packet buffer for invalidation again. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 56 +++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index f7c55b3c961a..2b39ad81b661 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -107,6 +107,8 @@ #define EMAC_RX_CTL0_RX_EN BIT(31) #define EMAC_RX_CTL1 0x28 #define EMAC_RX_CTL1_RX_MD BIT(1) +#define EMAC_RX_CTL1_RX_RUNT_FRM BIT(2) +#define EMAC_RX_CTL1_RX_ERR_FRM BIT(3) #define EMAC_RX_CTL1_RX_DMA_EN BIT(30) #define EMAC_RX_CTL1_RX_DMA_START BIT(31) #define EMAC_RX_DMA_DESC 0x34 @@ -125,6 +127,8 @@ #define EMAC_DESC_FIRST_DESC BIT(29) #define EMAC_DESC_CHAIN_SECOND BIT(24) +#define EMAC_DESC_RX_ERROR_MASK 0x400068db + DECLARE_GLOBAL_DATA_PTR; enum emac_variant { @@ -485,7 +489,8 @@ static int sun8i_emac_eth_start(struct udevice *dev) sun8i_adjust_link(priv, priv->phydev); /* Start RX/TX DMA */ - setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN); + setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN | + EMAC_RX_CTL1_RX_ERR_FRM | EMAC_RX_CTL1_RX_RUNT_FRM); setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN); /* Enable RX/TX */ @@ -565,10 +570,8 @@ static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) struct emac_eth_dev *priv = dev_get_priv(dev); u32 status, desc_num = priv->rx_currdescnum; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; - int length = -EAGAIN; - int good_packet = 1; - ulong data_start = (uintptr_t)desc_p->buf_addr; - ulong data_end; + uintptr_t data_start = (uintptr_t)desc_p->buf_addr; + int length; /* Invalidate entire buffer descriptor */ cache_inv_descriptor(desc_p); @@ -576,30 +579,31 @@ static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) status = desc_p->status; /* Check for DMA own bit */ - if (!(status & EMAC_DESC_OWN_DMA)) { - length = (desc_p->status >> 16) & 0x3FFF; + if (status & EMAC_DESC_OWN_DMA) + return -EAGAIN; - if (length < 0x40) { - good_packet = 0; - debug("RX: Bad Packet (runt)\n"); - } + length = (status >> 16) & 0x3fff; - data_end = data_start + length; - /* Invalidate received data */ - invalidate_dcache_range(rounddown(data_start, - ARCH_DMA_MINALIGN), - roundup(data_end, - ARCH_DMA_MINALIGN)); - if (good_packet) { - if (length > CONFIG_ETH_RXSIZE) { - printf("Received packet is too big (len=%d)\n", - length); - return -EMSGSIZE; - } - *packetp = (uchar *)(ulong)desc_p->buf_addr; - return length; - } + /* make sure we read from DRAM, not our cache */ + invalidate_dcache_range(data_start, + data_start + roundup(length, ARCH_DMA_MINALIGN)); + + if (status & EMAC_DESC_RX_ERROR_MASK) { + debug("RX: packet error: 0x%x\n", + status & EMAC_DESC_RX_ERROR_MASK); + return 0; } + if (length < 0x40) { + debug("RX: Bad Packet (runt)\n"); + return 0; + } + + if (length > CONFIG_ETH_RXSIZE) { + debug("RX: Too large packet (%d bytes)\n", length); + return 0; + } + + *packetp = (uchar *)(ulong)desc_p->buf_addr; return length; } From 88ae8fba84d85454008e3c89fcb790f8d077d7b5 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 21 Oct 2020 23:27:32 +0530 Subject: [PATCH 13/29] net: sun8i-emac: Make internal PHY handling more robust The current implementation of sun8i_get_ephy_nodes() makes quite some assumptions, in general relying on DT path names is a bad idea. I think the idea of the code was to determine if we are using the internal PHY, for which there are simpler and more robust methods: Rewrite (and rename) the existing function to simply lookup the DT node that "phy-handle" points to, using the device's DT node. Then check whether the parent of that PHY node is using an "H3 internal MDIO" compatible string. If we ever get another internal MDIO bus implementation, we will probably need code adjustments anyway, so this is good enough for now. Signed-off-by: Andre Przywara [jagan: rebase on master] Signed-off-by: Jagan Teki Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 47 +++++++++++++--------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 2b39ad81b661..985ac961f1c4 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -810,47 +810,30 @@ static const struct eth_ops sun8i_emac_eth_ops = { .stop = sun8i_emac_eth_stop, }; -static int sun8i_get_ephy_nodes(struct udevice *dev, struct emac_eth_dev *priv) +static int sun8i_handle_internal_phy(struct udevice *dev, struct emac_eth_dev *priv) { - int emac_node, ephy_node, ret, ephy_handle; - - emac_node = fdt_path_offset(gd->fdt_blob, - "/soc/ethernet@1c30000"); - if (emac_node < 0) { - debug("failed to get emac node\n"); - return emac_node; - } - ephy_handle = fdtdec_lookup_phandle(gd->fdt_blob, - emac_node, "phy-handle"); + struct ofnode_phandle_args phandle; + int ret; - /* look for mdio-mux node for internal PHY node */ - ephy_node = fdt_path_offset(gd->fdt_blob, - "/soc/ethernet@1c30000/mdio-mux/mdio@1/ethernet-phy@1"); - if (ephy_node < 0) { - debug("failed to get mdio-mux with internal PHY\n"); - return ephy_node; - } + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "phy-handle", + NULL, 0, 0, &phandle); + if (ret) + return ret; - /* This is not the phy we are looking for */ - if (ephy_node != ephy_handle) + /* If the PHY node is not a child of the internal MDIO bus, we are + * using some external PHY. + */ + if (!ofnode_device_is_compatible(ofnode_get_parent(phandle.node), + "allwinner,sun8i-h3-mdio-internal")) return 0; - ret = fdt_node_check_compatible(gd->fdt_blob, ephy_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(ephy_node), 0, - &priv->ephy_clk); + ret = clk_get_by_index_nodev(phandle.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(ephy_node), 0, - &priv->ephy_rst); + ret = reset_get_by_index_nodev(phandle.node, 0, &priv->ephy_rst); if (ret) { dev_err(dev, "failed to get EPHY TX reset\n"); return ret; @@ -942,7 +925,7 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) } if (priv->variant == H3_EMAC) { - ret = sun8i_get_ephy_nodes(dev, priv); + ret = sun8i_handle_internal_phy(dev, priv); if (ret) return ret; } From 4f0278dac56a658ef1e0967fec0bb95372a875bd Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:45 +0100 Subject: [PATCH 14/29] net: sun8i-emac: Lower MDIO frequency When sending a command via the MDIO bus, the Designware MAC expects some bits in the CMD register to describe the clock divider value between the main clock and the MDIO clock. So far we were omitting these bits, resulting in setting "00", which means "/ 16", so ending up with an MDIO frequency of either 18.75 or 12.5 MHz. All the internal PHYs in the H3/H5/H6 SoCs as well as the Gbit Realtek PHYs seem to be fine with that - although it looks like to be severly overclocked (the MDIO spec limits the frequency to 2.5 MHz). However the external 100Mbit PHY on the Pine64 (non-plus) board is not happy with that, Ethernet was actually never working there, as the PHY didn't probe. As we set the EMAC clock (via AHB2) to 300 MHz in ATF (on the 64-bit SoCs), and use 200 MHz on the H3, we need the highest divider of 128 to let the MDIO clock end up below the required 2.5 MHz. This enables Ethernet on the Pine64(non-plus). Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- drivers/net/sun8i_emac.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 985ac961f1c4..4524604126c9 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -41,6 +41,11 @@ #define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 #define MDIO_CMD_MII_PHY_ADDR_MASK 0x0001f000 #define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 +#define MDIO_CMD_MII_CLK_CSR_DIV_16 0x0 +#define MDIO_CMD_MII_CLK_CSR_DIV_32 0x1 +#define MDIO_CMD_MII_CLK_CSR_DIV_64 0x2 +#define MDIO_CMD_MII_CLK_CSR_DIV_128 0x3 +#define MDIO_CMD_MII_CLK_CSR_SHIFT 20 #define CONFIG_TX_DESCR_NUM 32 #define CONFIG_RX_DESCR_NUM 32 @@ -199,6 +204,12 @@ static int sun8i_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & MDIO_CMD_MII_PHY_ADDR_MASK; + /* + * The EMAC clock is either 200 or 300 MHz, so we need a divider + * of 128 to get the MDIO frequency below the required 2.5 MHz. + */ + mii_cmd |= MDIO_CMD_MII_CLK_CSR_DIV_128 << MDIO_CMD_MII_CLK_CSR_SHIFT; + mii_cmd |= MDIO_CMD_MII_BUSY; writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD); @@ -224,6 +235,12 @@ static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & MDIO_CMD_MII_PHY_ADDR_MASK; + /* + * The EMAC clock is either 200 or 300 MHz, so we need a divider + * of 128 to get the MDIO frequency below the required 2.5 MHz. + */ + mii_cmd |= MDIO_CMD_MII_CLK_CSR_DIV_128 << MDIO_CMD_MII_CLK_CSR_SHIFT; + mii_cmd |= MDIO_CMD_MII_WRITE; mii_cmd |= MDIO_CMD_MII_BUSY; From 3940a471553a31cda4e19024a1bf5bbdc4ac9e46 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 6 Jul 2020 01:40:46 +0100 Subject: [PATCH 15/29] sunxi: Pine-H64: Explicitly enable PHY regulator According to the devicetree and the schematic, the 3.3V power rail for the PHY is enabled by GPIO PC16. It's wired as active-high, with a pull-up resistor, so actually works already when the GPIO is in High-Z state. However we should not take any chances and explicitly set the GPIO pin to high, to avoid accidentally losing the PHY power. The existing MACPWR Kconfig allows to do this easily. Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Tested-by: Amit Singh Tomar # Pine64+ Reviewed-by: Jagan Teki --- configs/pine_h64_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 328849d7243f..07ad31e3a707 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -11,5 +11,6 @@ CONFIG_SPL_SPI_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y +CONFIG_MACPWR="PC16" CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y From 5108a257b77ef0398d4743d65fd92f70738f73f0 Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Sun, 9 Aug 2020 12:15:02 +0200 Subject: [PATCH 16/29] git-mailrc: Update email address of Maxime Ripard Update email address of Maxime Ripard in git-mailrc to match more recently updated entry in MAINTAINERS. commit 9bd9b2bcbee1 ("MAINTAINERS: Update my email address") commit bf8f4c4400e3 ("MAINTAINERS: Update email address for Maxime Ripard") Signed-off-by: Jonas Smedegaard Acked-by: Maxime Ripard Reviewed-by: Jagan Teki --- doc/git-mailrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/git-mailrc b/doc/git-mailrc index 31595a71c993..bbca3a9a373b 100644 --- a/doc/git-mailrc +++ b/doc/git-mailrc @@ -36,7 +36,7 @@ alias marex Marek Vasut alias mariosix Mario Six alias masahiro Masahiro Yamada alias mateusz Mateusz Kulikowski -alias maxime Maxime Ripard +alias maxime Maxime Ripard alias mbrugger Matthias Brugger alias monstr Michal Simek alias prom Minkyu Kang From fb5b1678433711091e6f89a1c1ef24c422b5f521 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 25 Jul 2020 20:18:51 +0200 Subject: [PATCH 17/29] arm: sunxi: increase SYS_MALLOC_F_LEN The current default of 0x400 for SYS_MALLOC_F_LEN is too small if any additional drivers marked as DM_FLAG_PRE_RELOC are loaded before relocation. CONFIG_RSA=y which is needed for UEFI secure boot or for FIT image verification loads the driver mod_exp_sw which has DM_FLAG_PRE_RELOC. CONFIG_LOG=Y is another setting requiring additional early malloc area, cf. log_init(). When running pine64-lts_defconfig with CONFIG_RSA=y and debug UART enabled we see as output in main U-Boot alloc_simple() alloc space exhausted With this patch the default values of SYS_MALLOC_F_LEN and SPL_SYS_MALLOC_F_LEN on ARCH_SUNXI are raised to 0x2000. Signed-off-by: Heinrich Schuchardt Reviewed-by: Jagan Teki Reviewed-by: Simon Glass --- Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Kconfig b/Kconfig index 520679f57e66..990353800f40 100644 --- a/Kconfig +++ b/Kconfig @@ -200,7 +200,7 @@ config SYS_MALLOC_F_LEN default 0x2000 if (ARCH_IMX8 || ARCH_IMX8M || ARCH_MX7 || \ ARCH_MX7ULP || ARCH_MX6 || ARCH_MX5 || \ ARCH_LS1012A || ARCH_LS1021A || ARCH_LS1043A || \ - ARCH_LS1046A || ARCH_QEMU) + ARCH_LS1046A || ARCH_QEMU || ARCH_SUNXI) default 0x400 help Before relocation, memory is very limited on many platforms. Still, From 0e3160b72323c819e7d48c0ae6eb047eb0fe4648 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 16 Oct 2020 17:33:08 +0800 Subject: [PATCH 18/29] sunxi: make V3s DRAM initialization more proper Previously, because we have no source code about the DRAM initialization of V3s and missing some configurations (delays and MBUS QoS info), our V3s DRAM initialization sequence is hacked from the H3 one. As the SDK shipped with PineCube contains source code for V3s libdram, we can retrieve these information from it and tweak some other magic bits. Signed-off-by: Icenowy Zheng Reviewed-by: Andre Przywara Acked-by: Jagan Teki --- arch/arm/include/asm/arch-sunxi/cpu.h | 1 + arch/arm/mach-sunxi/dram_sunxi_dw.c | 91 +++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 4c399b0a15be..8b57d24e2f0c 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -16,6 +16,7 @@ #define SOCID_A64 0x1689 #define SOCID_H3 0x1680 +#define SOCID_V3S 0x1681 #define SOCID_H5 0x1718 #define SOCID_R40 0x1701 diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c index a46253852196..d0600011ffa8 100644 --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c @@ -63,6 +63,8 @@ enum { MBUS_PORT_CSI = 5, MBUS_PORT_NAND = 6, MBUS_PORT_SS = 7, + MBUS_PORT_DE_V3S = 8, + MBUS_PORT_DE_CFD_V3S = 9, MBUS_PORT_TS = 8, MBUS_PORT_DI = 9, MBUS_PORT_DE = 10, @@ -134,6 +136,29 @@ static void mctl_set_master_priority_h3(void) MBUS_CONF(DE_CFD, true, HIGH, 0, 1024, 288, 64); } +static void mctl_set_master_priority_v3s(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel((1 << 16) | (400 << 0), &mctl_com->bwcr); + + /* set cpu high priority */ + writel(0x00000001, &mctl_com->mapr); + + MBUS_CONF( CPU, true, HIGHEST, 0, 160, 100, 80); + MBUS_CONF( GPU, true, HIGH, 0, 1792, 1536, 0); + MBUS_CONF( UNUSED, true, HIGHEST, 0, 256, 128, 80); + MBUS_CONF( DMA, true, HIGH, 0, 256, 100, 0); + MBUS_CONF( VE, true, HIGH, 0, 2048, 1600, 0); + MBUS_CONF( CSI, true, HIGHEST, 0, 384, 256, 0); + MBUS_CONF( NAND, true, HIGH, 0, 100, 50, 0); + MBUS_CONF( SS, true, HIGH, 0, 384, 256, 0); + MBUS_CONF( DE_V3S, false, HIGH, 0, 8192, 4096, 0); + MBUS_CONF(DE_CFD_V3S, true, HIGH, 0, 640, 256, 0); +} + static void mctl_set_master_priority_a64(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -231,6 +256,9 @@ static void mctl_set_master_priority(uint16_t socid) case SOCID_H3: mctl_set_master_priority_h3(); return; + case SOCID_V3S: + mctl_set_master_priority_v3s(); + return; case SOCID_A64: mctl_set_master_priority_a64(); return; @@ -334,6 +362,28 @@ static void mctl_h3_zq_calibration_quirk(struct dram_para *para) } } +static void mctl_v3s_zq_calibration_quirk(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u32 reg_val; + + clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, + CONFIG_DRAM_ZQ & 0xffffff); + mctl_phy_init(PIR_ZCAL); + + reg_val = readl(&mctl_ctl->zqdr[0]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[0]); + + reg_val = readl(&mctl_ctl->zqdr[1]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[1]); +} + static void mctl_set_cr(uint16_t socid, struct dram_para *para) { struct sunxi_mctl_com_reg * const mctl_com = @@ -391,7 +441,7 @@ static void mctl_sys_init(uint16_t socid, struct dram_para *para) CCM_DRAMCLK_CFG_DIV(1) | CCM_DRAMCLK_CFG_SRC_PLL11 | CCM_DRAMCLK_CFG_UPD); - } else if (socid == SOCID_H3 || socid == SOCID_H5) { + } else if (socid == SOCID_H3 || socid == SOCID_H5 || socid == SOCID_V3S) { clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false); clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV_MASK | @@ -474,6 +524,13 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para) /* dphy & aphy phase select 270 degree */ clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), (0x1 << 10) | (0x2 << 8)); + } else if (socid == SOCID_V3S) { + /* dx ddr_clk & hdr_clk dynamic mode */ + clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12)); + + /* dphy & aphy phase select 270 degree */ + clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), + (0x1 << 10) | (0x1 << 8)); } else if (socid == SOCID_A64 || socid == SOCID_H5) { /* dphy & aphy phase select ? */ clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), @@ -506,7 +563,12 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para) mctl_set_bit_delays(para); udelay(50); - if (socid == SOCID_H3) { + if (socid == SOCID_V3S) { + mctl_v3s_zq_calibration_quirk(para); + + mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | + PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE); + } else if (socid == SOCID_H3) { mctl_h3_zq_calibration_quirk(para); mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | @@ -570,7 +632,7 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para) udelay(10); /* set PGCR3, CKE polarity */ - if (socid == SOCID_H3) + if (socid == SOCID_H3 || socid == SOCID_V3S) writel(0x00aa0060, &mctl_ctl->pgcr[3]); else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) writel(0xc0aa0060, &mctl_ctl->pgcr[3]); @@ -636,6 +698,22 @@ static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para) 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0 } +#define SUN8I_V3S_DX_READ_DELAYS \ + {{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0 }, \ + { 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN8I_V3S_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN8I_V3S_AC_DELAYS \ + { 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0 } + #define SUN8I_R40_DX_READ_DELAYS \ {{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ @@ -702,6 +780,10 @@ unsigned long sunxi_dram_init(void) .dx_read_delays = SUN8I_H3_DX_READ_DELAYS, .dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS, .ac_delays = SUN8I_H3_AC_DELAYS, +#elif defined(CONFIG_MACH_SUN8I_V3S) + .dx_read_delays = SUN8I_V3S_DX_READ_DELAYS, + .dx_write_delays = SUN8I_V3S_DX_WRITE_DELAYS, + .ac_delays = SUN8I_V3S_AC_DELAYS, #elif defined(CONFIG_MACH_SUN8I_R40) .dx_read_delays = SUN8I_R40_DX_READ_DELAYS, .dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS, @@ -728,8 +810,7 @@ unsigned long sunxi_dram_init(void) /* Currently we cannot support R40 with dual rank memory */ para.dual_rank = 0; #elif defined(CONFIG_MACH_SUN8I_V3S) - /* TODO: set delays and mbus priority for V3s */ - uint16_t socid = SOCID_H3; + uint16_t socid = SOCID_V3S; #elif defined(CONFIG_MACH_SUN50I) uint16_t socid = SOCID_A64; #elif defined(CONFIG_MACH_SUN50I_H5) From 509978e5d2cdfc2faff1721de035c1f4f14fbcae Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:08 -0500 Subject: [PATCH 19/29] Makefile: Only define u-boot.itb rule when applicable If neither CONFIG_SPL_FIT_SOURCE nor CONFIG_USE_SPL_FIT_GENERATOR is enabled, U_BOOT_ITS will be undefined, and attempting to make u-boot.itb will pass invalid arguments to mkimage, causing it to print its help message. Remove the rule in that case, so it is more obvious that u-boot.itb is not something that can be made. This will reduce confusion as platforms move away from CONFIG_USE_SPL_FIT_GENERATOR, as u-boot.itb was previously a valid goal for those platforms. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 28c9f31fb444..2b2167573f6b 100644 --- a/Makefile +++ b/Makefile @@ -1440,11 +1440,13 @@ else MKIMAGEFLAGS_u-boot.itb = -E endif +ifdef U_BOOT_ITS u-boot.itb: u-boot-nodtb.bin \ $(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_OF_HOSTFILE),dts/dt.dtb) \ $(U_BOOT_ITS) FORCE $(call if_changed,mkfitimage) $(BOARD_SIZE_CHECK) +endif u-boot-spl.kwb: u-boot.img spl/u-boot-spl.bin FORCE $(call if_changed,mkimage) From b26536ad3d940d64a070d7c2d0ecb0f28bad8289 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:09 -0500 Subject: [PATCH 20/29] binman: Only write FDT once per node Due to an extra level of indentation, the "data" property containing the FDT was being written repeatedly after every other property in the node. This caused the generated FIT image to be invalid. Move the block up one level, so the property is added exactly once. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- tools/binman/etype/fit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index de4745c55211..8a24f12aa599 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -205,10 +205,10 @@ def _AddNode(base_node, depth, node): b'SEQ', tools.ToBytes(str(seq + 1))) fsw.property(pname, val) - # Add data for 'fdt' nodes (but not 'config') - if depth == 1 and in_images: - fsw.property('data', - tools.ReadFile(fname)) + # Add data for 'fdt' nodes (but not 'config') + if depth == 1 and in_images: + fsw.property('data', + tools.ReadFile(fname)) else: if self._fdts is None: if self._fit_list_prop: From eafbdbb8ebd8a8ca0da0a6571f8510784ed037bc Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:10 -0500 Subject: [PATCH 21/29] sunxi: binman: Fix spacing between nodes Nodes should have a blank line separating them from sibling nodes and properties. Add the necessary lines. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- arch/arm/dts/sunxi-u-boot.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index c97943b3c196..92a644723188 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -14,9 +14,11 @@ u-boot-sunxi-with-spl { filename = "u-boot-sunxi-with-spl.bin"; pad-byte = <0xff>; + blob { filename = "spl/sunxi-spl.bin"; }; + #ifdef CONFIG_ARM64 fit { description = "Configuration to load ATF before U-Boot"; @@ -34,6 +36,7 @@ u-boot-nodtb { }; }; + atf { description = "ARM Trusted Firmware"; type = "firmware"; @@ -47,6 +50,7 @@ load = <0x44000>; entry = <0x44000>; #endif + atf-bl31 { missing-msg = "atf-bl31-sunxi"; }; @@ -61,6 +65,7 @@ configurations { default = "config-1"; + @config-SEQ { description = "NAME"; firmware = "uboot"; From 6b7c7ed80a5a02325adcaef1eaaeb2d9ffedc314 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:11 -0500 Subject: [PATCH 22/29] sunxi: binman: Provide a default BL31 filename Prior to commit 7f7f8aca8257 ("sunxi: Convert 64-bit boards to use binman"), if the BL31 environment variable was not defined, the firmware would be loaded from a file "bl31.bin" in the current directory. Restore that behavior by providing that as the default filename in case no entry arg is provided, which will be the case if the environment variable is unset. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- arch/arm/dts/sunxi-u-boot.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index 92a644723188..b7195c567100 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -52,6 +52,7 @@ #endif atf-bl31 { + filename = "bl31.bin"; missing-msg = "atf-bl31-sunxi"; }; }; From 827c420c40030c9ddca16e97c2bb01244f2c66b4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:12 -0500 Subject: [PATCH 23/29] sunxi: binman: Use a macro for the BL31 load address This consolidates the SoC-specific part at the top of the file to avoid cluttering it up with preprocessor conditions. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- arch/arm/dts/sunxi-u-boot.dtsi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index b7195c567100..b8a0d4b6b08e 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -1,5 +1,11 @@ #include +#ifdef CONFIG_MACH_SUN50I_H6 +#define BL31_ADDR 0x104000 +#else +#define BL31_ADDR 0x44000 +#endif + / { aliases { mmc1 = &mmc2; @@ -42,14 +48,8 @@ type = "firmware"; arch = "arm64"; compression = "none"; -/* TODO: Do this with an overwrite in this board's dtb? */ -#ifdef CONFIG_MACH_SUN50I_H6 - load = <0x104000>; - entry = <0x104000>; -#else - load = <0x44000>; - entry = <0x44000>; -#endif + load = ; + entry = ; atf-bl31 { filename = "bl31.bin"; From cf70553e2c11a3f75835a51ecfbf898ac7e899f3 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:13 -0500 Subject: [PATCH 24/29] spl: fit: Minimally parse OS properties with FIT_IMAGE_TINY Some boards, specifically 64-bit Allwinner boards (sun50i), are extremely limited on SPL size. One strategy that was used to make space was to remove the FIT "os" property parsing code, because it uses a rather large lookup table. However, this forces the legacy FIT parsing code path, which requires the "firmware" entry in the FIT to reference the U-Boot binary, even if U-Boot is not the next binary in the boot sequence (for example, on sun50i boards, ATF is run first). This prevents the same FIT image from being used with a SPL with CONFIG_SPL_FIT_IMAGE_TINY=n and CONFIG_SPL_ATF=y, because the boot method selection code looks at `spl_image.os`, which is only set from the "firmware" entry's "os" property. To be able to use CONFIG_SPL_ATF=y, the "firmware" entry in the FIT must be ATF, and U-Boot must be a loadable. For this to work, we need to parse the "os" property just enough to tell U-Boot from other images, so we can find it in the loadables list to append the FDT, and so we don't try to append the FDT to ATF (which could clobber adjacent firmware). So add the minimal code necessary to distinguish U-Boot/non-U-Boot loadables with CONFIG_SPL_FIT_IMAGE_TINY=y. This adds about 300 bytes, much less than the 7400 bytes added by CONFIG_SPL_FIT_IMAGE_TINY=n. Acked-by: Patrick Wildt Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- common/spl/Kconfig | 4 +--- common/spl/spl_fit.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 620ea1e8b439..8d8126d08ebb 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -465,9 +465,7 @@ config SPL_FIT_IMAGE_TINY Enable this to reduce the size of the FIT image loading code in SPL, if space for the SPL binary is very tight. - This removes the detection of image types (which forces the - first image to be treated as having a U-Boot style calling - convention) and skips the recording of each loaded payload + This skips the recording of each loaded payload (i.e. loadable) into the FDT (modifying the loaded FDT to ensure this information is available to the next image invoked). diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 0e27ad1d6a55..49aa2449fe2f 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -469,7 +469,22 @@ static int spl_fit_record_loadable(const void *fit, int images, int index, static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os) { #if CONFIG_IS_ENABLED(FIT_IMAGE_TINY) && !defined(CONFIG_SPL_OS_BOOT) - return -ENOTSUPP; + const char *name = fdt_getprop(fit, noffset, FIT_OS_PROP, NULL); + + if (!name) + return -ENOENT; + + /* + * We don't care what the type of the image actually is, + * only whether or not it is U-Boot. This saves some + * space by omitting the large table of OS types. + */ + if (!strcmp(name, "u-boot")) + *os = IH_OS_U_BOOT; + else + *os = IH_OS_INVALID; + + return 0; #else return fit_image_get_os(fit, noffset, os); #endif From 68158d59d296607849a8af6db3fe16a246934fe7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:14 -0500 Subject: [PATCH 25/29] sunxi: binman: Update FIT component descriptions Since commit d879616e9e64 ("spl: fit: simplify logic for FDT loading for non-OS boots"), the SPL looks at the "os" properties of FIT images to determine where to append the FDT. The "os" property of the "firmware" image also determines how to execute the next stage of the boot process, as in 1d3790905d9c ("spl: atf: introduce spl_invoke_atf and make bl31_entry private"). For this reason, the next stage must be specified in "firmware", not in "loadables". To support this additional functionality, and to properly model the boot process, where ATF runs before U-Boot, add the "os" properties and swap the firmware/loadable images in the FIT image. Since this description was copied as an example in commit 70248d6a2916 ("binman: Support generating FITs with multiple dtbs"), update those examples as well for correctness and consistency. Acked-by: Patrick Wildt Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- arch/arm/dts/sunxi-u-boot.dtsi | 6 ++++-- tools/binman/README.entries | 4 ++-- tools/binman/etype/fit.py | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index b8a0d4b6b08e..9f5b103cbb86 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -35,6 +35,7 @@ uboot { description = "U-Boot (64-bit)"; type = "standalone"; + os = "u-boot"; arch = "arm64"; compression = "none"; load = <0x4a000000>; @@ -46,6 +47,7 @@ atf { description = "ARM Trusted Firmware"; type = "firmware"; + os = "arm-trusted-firmware"; arch = "arm64"; compression = "none"; load = ; @@ -69,8 +71,8 @@ @config-SEQ { description = "NAME"; - firmware = "uboot"; - loadables = "atf"; + firmware = "atf"; + loadables = "uboot"; fdt = "fdt-SEQ"; }; }; diff --git a/tools/binman/README.entries b/tools/binman/README.entries index c1d436563e89..bdb4fd6ee51c 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -385,8 +385,8 @@ You can create config nodes in a similar way: default = "@config-DEFAULT-SEQ"; @config-SEQ { description = "NAME"; - firmware = "uboot"; - loadables = "atf"; + firmware = "atf"; + loadables = "uboot"; fdt = "fdt-SEQ"; }; }; diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 8a24f12aa599..1a7cbd7cec7a 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -73,8 +73,8 @@ class Entry_fit(Entry): default = "@config-DEFAULT-SEQ"; @config-SEQ { description = "NAME"; - firmware = "uboot"; - loadables = "atf"; + firmware = "atf"; + loadables = "uboot"; fdt = "fdt-SEQ"; }; }; From 18bd45592cbd87a766450d08169ec574853b50fa Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:15 -0500 Subject: [PATCH 26/29] binman: Add support for SCP firmware Add an entry type for a firmware blob for a System Control Processor, given by an entry arg. This firmware is a raw binary blob. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- Makefile | 1 + tools/binman/etype/scp.py | 19 +++++++++++++++++++ tools/binman/ftest.py | 7 +++++++ tools/binman/test/172_scp.dts | 16 ++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 tools/binman/etype/scp.py create mode 100644 tools/binman/test/172_scp.dts diff --git a/Makefile b/Makefile index 2b2167573f6b..1502a74d2a87 100644 --- a/Makefile +++ b/Makefile @@ -1331,6 +1331,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \ -a atf-bl31-path=${BL31} \ -a default-dt=$(default_dt) \ + -a scp-path=$(SCP) \ $(BINMAN_$(@F)) OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex diff --git a/tools/binman/etype/scp.py b/tools/binman/etype/scp.py new file mode 100644 index 000000000000..93f8787d2d7c --- /dev/null +++ b/tools/binman/etype/scp.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Samuel Holland +# +# Entry-type module for System Control Processor (SCP) firmware blob +# + +from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg + +class Entry_scp(Entry_blob_named_by_arg): + """Entry containing a System Control Processor (SCP) firmware blob + + Properties / Entry arguments: + - scp-path: Filename of file to read into the entry, typically scp.bin + + This entry holds firmware for an external platform-specific coprocessor. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'scp') + self.external = True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index b771b9d5df71..75f6ca3a8953 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -75,6 +75,7 @@ FSP_S_DATA = b'fsp_s' FSP_T_DATA = b'fsp_t' ATF_BL31_DATA = b'bl31' +SCP_DATA = b'scp' TEST_FDT1_DATA = b'fdt1' TEST_FDT2_DATA = b'test-fdt2' ENV_DATA = b'var1=1\nvar2="2"' @@ -175,6 +176,7 @@ def setUpClass(cls): TestFunctional._MakeInputFile('compress', COMPRESS_DATA) TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) + TestFunctional._MakeInputFile('scp.bin', SCP_DATA) # Add a few .dtb files for testing TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, @@ -3578,6 +3580,11 @@ def testPackBl31(self): data = self._DoReadFile('169_atf_bl31.dts') self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)]) + def testPackScp(self): + """Test that an image with an SCP binary can be created""" + data = self._DoReadFile('172_scp.dts') + self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) + def testFitFdt(self): """Test an image with an FIT with multiple FDT images""" def _CheckFdt(seq, expected_data): diff --git a/tools/binman/test/172_scp.dts b/tools/binman/test/172_scp.dts new file mode 100644 index 000000000000..354e4ef17dfd --- /dev/null +++ b/tools/binman/test/172_scp.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + scp { + filename = "scp.bin"; + }; + }; +}; From e72a6be4fc071930016903638e1e493ab5d3be8a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 21 Oct 2020 21:12:16 -0500 Subject: [PATCH 27/29] sunxi: binman: Add support for including SCP firmware Allwinner sun50i SoCs contain an OpenRISC 1000 CPU that functions as a System Control Processor, or SCP. ARM Trusted Firmware (ATF) communicates with the SCP over SCPI to implement the PSCI system suspend, shutdown and reset functionality. Currently, SCP firmware is optional; the system will boot and run without it, but system suspend will be unavailable. Since all communication with the SCP is mediated by ATF, the only thing U-Boot needs to do is load the firmware into SRAM. The SCP firmware occupies the last 16KiB of SRAM A2, immediately following ATF. Reviewed-by: Simon Glass Signed-off-by: Samuel Holland Reviewed-by: Jagan Teki --- arch/arm/dts/sunxi-u-boot.dtsi | 17 +++++++++++- board/sunxi/README.sunxi64 | 47 +++++++++++++++++++++++++++++----- tools/binman/missing-blob-help | 4 +++ 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index 9f5b103cbb86..c77cf7cacf0c 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -2,8 +2,10 @@ #ifdef CONFIG_MACH_SUN50I_H6 #define BL31_ADDR 0x104000 +#define SCP_ADDR 0x114000 #else #define BL31_ADDR 0x44000 +#define SCP_ADDR 0x50000 #endif / { @@ -59,6 +61,19 @@ }; }; + scp { + description = "SCP firmware"; + type = "firmware"; + arch = "or1k"; + compression = "none"; + load = ; + + scp { + filename = "scp.bin"; + missing-msg = "scp-sunxi"; + }; + }; + @fdt-SEQ { description = "NAME"; type = "flat_dt"; @@ -72,7 +87,7 @@ @config-SEQ { description = "NAME"; firmware = "atf"; - loadables = "uboot"; + loadables = "scp", "uboot"; fdt = "fdt-SEQ"; }; }; diff --git a/board/sunxi/README.sunxi64 b/board/sunxi/README.sunxi64 index 258921af22de..4803bc9ff2f7 100644 --- a/board/sunxi/README.sunxi64 +++ b/board/sunxi/README.sunxi64 @@ -14,8 +14,12 @@ Quick Start / Overview - Build the ARM Trusted Firmware binary (see "ARM Trusted Firmware (ATF)" below) $ cd /src/arm-trusted-firmware $ make PLAT=sun50i_a64 DEBUG=1 bl31 +- Build the SCP firmware binary (see "SCP firmware (Crust)" below) + $ cd /src/crust + $ make pine64_plus_defconfig && make -j5 scp - Build U-Boot (see "SPL/U-Boot" below) $ export BL31=/path/to/bl31.bin + $ export SCP=/src/crust/build/scp/scp.bin $ make pine64_plus_defconfig && make -j5 - Transfer to an uSD card (see "microSD card" below) $ dd if=u-boot-sunxi-with-spl.bin of=/dev/sdx bs=8k seek=1 @@ -24,13 +28,17 @@ Quick Start / Overview Building the firmware ===================== -The Allwinner A64/H5 firmware consists of three parts: U-Boot's SPL, an -ARM Trusted Firmware (ATF) build and the U-Boot proper. -The SPL will load both ATF and U-Boot proper along with the right device -tree blob (.dtb) and will pass execution to ATF (in EL3), which in turn will -drop into the U-Boot proper (in EL2). -As the ATF binary will become part of the U-Boot image file, you will need -to build it first. +The Allwinner A64/H5/H6 firmware consists of several parts: U-Boot's SPL, +ARM Trusted Firmware (ATF), optional System Control Processor (SCP) firmware +(e.g. Crust), and the U-Boot proper. + +The SPL will load all of the other firmware binaries into RAM, along with the +right device tree blob (.dtb), and will pass execution to ATF (in EL3). If SCP +firmware was loaded, ATF will power on the SCP and wait for it to boot. +ATF will then drop into U-Boot proper (in EL2). + +As the ATF binary and SCP firmware will become part of the U-Boot image file, +you will need to build them first. ARM Trusted Firmware (ATF) ---------------------------- @@ -53,6 +61,31 @@ As sometimes the ATF build process is a bit picky about the toolchain used, or if you can't be bothered with building ATF, there are known working binaries in the firmware repository[3], purely for convenience reasons. + SCP firmware (Crust) +---------------------- +SCP firmware is responsible for implementing system suspend/resume, and (on +boards without a PMIC) soft poweroff/on. ATF contains fallback code for CPU +power control, so SCP firmware is optional if you don't need either of these +features. It runs on the AR100, with is an or1k CPU, not ARM, so it needs a +different cross toolchain. + +There is one SCP firmware implementation currently available, Crust: +$ git clone https://github.com/crust-firmware/crust +$ cd crust +$ export CROSS_COMPILE=or1k-linux-musl- +$ make pine64_plus_defconfig +$ make scp + +The same configuration generally works on any board with the same SoC (A64, H5, +or H6), so if there is no config for your board, use one for a similar board. + +Like for ATF, U-Boot finds the SCP firmware binary via an environment variable: +$ export SCP=/src/crust/build/scp/scp.bin + +If you do not want to use SCP firmware, you can silence the warning from binman +by pointing it to an empty file: +$ export SCP=/dev/null + SPL/U-Boot ------------ Both U-Boot proper and the SPL are using the 64-bit mode. As the boot ROM diff --git a/tools/binman/missing-blob-help b/tools/binman/missing-blob-help index 7cf1c346101d..f7bc80ea8309 100644 --- a/tools/binman/missing-blob-help +++ b/tools/binman/missing-blob-help @@ -13,3 +13,7 @@ Firmware and build with BL31=/path/to/bl31.bin atf-bl31-sunxi: Please read the section on ARM Trusted Firmware (ATF) in board/sunxi/README.sunxi64 + +scp-sunxi: +SCP firmware is required for system suspend, but is otherwise optional. +Please read the section on SCP firmware in board/sunxi/README.sunxi64 From 22885f4ed9f5a67564aa8f0401669a9601051251 Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Sun, 9 Aug 2020 11:48:51 +0200 Subject: [PATCH 28/29] sunxi: Enable ethernet on newer Olimex OLinuXino-A20-Lime2-eMMC Olimex OLinuXino LIME2 rev. H through L uses Micrel KSZ9031 PHY. This enables the Micrel PHY for A20-OLinuXino-Lime2-eMMC_defconfig. Signed-off-by: Jonas Smedegaard Reviewed-by: Jagan Teki --- configs/A20-OLinuXino-Lime2-eMMC_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/A20-OLinuXino-Lime2-eMMC_defconfig b/configs/A20-OLinuXino-Lime2-eMMC_defconfig index b1a38d7a4dc3..f9f8f14151e6 100644 --- a/configs/A20-OLinuXino-Lime2-eMMC_defconfig +++ b/configs/A20-OLinuXino-Lime2-eMMC_defconfig @@ -19,6 +19,8 @@ CONFIG_CMD_USB_MASS_STORAGE=y CONFIG_SCSI_AHCI=y CONFIG_DFU_RAM=y CONFIG_FASTBOOT_CMD_OEM_FORMAT=y +CONFIG_PHY_MICREL=y +CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_PHY_REALTEK=y CONFIG_ETH_DESIGNWARE=y CONFIG_RGMII=y From 194923246c199bc6a4baa2ffcda1e08677b6f07c Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Thu, 6 Aug 2020 01:09:12 +0200 Subject: [PATCH 29/29] sun50i: a64: A64-Teres-I board detect builtin keyboard A64-Teres-I board is a laptop which comes with a builtin keyboard. The keyboard+trackpad controller pauses for 2 seconds at a firmware prompt before loading its HID interface. U-Boot needs to wait equally long to reliably enable the keyboard. Signed-off-by: Jonas Smedegaard Reviewed-by: Tom Rini Series-Cc: Jagan Teki Series-Cc: Lukasz Majewski Series-Cc: Andre Przywara --- configs/teres_i_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/teres_i_defconfig b/configs/teres_i_defconfig index 1eba20e5f5d0..ee07f86f6402 100644 --- a/configs/teres_i_defconfig +++ b/configs/teres_i_defconfig @@ -7,6 +7,7 @@ CONFIG_DRAM_ZQ=3881949 CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_USB1_VBUS_PIN="PL7" CONFIG_I2C0_ENABLE=y +CONFIG_PREBOOT="setenv usb_pgood_delay 2000; usb start" CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-teres-i" CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y