[PATCH v1 06/12] net: airoha-eth: add EN7528 Ethernet support
AK Sharma <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
Extend the Airoha GDM Ethernet driver to support the EN7528: the gigabit switch (mt7530) ports, the GDMA/QDMA rings and the MAC init. Signed-off-by: AK Sharma <[email protected]> --- drivers/net/Kconfig | 8 +- drivers/net/airoha_eth.c | 850 ++++++++++++++++++++++++++++++++++++--- drivers/phy/Kconfig | 2 +- 3 files changed, 808 insertions(+), 52 deletions(-) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 66661868..7bcc58ad 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -122,13 +122,13 @@ source "drivers/net/airoha/Kconfig" config AIROHA_ETH bool "Airoha Ethernet QDMA Driver" - depends on ARCH_AIROHA + depends on ARCH_AIROHA || ARCH_EN75XX select MISC select PHYLIB select DEVRES - select DM_ETH_PHY - select DM_RESET - select MDIO_MT7531_MMIO + select DM_ETH_PHY if ARCH_AIROHA + select DM_RESET if ARCH_AIROHA + select MDIO_MT7531_MMIO if ARCH_AIROHA help This Driver support Airoha Ethernet QDMA Driver Say Y to enable support for the Airoha Ethernet QDMA. diff --git a/drivers/net/airoha_eth.c b/drivers/net/airoha_eth.c index e5d39b95..640e8bb2 100644 --- a/drivers/net/airoha_eth.c +++ b/drivers/net/airoha_eth.c @@ -4,6 +4,7 @@ * and simplified for U-Boot usage for single TX/RX ring. * * Copyright (c) 2024 AIROHA Inc + * EN751221 support based on econet_eth by Caleb James DeLisle. * Author: Lorenzo Bianconi <[email protected]> * Christian Marangi <[email protected]> */ @@ -13,6 +14,7 @@ #include <dm/devres.h> #include <dm/lists.h> #include <mapmem.h> +#include <malloc.h> #include <miiphy.h> #include <net.h> #include <regmap.h> @@ -24,16 +26,27 @@ #include <linux/ethtool.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/mii.h> #include <linux/time.h> +#if IS_ENABLED(CONFIG_ARCH_EN75XX) +#include <asm/addrspace.h> /* CKSEG1ADDR: uncached DMA-ring alias */ +#endif +#if IS_ENABLED(CONFIG_ARCH_AIROHA) #include <asm/arch/scu-regmap.h> +#endif #include "airoha/pcs-airoha.h" #define AIROHA_MAX_NUM_GDM_PORTS 4 #define AIROHA_MAX_NUM_QDMA 1 -#define AIROHA_MAX_NUM_RSTS 3 +#define AIROHA_MAX_NUM_RSTS 4 #define AIROHA_MAX_NUM_XSI_RSTS 4 +#define AIROHA_MAX_NUM_SWITCH_PORT 4 +#define AIROHA_MAX_PBUS_TRY 10 +#define AIROHA_PBUS_SLEEP 100 +#define AIROHA_PBUS_C22_MASK 0x800000 + #define AIROHA_MAX_PACKET_SIZE 2048 #define AIROHA_NUM_TX_RING 1 #define AIROHA_NUM_RX_RING 1 @@ -53,6 +66,8 @@ #define SWITCH_BC_FFP GENMASK(31, 24) #define SWITCH_UNM_FFP GENMASK(23, 16) #define SWITCH_UNU_FFP GENMASK(15, 8) +#define SWITCH_CPU_EN BIT(7) +#define SWITCH_CPU_PORT GENMASK(6, 4) #define SWITCH_PMCR(_n) 0x3000 + ((_n) * 0x100) #define SWITCH_IPG_CFG GENMASK(19, 18) #define SWITCH_IPG_CFG_NORMAL FIELD_PREP(SWITCH_IPG_CFG, 0x0) @@ -86,6 +101,19 @@ #define SWITCH_PHY_PRE_EN BIT(15) #define SWITCH_PHY_END_ADDR GENMASK(12, 8) #define SWITCH_PHY_ST_ADDR GENMASK(4, 0) +#define SWITCH_GEPHY_CONN_CFG 0x7c14 +#define SWITCH_DPHY_CKIN_SEL BIT(31) +#define SWITCH_PHY_CORE_REG_CLK_SEL BIT(30) +#define SWITCH_ETHER_AFE_PWD GENMASK(28, 24) +#define SWITCH_PBUS_PHY_IAC 0x7c20 +#define SWITCH_PBUS_PHY_START BIT(31) +#define SWITCH_PBUS_PHY_CMD BIT(30) +#define SWITCH_PBUS_PHY_CMD_READ FIELD_PREP(SWITCH_PBUS_PHY_CMD, 0x0) +#define SWITCH_PBUS_PHY_CMD_WRITE FIELD_PREP(SWITCH_PBUS_PHY_CMD, 0x1) +#define SWITCH_PBUS_PHY_PORTADDR GENMASK(28, 24) +#define SWITCH_PBUS_PHY_REGADDR GENMASK(23, 0) +#define SWITCH_PBUS_PHY_IAWD 0x7c24 +#define SWITCH_PBUS_PHY_IARD 0x7c28 /* FE */ #define PSE_BASE 0x0100 @@ -106,6 +134,9 @@ (_n) == 2 ? GDM2_BASE : GDM1_BASE) #define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) +#define REG_GDM_MAC_LSB(_n) (GDM_BASE(_n) + 0x08) +#define REG_GDM_MAC_MSB(_n) (GDM_BASE(_n) + 0x0c) +#define REG_GDM_RX_LEN_THR(_n) (GDM_BASE(_n) + 0x14) #define GDM_PAD_EN BIT(28) #define GDM_DROP_CRC_ERR BIT(23) #define GDM_IP4_CKSUM BIT(22) @@ -116,6 +147,74 @@ #define GDM_MCFQ_MASK GENMASK(7, 4) #define GDM_OCFQ_MASK GENMASK(3, 0) +/* EN7512/EN7521 GDM forwarding configuration */ +#define EN751221_GDM_DROP_OVERSIZE BIT(25) /* matches en7528 gdm_regs.h */ +#define EN751221_GDM_MYMAC_FPORT GENMASK(15, 12) +#define EN751221_GDM_BCAST_FPORT GENMASK(11, 8) +#define EN751221_GDM_MCAST_FPORT GENMASK(7, 4) +#define EN751221_GDM_DEFAULT_FPORT GENMASK(3, 0) +#define EN751221_GDM_OVERSIZE_LEN GENMASK(31, 16) +#define EN751221_GDM_RUNT_LEN GENMASK(15, 0) + +/* EN7512/EN7521 QDMA register layout */ +#define EN751221_REG_TX_RING_BASE 0x0008 +#define EN751221_REG_RX_RING_BASE 0x000c +#define EN751221_REG_TX_CPU_IDX 0x0010 +#define EN751221_REG_TX_DMA_IDX 0x0014 +#define EN751221_REG_RX_CPU_IDX 0x0018 +#define EN751221_REG_RX_DMA_IDX 0x001c +#define EN751221_REG_FWD_DSCP_BASE 0x0020 +#define EN751221_REG_FWD_BUF_BASE 0x0024 +#define EN751221_REG_FWD_DSCP_CFG 0x0028 +#define EN751221_REG_LMGR_INIT_CFG 0x0030 +#define EN751221_REG_INT_STATUS 0x0050 +#define EN751221_REG_INT_ENABLE 0x0054 +#define EN751221_REG_RX_DELAY_INT 0x005c +#define EN751221_REG_IRQ_BASE 0x0060 +#define EN751221_REG_IRQ_CFG 0x0064 +#define EN751221_REG_IRQ_CLEAR_LEN 0x0068 +#define EN751221_REG_IRQ_STATUS 0x006c +#define EN751221_REG_RX_RING_SIZE 0x0100 +#define EN751221_REG_RX_RING_THR 0x0104 + +#define EN751221_RING_IDX_MASK GENMASK(11, 0) +#define EN751221_IRQ_DEPTH_MASK GENMASK(11, 0) +#define EN751221_IRQ_THRESHOLD_MASK GENMASK(27, 16) +#define EN751221_IRQ_HEAD_IDX_MASK GENMASK(11, 0) +#define EN751221_IRQ_ENTRY_LEN_MASK GENMASK(27, 16) +#define EN751221_IRQ_CLEAR_LEN_MASK GENMASK(7, 0) +#define EN751221_HWFWD_LOW_THR_MASK GENMASK(12, 0) +#define EN751221_HWFWD_DESC_NUM_MASK GENMASK(15, 0) +#define EN751221_HWFWD_OVERHEAD_MASK GENMASK(23, 16) +#define EN751221_HWFWD_OVERHEAD_EN BIT(24) /* en7528: stock sets this */ +#define EN751221_GLOBAL_CFG_MSG_WORD_SWAP BIT(28) + +#define EN751221_TXMSG_CHANNEL_MASK GENMASK(10, 3) +#define EN751221_TXMSG_QUEUE_MASK GENMASK(2, 0) +#define EN751221_TXMSG_FPORT_MASK GENMASK(21, 19) +#define EN751221_FPORT_GDM1 1 +#define EN751221_HW_DSCP_NUM 256 /* en7528 stock LMGR pool (was 4) */ +#define EN751221_IRQ_QUEUE_LEN 32 +#define EN751221_IRQ_THRESHOLD 4 +/* + * SCU reset banks (base 0x1fb00000): kernel clk-en7523.c en751221_rst_ofs[] = + * { bank0 = REG_RST_CTRL2 = 0x830, bank1 = REG_RST_CTRL1 = 0x834 }, and + * en751221_rst_map[] places the frame-engine domains as (RST_NR_PER_BANK=32): + * FE_QDMA1 "qdma0" = 32+1 -> bank1 bit1 FE_QDMA2 "qdma1" = 32+2 -> bank1 bit2 + * FE_RST "fe" = 32+21-> bank1 bit21 XPON_MAC = 32+31 -> bank1 bit31 + * XPON_PHY "xpon-phy" = 0 -> bank0 bit0 + * The stock econet-eth eth node resets ALL FIVE as one array pulse. + */ +#define EN751221_RESET_CONTROL_B1 0xbfb00834 /* SCU reset bank1 (REG_RST_CTRL1) */ +#define EN751221_RESET_CONTROL_B0 0xbfb00830 /* SCU reset bank0 (REG_RST_CTRL2) */ +#define EN751221_RESET_CONTROL2 EN751221_RESET_CONTROL_B1 /* back-compat alias */ +#define EN751221_FE_SRAM_SEL 0xbfb00958 +#define EN751221_RST_QDMA0 BIT(1) /* bank1: FE_QDMA1 "qdma0" */ +#define EN751221_RST_QDMA1 BIT(2) /* bank1: FE_QDMA2 "qdma1" (was missing) */ +#define EN751221_RST_FE BIT(21) /* bank1: FE_RST */ +#define EN751221_RST_XPON_MAC BIT(31) /* bank1: XPON_MAC (was missing) */ +#define EN751221_RST_XPON_PHY BIT(0) /* bank0: XPON_PHY (was missing) */ + /* QDMA */ #define REG_QDMA_GLOBAL_CFG 0x0004 #define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) @@ -290,11 +389,24 @@ struct airoha_qdma_fwd_desc { __le32 rsv1; }; +struct en751221_qdma_fwd_desc { + u32 addr; + u32 info; + u32 msg0; + u32 msg1; +}; + struct airoha_queue { struct airoha_qdma_desc *desc; u16 head; int ndesc; + + /* Uncached TX bounce buffer: the same broken-flush issue means a CPU- + * built packet in cached memory is not guaranteed to be in DRAM when the + * QDMA reads it, so copy each TX frame through this uncached buffer. */ + void *bounce; + unsigned long bounce_phys; }; struct airoha_tx_irq_queue { @@ -347,6 +459,7 @@ struct airoha_eth { struct airoha_eth_soc_data { u32 version; + bool econet; int num_xsi_rsts; const char * const *xsi_rsts_names; const char *switch_compatible; @@ -365,6 +478,12 @@ static const char * const en7581_xsi_rsts_names[] = { "xfp-mac", }; +static const char * const an7583_xsi_rsts_names[] = { + "hsi0-mac", + "hsi1-mac", + "xfp-mac", +}; + static u32 airoha_rr(void __iomem *base, u32 offset) { return readl(base + offset); @@ -405,8 +524,12 @@ static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) #define airoha_qdma_clear(qdma, offset, val) \ airoha_rmw((qdma)->regs, (offset), (val), 0) +#define airoha_switch_rr(eth, offset) \ + airoha_rr((eth)->switch_regs, (offset)) #define airoha_switch_wr(eth, offset, val) \ airoha_wr((eth)->switch_regs, (offset), (val)) +#define airoha_switch_rmw(eth, offset, mask, val) \ + airoha_rmw((eth)->switch_regs, (offset), (mask), (val)) static inline dma_addr_t dma_map_unaligned(void *vaddr, size_t len, enum dma_data_direction dir) @@ -429,12 +552,41 @@ static inline void dma_unmap_unaligned(dma_addr_t addr, size_t len, dma_unmap_single(start, end - start, dir); } +static void *airoha_dma_alloc_coherent(size_t size, unsigned long *dma_addr) +{ +#if IS_ENABLED(CONFIG_ARCH_EN75XX) + void *buf; + + size = ALIGN(size, ARCH_DMA_MINALIGN); + buf = memalign(ARCH_DMA_MINALIGN, size); + if (!buf) + return NULL; + + *dma_addr = virt_to_phys(buf); + /* + * EN7528 CM L2 has no Hit_Writeback_Inv_SD (op 0x17 hangs). DMA-shared + * rings must therefore live in KSEG1 so CPU stores reach DRAM without + * an L2 writeback. QDMA then sees descriptors by construction. + */ + return (void *)CKSEG1ADDR((unsigned long)buf); +#else + return dma_alloc_coherent(size, dma_addr); +#endif +} + +static bool airoha_is_en751221(struct airoha_eth *eth) +{ + return eth->soc->econet; +} + static int airoha_get_fe_port(struct airoha_gdm_port *port) { struct airoha_qdma *qdma = port->qdma; struct airoha_eth *eth = qdma->eth; switch (eth->soc->version) { + case 0x7512: + return EN751221_FPORT_GDM1; case 0x7523: /* FIXME: GDM1 is the only supported port */ return FE_PSE_PORT_GDM1; @@ -446,6 +598,32 @@ static int airoha_get_fe_port(struct airoha_gdm_port *port) static void airoha_fe_maccr_init(struct airoha_gdm_port *port) { + if (airoha_is_en751221(port->qdma->eth)) { + u32 fwd_cfg, len_thr; + + /* + * QDMA0 CPU is fport zero: received frames go to the CPU. The + * stock en7528 econet-eth driver sets exactly this (all fport + * classes = CPU) plus drop_oversize. (BIT25 DROP_OVERSIZE is the + * correct en7528 bit per gdm_regs.h — the earlier bits[31:30] + * "forward-enable" guess was wrong: those are VIP/L2LU bits.) + */ + fwd_cfg = EN751221_GDM_DROP_OVERSIZE | + FIELD_PREP(EN751221_GDM_MYMAC_FPORT, 0) | + FIELD_PREP(EN751221_GDM_BCAST_FPORT, 0) | + FIELD_PREP(EN751221_GDM_MCAST_FPORT, 0) | + FIELD_PREP(EN751221_GDM_DEFAULT_FPORT, 0); + airoha_fe_wr(port->qdma->eth, REG_GDM_FWD_CFG(port->id), + fwd_cfg); + + len_thr = FIELD_PREP(EN751221_GDM_OVERSIZE_LEN, + PKTSIZE_ALIGN) | + FIELD_PREP(EN751221_GDM_RUNT_LEN, 60); + airoha_fe_wr(port->qdma->eth, REG_GDM_RX_LEN_THR(port->id), + len_thr); + return; + } + /* * Disable any kind of CRC drop or offload. * Enable padding of short TX packets to 60 bytes. @@ -460,6 +638,214 @@ static int airoha_fe_init(struct airoha_gdm_port *port) return 0; } +static void en751221_qdma_reset_rx_desc(struct airoha_queue *q, int index) +{ + struct airoha_qdma_desc *desc = &q->desc[index]; + uchar *rx_packet = net_rx_packets[index]; + u32 next; + + dma_map_single(rx_packet, PKTSIZE_ALIGN, DMA_FROM_DEVICE); + next = (index + 1) % q->ndesc; + + WRITE_ONCE(desc->rsv, 0); + WRITE_ONCE(desc->ctrl, + FIELD_PREP(QDMA_DESC_LEN_MASK, PKTSIZE_ALIGN)); + WRITE_ONCE(desc->addr, virt_to_phys(rx_packet)); + WRITE_ONCE(desc->data, next); + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + WRITE_ONCE(desc->msg2, 0); + WRITE_ONCE(desc->msg3, 0); + + dma_map_unaligned(desc, sizeof(*desc), DMA_TO_DEVICE); +} + +static void en751221_qdma_init_rx_desc(struct airoha_qdma *qdma) +{ + struct airoha_queue *q = &qdma->q_rx[0]; + int i; + + for (i = 0; i < q->ndesc; i++) + en751221_qdma_reset_rx_desc(q, i); + + /* On EN751221, equal CPU/HW indices mean that the RX ring is full. */ + q->head = 0; + airoha_qdma_wr(qdma, EN751221_REG_RX_CPU_IDX, 0); + airoha_qdma_wr(qdma, EN751221_REG_RX_DMA_IDX, 1); +} + +static int en751221_qdma_init_rx(struct airoha_qdma *qdma) +{ + struct airoha_queue *q = &qdma->q_rx[0]; + unsigned long dma_addr; + + q->ndesc = RX_DSCP_NUM; + q->desc = airoha_dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + if (!q->desc) + return -ENOMEM; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + airoha_qdma_wr(qdma, EN751221_REG_RX_RING_BASE, dma_addr); + airoha_qdma_wr(qdma, EN751221_REG_RX_RING_SIZE, q->ndesc); + airoha_qdma_wr(qdma, EN751221_REG_RX_RING_THR, 0); + + en751221_qdma_init_rx_desc(qdma); + return 0; +} + +static int en751221_qdma_init_tx(struct airoha_qdma *qdma) +{ + struct airoha_queue *q = &qdma->q_tx[0]; + unsigned long dma_addr; + + q->ndesc = TX_DSCP_NUM; + q->head = 0; + q->desc = airoha_dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + if (!q->desc) + return -ENOMEM; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + airoha_qdma_wr(qdma, EN751221_REG_TX_RING_BASE, dma_addr); + airoha_qdma_wr(qdma, EN751221_REG_TX_CPU_IDX, 0); + airoha_qdma_wr(qdma, EN751221_REG_TX_DMA_IDX, 0); + + q->bounce = airoha_dma_alloc_coherent(AIROHA_MAX_PACKET_SIZE, + &q->bounce_phys); + if (!q->bounce) + return -ENOMEM; + + return 0; +} + +static int en751221_qdma_init_tx_irq(struct airoha_qdma *qdma) +{ + struct airoha_tx_irq_queue *irq_q = &qdma->q_tx_irq[0]; + unsigned long dma_addr; + u32 cfg; + + irq_q->size = EN751221_IRQ_QUEUE_LEN; + irq_q->q = airoha_dma_alloc_coherent(irq_q->size * sizeof(u32), + &dma_addr); + if (!irq_q->q) + return -ENOMEM; + + irq_q->qdma = qdma; + memset(irq_q->q, 0xff, irq_q->size * sizeof(u32)); + dma_map_single(irq_q->q, irq_q->size * sizeof(u32), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, EN751221_REG_IRQ_BASE, dma_addr); + cfg = FIELD_PREP(EN751221_IRQ_THRESHOLD_MASK, + EN751221_IRQ_THRESHOLD) | + FIELD_PREP(EN751221_IRQ_DEPTH_MASK, irq_q->size); + airoha_qdma_wr(qdma, EN751221_REG_IRQ_CFG, cfg); + + return 0; +} + +static void en751221_qdma_reset_tx(struct airoha_qdma *qdma) +{ + struct airoha_queue *q = &qdma->q_tx[0]; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + dma_map_single(q->desc, q->ndesc * sizeof(*q->desc), DMA_TO_DEVICE); + q->head = 0; + airoha_qdma_wr(qdma, EN751221_REG_TX_CPU_IDX, 0); + airoha_qdma_wr(qdma, EN751221_REG_TX_DMA_IDX, 0); +} + +static int en751221_qdma_init_hfwd(struct airoha_qdma *qdma) +{ + unsigned long dma_addr; + u32 status, val; + int size; + + size = EN751221_HW_DSCP_NUM * sizeof(struct en751221_qdma_fwd_desc); + qdma->hfwd.desc = airoha_dma_alloc_coherent(size, &dma_addr); + if (!qdma->hfwd.desc) + return -ENOMEM; + memset(qdma->hfwd.desc, 0, size); + dma_map_single(qdma->hfwd.desc, size, DMA_TO_DEVICE); + airoha_qdma_wr(qdma, EN751221_REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * EN751221_HW_DSCP_NUM; + qdma->hfwd.q = airoha_dma_alloc_coherent(size, &dma_addr); + if (!qdma->hfwd.q) + return -ENOMEM; + memset(qdma->hfwd.q, 0, size); + dma_map_single(qdma->hfwd.q, size, DMA_TO_DEVICE); + airoha_qdma_wr(qdma, EN751221_REG_FWD_BUF_BASE, dma_addr); + + /* en7528 stock: low_thr = 32 (hwf_cfg = 0x20), not 1. */ + val = FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0) | + FIELD_PREP(EN751221_HWFWD_LOW_THR_MASK, 32); + airoha_qdma_wr(qdma, EN751221_REG_FWD_DSCP_CFG, val); + + /* + * en7528 stock LMGR init = 0x01140100 (OVERHEAD_EN + overhead 0x14 + + * 256 descriptors). The borrowed value omitted OVERHEAD_EN and used + * only 4 descriptors, starving the PSE store-and-forward pool so + * CPU-injected frames were dropped after QDMA writeback DONE. + */ + val = LMGR_INIT_START | + EN751221_HWFWD_OVERHEAD_EN | + FIELD_PREP(EN751221_HWFWD_OVERHEAD_MASK, 0x14) | + FIELD_PREP(EN751221_HWFWD_DESC_NUM_MASK, + EN751221_HW_DSCP_NUM); + airoha_qdma_wr(qdma, EN751221_REG_LMGR_INIT_CFG, val); + + return read_poll_timeout(airoha_qdma_rr, status, + !(status & LMGR_INIT_START), USEC_PER_MSEC, + 30 * USEC_PER_MSEC, qdma, + EN751221_REG_LMGR_INIT_CFG); +} + +static int en751221_qdma_init(struct airoha_qdma *qdma) +{ + u32 cfg; + int ret; + + /* Stop the engine before replacing all descriptor rings. */ + airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, 0); + airoha_qdma_wr(qdma, EN751221_REG_INT_ENABLE, 0); + airoha_qdma_wr(qdma, EN751221_REG_INT_STATUS, 0xffffffff); + + ret = en751221_qdma_init_rx(qdma); + if (ret) + return ret; + + ret = en751221_qdma_init_tx(qdma); + if (ret) + return ret; + + ret = en751221_qdma_init_tx_irq(qdma); + if (ret) + return ret; + + ret = en751221_qdma_init_hfwd(qdma); + if (ret) + return ret; + + /* + * EN7528: do NOT set GLOBAL_CFG_DSCP_BYTE_SWAP. The stock en7528 + * econet-eth driver programs qdma_cfg = 0x140800f5 (msg_word_swap + + * payload_byte_swap, dscp_byte_swap = soc->dscp_byte_swap = false). + * The mainline-airoha value this port borrowed set dscp_byte_swap, + * byte-swapping the TX descriptor the QDMA reads (addr/msg/FPORT) so + * the frame was routed nowhere while still reporting DONE. Matching + * the stock value restores CPU->GDM1->switch egress. + */ + cfg = EN751221_GLOBAL_CFG_MSG_WORD_SWAP | + GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | + GLOBAL_CFG_IRQ0_EN_MASK | + GLOBAL_CFG_CHECK_DONE_MASK | + GLOBAL_CFG_TX_WB_DONE_MASK | + FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 3); + airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, cfg); + airoha_qdma_wr(qdma, EN751221_REG_RX_DELAY_INT, 0); + + return 0; +} + static void airoha_qdma_reset_rx_desc(struct airoha_queue *q, int index) { struct airoha_qdma_desc *desc; @@ -501,7 +887,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, q->ndesc = ndesc; q->head = 0; - q->desc = dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + q->desc = airoha_dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); if (!q->desc) return -ENOMEM; @@ -548,7 +934,7 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q, q->ndesc = size; q->head = 0; - q->desc = dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + q->desc = airoha_dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); if (!q->desc) return -ENOMEM; @@ -570,7 +956,7 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, int id = irq_q - &qdma->q_tx_irq[0]; unsigned long dma_addr; - irq_q->q = dma_alloc_coherent(size * sizeof(u32), &dma_addr); + irq_q->q = airoha_dma_alloc_coherent(size * sizeof(u32), &dma_addr); if (!irq_q->q) return -ENOMEM; @@ -615,7 +1001,7 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) int size; size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); - qdma->hfwd.desc = dma_alloc_coherent(size, &dma_addr); + qdma->hfwd.desc = airoha_dma_alloc_coherent(size, &dma_addr); if (!qdma->hfwd.desc) return -ENOMEM; @@ -625,7 +1011,7 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; - qdma->hfwd.q = dma_alloc_coherent(size, &dma_addr); + qdma->hfwd.q = airoha_dma_alloc_coherent(size, &dma_addr); if (!qdma->hfwd.q) return -ENOMEM; @@ -689,6 +1075,8 @@ static int airoha_qdma_init(struct udevice *dev, qdma->regs = dev_remap_addr_name(dev, "qdma0"); if (IS_ERR(qdma->regs)) return PTR_ERR(qdma->regs); + if (airoha_is_en751221(eth)) + return en751221_qdma_init(qdma); err = airoha_qdma_init_rx(qdma); if (err) @@ -735,11 +1123,51 @@ static int airoha_pcs_init(struct udevice *dev) } #endif +static int en751221_hw_init(struct udevice *dev, struct airoha_eth *eth) +{ + u32 val, val2; + int ret; + + /* Expose the frame-engine register window instead of the FE SRAM. */ + __raw_writel(0, (void __iomem *)EN751221_FE_SRAM_SEL); + + /* + * Reset the SAME FIVE frame-engine domains the stock econet-eth DT eth + * node resets as one array pulse: {fe, qdma0=FE_QDMA1, qdma1=FE_QDMA2, + * xpon-mac, xpon-phy}. The earlier port reset only qdma0(bit1)+fe(bit21) + * and left FE_QDMA2 (bank1 bit2) — the second FE-DMA domain that gates + * the QDMA<->PSE store-and-forward datapath shared by both QDMAs — in its + * bootbase state. That back-pressures qdma0's drain: the first CPU-TX + * latches TX_DMA_BUSY (qdma_cfg .f2) but the frame never reaches + * GDM1/egress and tx_hwi never advances. Assert all five, hold, deassert. + */ + val = __raw_readl((void __iomem *)EN751221_RESET_CONTROL_B1); + val2 = __raw_readl((void __iomem *)EN751221_RESET_CONTROL_B0); + __raw_writel(val | EN751221_RST_QDMA0 | EN751221_RST_QDMA1 | + EN751221_RST_FE | EN751221_RST_XPON_MAC, + (void __iomem *)EN751221_RESET_CONTROL_B1); + __raw_writel(val2 | EN751221_RST_XPON_PHY, + (void __iomem *)EN751221_RESET_CONTROL_B0); + mdelay(20); + __raw_writel(val, (void __iomem *)EN751221_RESET_CONTROL_B1); + __raw_writel(val2, (void __iomem *)EN751221_RESET_CONTROL_B0); + mdelay(20); + + ret = airoha_qdma_init(dev, eth, ð->qdma[0]); + if (ret) + return ret; + + return 0; +} + static int airoha_hw_init(struct udevice *dev, struct airoha_eth *eth) { int ret, i; + if (airoha_is_en751221(eth)) + return en751221_hw_init(dev, eth); + /* disable xsi */ ret = reset_assert_bulk(ð->xsi_rsts); if (ret) @@ -787,6 +1215,86 @@ static int airoha_switch_init(struct udevice *dev, struct airoha_eth *eth) /* Switch doesn't have a DEV, gets address and setup Flood and CPU port */ eth->switch_regs = map_sysmem(addr, 0); + if (data->econet) { + u32 pmcr; + + /* + * Soft-reset the integrated MT7530 switch core to a known state + * before (re)configuring it, mirroring mt7531_setup(): the stock + * bootbase leaves it in its own config. SYS_CTRL @0x7000, bits + * SW_SYS_RST|SW_REG_RST self-clear when the reset completes. + */ + airoha_switch_wr(eth, 0x7000, 0x3); + mdelay(100); + + /* Integrated MT7530: flood unknown traffic to all ports and use P6. */ + airoha_switch_wr(eth, SWITCH_MFC, + SWITCH_BC_FFP | SWITCH_UNM_FFP | SWITCH_UNU_FFP | + SWITCH_CPU_EN | FIELD_PREP(SWITCH_CPU_PORT, 6)); + + pmcr = SWITCH_MAC_MODE | SWITCH_FORCE_MODE | SWITCH_MAC_TX_EN | + SWITCH_MAC_RX_EN | SWITCH_BKOFF_EN | SWITCH_BKPR_EN | + SWITCH_FORCE_SPD_1000 | SWITCH_FORCE_DPX | + SWITCH_FORCE_LNK; + + /* + * P5 is the TRGMII cascade to the external MCM switch while P6 + * is the CPU port. The EN751221 OEM setup uses shrink IPG only + * on the cascade and short IPG on the CPU link. + */ + airoha_switch_wr(eth, SWITCH_PMCR(5), + pmcr | SWITCH_IPG_CFG_SHRINK); + airoha_switch_wr(eth, SWITCH_PMCR(6), + pmcr | SWITCH_IPG_CFG_SHORT); + airoha_switch_wr(eth, SWITCH_PHY_POLL, 0x7f7f8c08); + + /* + * EN7528 LAN ports 1-4 (internal GPHYs at MDIO 9-12): enable the + * MAC but DO NOT force the link. Leaving FORCE_MODE clear lets the + * switch PHY-poll (SWITCH_PHY_POLL, set just above) drive each port + * MAC to the speed/duplex the GPHY actually negotiates with its + * peer. Force-linking these at 1G silently breaks egress to a + * 100M/10M peer: the MAC<->GPHY line rate mismatches, so a CPU-TX + * frame leaves GDM1 (GDM1 TX MIB increments) but never reaches the + * RJ45. Proven on HW with a 100M host — forced 1G => 0 egress, auto + * => ping/traffic works and PMSR reports the real 100M FDX link. + * (Ports 5/6 keep the forced-1G `pmcr` above: those are the fixed + * internal CPU/cascade links, not GPHY line ports.) + */ + { + u32 lan_pmcr = SWITCH_MAC_MODE | SWITCH_MAC_TX_EN | + SWITCH_MAC_RX_EN | SWITCH_BKOFF_EN | + SWITCH_BKPR_EN | SWITCH_IPG_CFG_NORMAL; + int p; + + for (p = 1; p <= 4; p++) + airoha_switch_wr(eth, SWITCH_PMCR(p), lan_pmcr); + } + + /* + * EN7528: the integrated MT7530 needs its per-port forwarding + * matrix programmed, or no frames pass between ports (the LAN + * GPHYs at MDIO 9-12 are already powered and link fine; the + * en7512 econet stub only set MFC/PMCR and never the matrix). + * Give each user port (0-5) a matrix pointing at the CPU port + * (6) and the CPU port a matrix of all user ports, and mark + * every port a VLAN user port. Mirrors mt753x_port_isolation() + * / mt7988_setup(): PCR = 0x2004 + p*0x100 (matrix at bit 16), + * PVC = 0x2010 + p*0x100 (STAG 0x8100, VLAN_ATTR_USER). + */ + { + int p; + + for (p = 0; p < 7; p++) { + airoha_switch_wr(eth, 0x2004 + p * 0x100, + (u32)(p == 6 ? 0x3f : 0x40) << 16); + airoha_switch_wr(eth, 0x2010 + p * 0x100, + 0x81000000); + } + } + + return 0; + } /* Set FLOOD, no CPU switch register */ airoha_switch_wr(eth, SWITCH_MFC, SWITCH_BC_FFP | SWITCH_UNM_FFP | @@ -809,6 +1317,59 @@ static int airoha_switch_init(struct udevice *dev, struct airoha_eth *eth) FIELD_PREP(SWITCH_PHY_END_ADDR, 0xc) | FIELD_PREP(SWITCH_PHY_ST_ADDR, 0x8)); + /* AN7583 require tweak to GEPHY_CONN_CFG and clear PHY BMCR_PDOWN */ + if (!strcmp(data->switch_compatible, "airoha,an7583-switch")) { + int i; + + airoha_switch_rmw(eth, SWITCH_GEPHY_CONN_CFG, + SWITCH_DPHY_CKIN_SEL | + SWITCH_PHY_CORE_REG_CLK_SEL | + SWITCH_ETHER_AFE_PWD, + SWITCH_DPHY_CKIN_SEL | + SWITCH_PHY_CORE_REG_CLK_SEL | + FIELD_PREP(SWITCH_ETHER_AFE_PWD, 0)); + + /* Disable BMCR_PDOWN for every PHY */ + for (i = 0; i < AIROHA_MAX_NUM_SWITCH_PORT; i++) { + int try; + u32 val; + + airoha_switch_wr(eth, SWITCH_PBUS_PHY_IAC, + SWITCH_PBUS_PHY_START | + SWITCH_PBUS_PHY_CMD_READ | + FIELD_PREP(SWITCH_PBUS_PHY_PORTADDR, i) | + FIELD_PREP(SWITCH_PBUS_PHY_REGADDR, + AIROHA_PBUS_C22_MASK | MII_BMCR)); + + for (try = 0; try < AIROHA_MAX_PBUS_TRY; try++) { + val = airoha_switch_rr(eth, SWITCH_PBUS_PHY_IAC); + if (!(val & SWITCH_PBUS_PHY_START)) + break; + + udelay(AIROHA_PBUS_SLEEP); + } + + val = airoha_switch_rr(eth, SWITCH_PBUS_PHY_IARD); + val &= ~BMCR_PDOWN; + + airoha_switch_wr(eth, SWITCH_PBUS_PHY_IAWD, val); + airoha_switch_wr(eth, SWITCH_PBUS_PHY_IAC, + SWITCH_PBUS_PHY_START | + SWITCH_PBUS_PHY_CMD_WRITE | + FIELD_PREP(SWITCH_PBUS_PHY_PORTADDR, i) | + FIELD_PREP(SWITCH_PBUS_PHY_REGADDR, + AIROHA_PBUS_C22_MASK | MII_BMCR)); + + for (try = 0; try < AIROHA_MAX_PBUS_TRY; try++) { + val = airoha_switch_rr(eth, SWITCH_PBUS_PHY_IAC); + if (!(val & SWITCH_PBUS_PHY_START)) + break; + + udelay(AIROHA_PBUS_SLEEP); + } + } + } + return 0; } @@ -828,6 +1389,8 @@ static int airoha_alloc_gdm_port(struct udevice *dev, ofnode node) ret = ofnode_read_u32(node, "reg", &id); if (ret) return ret; + if (eth->soc->econet) + id++; if (id > AIROHA_MAX_NUM_GDM_PORTS) return -EINVAL; @@ -882,56 +1445,65 @@ static int airoha_eth_probe(struct udevice *dev) { struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev); struct airoha_eth *eth = dev_get_priv(dev); - struct regmap *scu_regmap; struct udevice *mdio_dev; ofnode node; int i, ret; - scu_regmap = airoha_get_scu_regmap(); - if (IS_ERR(scu_regmap)) - return PTR_ERR(scu_regmap); + eth->soc = data; + if (!data->econet) { +#if IS_ENABLED(CONFIG_ARCH_AIROHA) + struct regmap *scu_regmap; - /* It seems by default the FEMEM_SEL is set to Memory (0x1) - * preventing any access to any QDMA and FrameEngine register - * reporting all 0xdeadbeef (poor cow :( ) - */ - regmap_write(scu_regmap, SCU_SHARE_FEMEM_SEL, 0x0); + scu_regmap = airoha_get_scu_regmap(); + if (IS_ERR(scu_regmap)) + return PTR_ERR(scu_regmap); - eth->soc = data; + /* FEMEM_SEL=1 hides QDMA/FE behind the shared SRAM window. */ + regmap_write(scu_regmap, SCU_SHARE_FEMEM_SEL, 0x0); +#else + return -EOPNOTSUPP; +#endif + } eth->fe_regs = dev_remap_addr_name(dev, "fe"); if (!eth->fe_regs) return -ENOMEM; - eth->rsts.resets = devm_kcalloc(dev, AIROHA_MAX_NUM_RSTS, - sizeof(struct reset_ctl), GFP_KERNEL); - if (!eth->rsts.resets) - return -ENOMEM; - eth->rsts.count = AIROHA_MAX_NUM_RSTS; + if (!data->econet) { + eth->rsts.resets = devm_kcalloc(dev, AIROHA_MAX_NUM_RSTS, + sizeof(struct reset_ctl), GFP_KERNEL); + if (!eth->rsts.resets) + return -ENOMEM; + eth->rsts.count = AIROHA_MAX_NUM_RSTS; - eth->xsi_rsts.resets = devm_kcalloc(dev, data->num_xsi_rsts, - sizeof(struct reset_ctl), GFP_KERNEL); - if (!eth->xsi_rsts.resets) - return -ENOMEM; - eth->xsi_rsts.count = data->num_xsi_rsts; + eth->xsi_rsts.resets = devm_kcalloc(dev, data->num_xsi_rsts, + sizeof(struct reset_ctl), GFP_KERNEL); + if (!eth->xsi_rsts.resets) + return -ENOMEM; + eth->xsi_rsts.count = data->num_xsi_rsts; - ret = reset_get_by_name(dev, "fe", ð->rsts.resets[0]); - if (ret) - return ret; + ret = reset_get_by_name(dev, "fe", ð->rsts.resets[0]); + if (ret) + return ret; - ret = reset_get_by_name(dev, "pdma", ð->rsts.resets[1]); - if (ret) - return ret; + ret = reset_get_by_name(dev, "pdma", ð->rsts.resets[1]); + if (ret) + return ret; - ret = reset_get_by_name(dev, "qdma", ð->rsts.resets[2]); - if (ret) - return ret; + ret = reset_get_by_name(dev, "qdma", ð->rsts.resets[2]); + if (ret) + return ret; - for (i = 0; i < data->num_xsi_rsts; i++) { - ret = reset_get_by_name(dev, data->xsi_rsts_names[i], - ð->xsi_rsts.resets[i]); + ret = reset_get_by_name(dev, "switch", ð->rsts.resets[3]); if (ret) return ret; + + for (i = 0; i < data->num_xsi_rsts; i++) { + ret = reset_get_by_name(dev, data->xsi_rsts_names[i], + ð->xsi_rsts.resets[i]); + if (ret) + return ret; + } } ret = airoha_hw_init(dev, eth); @@ -943,12 +1515,15 @@ static int airoha_eth_probe(struct udevice *dev) return ret; /* Airoha switch mdio PHYs maybe used by several GDM devices */ - mdio_dev = airoha_switch_mdio_init(dev); - if (!IS_ERR_OR_NULL(mdio_dev)) - eth->switch_mdio_dev = mdio_dev; + if (!data->econet) { + mdio_dev = airoha_switch_mdio_init(dev); + if (!IS_ERR_OR_NULL(mdio_dev)) + eth->switch_mdio_dev = mdio_dev; + } ofnode_for_each_subnode(node, dev_ofnode(dev)) { - if (!ofnode_device_is_compatible(node, "airoha,eth-mac")) + if (!ofnode_device_is_compatible(node, "airoha,eth-mac") && + !ofnode_device_is_compatible(node, "econet,eth-mac")) continue; if (!ofnode_is_enabled(node)) @@ -964,9 +1539,18 @@ static int airoha_eth_probe(struct udevice *dev) static int airoha_eth_port_of_to_plat(struct udevice *dev) { + struct airoha_eth *eth = (void *)dev_get_driver_data(dev); struct airoha_gdm_port *port = dev_get_priv(dev); + u32 id; + int ret; + + ret = dev_read_u32(dev, "reg", &id); + if (ret) + return ret; + + port->id = id + (eth->soc->econet ? 1 : 0); - return dev_read_u32(dev, "reg", &port->id); + return 0; } static int airoha_eth_port_probe(struct udevice *dev) @@ -992,6 +1576,9 @@ static int airoha_eth_port_probe(struct udevice *dev) return -EINVAL; #endif } else { + if (eth->soc->econet) + return 0; + /* * GDM1 device connected to airoha switch. Probe airoha switch * mdio to be able set/query states of corresponding LAN ports. @@ -1016,7 +1603,12 @@ static int airoha_eth_init(struct udevice *dev) qid = 0; q = &qdma->q_rx[qid]; - airoha_qdma_init_rx_desc(q); + if (airoha_is_en751221(qdma->eth)) + en751221_qdma_init_rx_desc(qdma); + else + airoha_qdma_init_rx_desc(q); + if (airoha_is_en751221(qdma->eth)) + en751221_qdma_reset_tx(qdma); airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK | @@ -1087,6 +1679,129 @@ static void airoha_eth_stop(struct udevice *dev) GLOBAL_CFG_RX_DMA_EN_MASK); } +static int en751221_eth_send(struct udevice *dev, void *packet, int length) +{ + struct airoha_gdm_port *port = dev_get_priv(dev); + struct airoha_qdma *qdma = port->qdma; + struct airoha_queue *q = &qdma->q_tx[0]; + struct airoha_qdma_desc *desc; + u32 index, next, ctrl = 0, irq_status = 0; + int i; + + index = q->head; + next = (index + 1) % q->ndesc; + desc = &q->desc[index]; + + /* + * Stage the frame in the KSEG1 bounce buffer (see + * airoha_dma_alloc_coherent). desc and bounce are uncached, so the + * writes land in DRAM; no map/flush. + */ + memcpy(q->bounce, packet, length); + + WRITE_ONCE(desc->rsv, 0); + WRITE_ONCE(desc->ctrl, FIELD_PREP(QDMA_DESC_LEN_MASK, length)); + WRITE_ONCE(desc->addr, q->bounce_phys); + WRITE_ONCE(desc->data, next); + WRITE_ONCE(desc->msg0, + FIELD_PREP(EN751221_TXMSG_CHANNEL_MASK, 0) | + FIELD_PREP(EN751221_TXMSG_QUEUE_MASK, 0)); + WRITE_ONCE(desc->msg1, + FIELD_PREP(EN751221_TXMSG_FPORT_MASK, + EN751221_FPORT_GDM1)); + WRITE_ONCE(desc->msg2, 0); + WRITE_ONCE(desc->msg3, 0); + + airoha_qdma_wr(qdma, EN751221_REG_TX_CPU_IDX, next); + + for (i = 0; i < 10000; i++) { + ctrl = READ_ONCE(desc->ctrl); + if (ctrl & QDMA_DESC_DONE_MASK) + break; + udelay(1); + } + + if (!(ctrl & QDMA_DESC_DONE_MASK)) + return -ETIMEDOUT; + + for (i = 0; i < 10000; i++) { + irq_status = airoha_qdma_rr(qdma, EN751221_REG_IRQ_STATUS); + if (FIELD_GET(EN751221_IRQ_ENTRY_LEN_MASK, irq_status)) + break; + udelay(1); + } + if (!FIELD_GET(EN751221_IRQ_ENTRY_LEN_MASK, irq_status)) + return -ETIMEDOUT; + + /* + * QDMA consumes one hardware-forward descriptor for each CPU TX + * context. Advancing the completion queue returns that descriptor + * to LMGR; without this, the four-entry first-light pool is exhausted + * after four packets even though TX writeback reports DONE. + */ + airoha_qdma_rmw(qdma, EN751221_REG_IRQ_CLEAR_LEN, + EN751221_IRQ_CLEAR_LEN_MASK, 1); + + q->head = next; + return 0; +} + +static int en751221_eth_recv(struct udevice *dev, uchar **packetp) +{ + struct airoha_gdm_port *port = dev_get_priv(dev); + struct airoha_qdma *qdma = port->qdma; + struct airoha_queue *q = &qdma->q_rx[0]; + struct airoha_qdma_desc *desc; + uchar *rx_packet; + u32 index, hw_index, length, addr; + + index = (q->head + 1) % q->ndesc; + hw_index = airoha_qdma_rr(qdma, EN751221_REG_RX_DMA_IDX) & + EN751221_RING_IDX_MASK; + if (index == hw_index) + return -EAGAIN; + + desc = &q->desc[index]; + dma_unmap_unaligned((dma_addr_t)desc, sizeof(*desc), + DMA_FROM_DEVICE); + length = FIELD_GET(QDMA_DESC_LEN_MASK, READ_ONCE(desc->ctrl)); + addr = READ_ONCE(desc->addr); + if (!length || length > PKTSIZE_ALIGN) { + en751221_qdma_reset_rx_desc(q, index); + airoha_qdma_wr(qdma, EN751221_REG_RX_CPU_IDX, index); + q->head = index; + return -EIO; + } + + /* + * Hand U-Boot the KSEG1 alias of the received frame. QDMA wrote DRAM + * directly; a cached read can still hit a stale L2 line because this + * CM cannot Hit_Writeback_Inv_SD (see airoha_dma_alloc_coherent). + */ + rx_packet = (uchar *)CKSEG1ADDR((unsigned long)addr); + *packetp = rx_packet; + + return length; +} + +static int en751221_eth_free_pkt(struct udevice *dev, uchar *packet) +{ + struct airoha_gdm_port *port = dev_get_priv(dev); + struct airoha_qdma *qdma = port->qdma; + struct airoha_queue *q = &qdma->q_rx[0]; + u32 index; + + if (!packet) + return 0; + + index = (q->head + 1) % q->ndesc; + en751221_qdma_reset_rx_desc(q, index); + airoha_qdma_wr(qdma, EN751221_REG_RX_CPU_IDX, index); + q->head = index; + + return 0; +} + static int airoha_eth_send(struct udevice *dev, void *packet, int length) { struct airoha_gdm_port *port = dev_get_priv(dev); @@ -1100,6 +1815,9 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length) u32 val; int i; + if (airoha_is_en751221(qdma->eth)) + return en751221_eth_send(dev, packet, length); + /* * There is no need to pad short TX packets to 60 bytes since the * GDM_PAD_EN bit set in the corresponding REG_GDM_FWD_CFG(n) register. @@ -1161,6 +1879,9 @@ static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp) u16 length; int qid; + if (airoha_is_en751221(qdma->eth)) + return en751221_eth_recv(dev, packetp); + qid = 0; q = &qdma->q_rx[qid]; desc = &q->desc[q->head]; @@ -1187,6 +1908,9 @@ static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length) struct airoha_queue *q; int qid; + if (airoha_is_en751221(qdma->eth)) + return en751221_eth_free_pkt(dev, packet); + if (!packet) return 0; @@ -1237,6 +1961,11 @@ static int arht_eth_write_hwaddr(struct udevice *dev) macaddr_msb = FIELD_PREP(SMACCR1_MAC1, mac[1]) | FIELD_PREP(SMACCR1_MAC0, mac[0]); + if (airoha_is_en751221(qdma->eth)) { + airoha_fe_wr(qdma->eth, REG_GDM_MAC_LSB(port->id), macaddr_lsb); + airoha_fe_wr(qdma->eth, REG_GDM_MAC_MSB(port->id), macaddr_msb); + } + /* Set MAC for Switch */ airoha_switch_wr(qdma->eth, SWITCH_SMACCR0, macaddr_lsb); airoha_switch_wr(qdma->eth, SWITCH_SMACCR1, macaddr_msb); @@ -1246,11 +1975,20 @@ static int arht_eth_write_hwaddr(struct udevice *dev) static int airoha_eth_bind(struct udevice *dev) { + const struct airoha_eth_soc_data *data; + + data = (const void *)dev_get_driver_data(dev); + /* - * Force Probe as we set the Main ETH driver as misc - * to register multiple eth port for each GDM + * Force probe on Airoha ARM SoCs since the parent is a misc device + * responsible for registering the child GDM Ethernet devices. Do not + * probe EN751221 here: DM_FLAG_PROBE_AFTER_BIND is handled by + * initr_dm_devices() before serial_initialize(), which makes an early + * QDMA/switch failure look like a silent hang. The EN751221 board + * probes this parent from board_late_init(), immediately before net init. */ - dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); + if (!data || !data->econet) + dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); return 0; } @@ -1262,6 +2000,12 @@ static const struct airoha_eth_soc_data en7523_data = { .switch_compatible = "airoha,en7523-switch", }; +static const struct airoha_eth_soc_data en751221_data = { + .version = 0x7512, + .econet = true, + .switch_compatible = "mediatek,mt7530", +}; + static const struct airoha_eth_soc_data en7581_data = { .version = 0x7581, .xsi_rsts_names = en7581_xsi_rsts_names, @@ -1269,13 +2013,25 @@ static const struct airoha_eth_soc_data en7581_data = { .switch_compatible = "airoha,en7581-switch", }; +static const struct airoha_eth_soc_data an7583_data = { + .xsi_rsts_names = an7583_xsi_rsts_names, + .num_xsi_rsts = ARRAY_SIZE(an7583_xsi_rsts_names), + .switch_compatible = "airoha,an7583-switch", +}; + static const struct udevice_id airoha_eth_ids[] = { + { .compatible = "econet,en751221-eth", + .data = (ulong)&en751221_data, + }, { .compatible = "airoha,en7523-eth", .data = (ulong)&en7523_data, }, { .compatible = "airoha,en7581-eth", .data = (ulong)&en7581_data, }, + { .compatible = "airoha,an7583-eth", + .data = (ulong)&an7583_data, + }, { } }; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 5c8ec2b1..28909d49 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -272,7 +272,7 @@ config PHY_EXYNOS_USBDRD config PHY_MTK_TPHY bool "MediaTek T-PHY Driver" depends on PHY - depends on ARCH_MEDIATEK || SOC_MT7621 + depends on ARCH_MEDIATEK || SOC_MT7621 || ARCH_EN75XX select REGMAP select SYSCON help -- 2.53.0