123020f04SLorenzo Bianconi // SPDX-License-Identifier: GPL-2.0-only 223020f04SLorenzo Bianconi /* 323020f04SLorenzo Bianconi * Copyright (c) 2024 AIROHA Inc 423020f04SLorenzo Bianconi * Author: Lorenzo Bianconi <lorenzo@kernel.org> 523020f04SLorenzo Bianconi */ 623020f04SLorenzo Bianconi #include <linux/of.h> 723020f04SLorenzo Bianconi #include <linux/of_net.h> 83a1ce9e3SLorenzo Bianconi #include <linux/of_reserved_mem.h> 923020f04SLorenzo Bianconi #include <linux/platform_device.h> 1023020f04SLorenzo Bianconi #include <linux/tcp.h> 1123020f04SLorenzo Bianconi #include <linux/u64_stats_sync.h> 12af3cf757SLorenzo Bianconi #include <net/dst_metadata.h> 1323020f04SLorenzo Bianconi #include <net/page_pool/helpers.h> 1420bf7d07SLorenzo Bianconi #include <net/pkt_cls.h> 1523020f04SLorenzo Bianconi #include <uapi/linux/ppp_defs.h> 1623020f04SLorenzo Bianconi 17ec663d9aSLorenzo Bianconi #include "airoha_regs.h" 18b38f4ff0SLorenzo Bianconi #include "airoha_eth.h" 19ef1ca927SLorenzo Bianconi 20e0758a86SLorenzo Bianconi u32 airoha_rr(void __iomem *base, u32 offset) 2123020f04SLorenzo Bianconi { 2223020f04SLorenzo Bianconi return readl(base + offset); 2323020f04SLorenzo Bianconi } 2423020f04SLorenzo Bianconi 25e0758a86SLorenzo Bianconi void airoha_wr(void __iomem *base, u32 offset, u32 val) 2623020f04SLorenzo Bianconi { 2723020f04SLorenzo Bianconi writel(val, base + offset); 2823020f04SLorenzo Bianconi } 2923020f04SLorenzo Bianconi 30e0758a86SLorenzo Bianconi u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) 3123020f04SLorenzo Bianconi { 3223020f04SLorenzo Bianconi val |= (airoha_rr(base, offset) & ~mask); 3323020f04SLorenzo Bianconi airoha_wr(base, offset, val); 3423020f04SLorenzo Bianconi 3523020f04SLorenzo Bianconi return val; 3623020f04SLorenzo Bianconi } 3723020f04SLorenzo Bianconi 389439db26SLorenzo Bianconi static void airoha_qdma_set_irqmask(struct airoha_irq_bank *irq_bank, 399439db26SLorenzo Bianconi int index, u32 clear, u32 set) 4023020f04SLorenzo Bianconi { 419439db26SLorenzo Bianconi struct airoha_qdma *qdma = irq_bank->qdma; 429439db26SLorenzo Bianconi int bank = irq_bank - &qdma->irq_banks[0]; 4323020f04SLorenzo Bianconi unsigned long flags; 4423020f04SLorenzo Bianconi 459439db26SLorenzo Bianconi if (WARN_ON_ONCE(index >= ARRAY_SIZE(irq_bank->irqmask))) 4623020f04SLorenzo Bianconi return; 4723020f04SLorenzo Bianconi 489439db26SLorenzo Bianconi spin_lock_irqsave(&irq_bank->irq_lock, flags); 4923020f04SLorenzo Bianconi 509439db26SLorenzo Bianconi irq_bank->irqmask[index] &= ~clear; 519439db26SLorenzo Bianconi irq_bank->irqmask[index] |= set; 529439db26SLorenzo Bianconi airoha_qdma_wr(qdma, REG_INT_ENABLE(bank, index), 539439db26SLorenzo Bianconi irq_bank->irqmask[index]); 5423020f04SLorenzo Bianconi /* Read irq_enable register in order to guarantee the update above 5523020f04SLorenzo Bianconi * completes in the spinlock critical section. 5623020f04SLorenzo Bianconi */ 579439db26SLorenzo Bianconi airoha_qdma_rr(qdma, REG_INT_ENABLE(bank, index)); 5823020f04SLorenzo Bianconi 599439db26SLorenzo Bianconi spin_unlock_irqrestore(&irq_bank->irq_lock, flags); 6023020f04SLorenzo Bianconi } 6123020f04SLorenzo Bianconi 629439db26SLorenzo Bianconi static void airoha_qdma_irq_enable(struct airoha_irq_bank *irq_bank, 639439db26SLorenzo Bianconi int index, u32 mask) 6423020f04SLorenzo Bianconi { 659439db26SLorenzo Bianconi airoha_qdma_set_irqmask(irq_bank, index, 0, mask); 6623020f04SLorenzo Bianconi } 6723020f04SLorenzo Bianconi 689439db26SLorenzo Bianconi static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank, 699439db26SLorenzo Bianconi int index, u32 mask) 7023020f04SLorenzo Bianconi { 719439db26SLorenzo Bianconi airoha_qdma_set_irqmask(irq_bank, index, mask, 0); 7223020f04SLorenzo Bianconi } 7323020f04SLorenzo Bianconi 74812a2751SLorenzo Bianconi static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) 75812a2751SLorenzo Bianconi { 76812a2751SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 77812a2751SLorenzo Bianconi u32 val, reg; 78812a2751SLorenzo Bianconi 79812a2751SLorenzo Bianconi reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H 80812a2751SLorenzo Bianconi : REG_FE_WAN_MAC_H; 8123020f04SLorenzo Bianconi val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; 82812a2751SLorenzo Bianconi airoha_fe_wr(eth, reg, val); 8323020f04SLorenzo Bianconi 8423020f04SLorenzo Bianconi val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; 85812a2751SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); 86812a2751SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); 87a869d3a5SLorenzo Bianconi 88a869d3a5SLorenzo Bianconi airoha_ppe_init_upd_mem(port); 8923020f04SLorenzo Bianconi } 9023020f04SLorenzo Bianconi 9123020f04SLorenzo Bianconi static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, 9223020f04SLorenzo Bianconi u32 val) 9323020f04SLorenzo Bianconi { 9423020f04SLorenzo Bianconi airoha_fe_rmw(eth, addr, GDM_OCFQ_MASK, 9523020f04SLorenzo Bianconi FIELD_PREP(GDM_OCFQ_MASK, val)); 9623020f04SLorenzo Bianconi airoha_fe_rmw(eth, addr, GDM_MCFQ_MASK, 9723020f04SLorenzo Bianconi FIELD_PREP(GDM_MCFQ_MASK, val)); 9823020f04SLorenzo Bianconi airoha_fe_rmw(eth, addr, GDM_BCFQ_MASK, 9923020f04SLorenzo Bianconi FIELD_PREP(GDM_BCFQ_MASK, val)); 10023020f04SLorenzo Bianconi airoha_fe_rmw(eth, addr, GDM_UCFQ_MASK, 10123020f04SLorenzo Bianconi FIELD_PREP(GDM_UCFQ_MASK, val)); 10223020f04SLorenzo Bianconi } 10323020f04SLorenzo Bianconi 104c28b8375SLorenzo Bianconi static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port, 105c28b8375SLorenzo Bianconi bool enable) 10623020f04SLorenzo Bianconi { 107c28b8375SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 10867fde5d5SLorenzo Bianconi u32 vip_port; 10923020f04SLorenzo Bianconi 110c28b8375SLorenzo Bianconi switch (port->id) { 111c28b8375SLorenzo Bianconi case 3: 112c28b8375SLorenzo Bianconi /* FIXME: handle XSI_PCIE1_PORT */ 11323020f04SLorenzo Bianconi vip_port = XSI_PCIE0_VIP_PORT_MASK; 11423020f04SLorenzo Bianconi break; 115c28b8375SLorenzo Bianconi case 4: 116c28b8375SLorenzo Bianconi /* FIXME: handle XSI_USB_PORT */ 11723020f04SLorenzo Bianconi vip_port = XSI_ETH_VIP_PORT_MASK; 11823020f04SLorenzo Bianconi break; 11923020f04SLorenzo Bianconi default: 120c28b8375SLorenzo Bianconi return 0; 12123020f04SLorenzo Bianconi } 12223020f04SLorenzo Bianconi 12323020f04SLorenzo Bianconi if (enable) { 12423020f04SLorenzo Bianconi airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); 12523020f04SLorenzo Bianconi airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); 12623020f04SLorenzo Bianconi } else { 12723020f04SLorenzo Bianconi airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, vip_port); 12823020f04SLorenzo Bianconi airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port); 12923020f04SLorenzo Bianconi } 13023020f04SLorenzo Bianconi 13123020f04SLorenzo Bianconi return 0; 13223020f04SLorenzo Bianconi } 13323020f04SLorenzo Bianconi 13423020f04SLorenzo Bianconi static void airoha_fe_maccr_init(struct airoha_eth *eth) 13523020f04SLorenzo Bianconi { 13623020f04SLorenzo Bianconi int p; 13723020f04SLorenzo Bianconi 13854d989d5SLorenzo Bianconi for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) 13923020f04SLorenzo Bianconi airoha_fe_set(eth, REG_GDM_FWD_CFG(p), 14023020f04SLorenzo Bianconi GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | 14123020f04SLorenzo Bianconi GDM_DROP_CRC_ERR); 14223020f04SLorenzo Bianconi 14323020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, 14423020f04SLorenzo Bianconi FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); 14523020f04SLorenzo Bianconi 14623020f04SLorenzo Bianconi airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD); 14723020f04SLorenzo Bianconi } 14823020f04SLorenzo Bianconi 14923020f04SLorenzo Bianconi static void airoha_fe_vip_setup(struct airoha_eth *eth) 15023020f04SLorenzo Bianconi { 15123020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(3), ETH_P_PPP_DISC); 15223020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(3), PATN_FCPU_EN_MASK | PATN_EN_MASK); 15323020f04SLorenzo Bianconi 15423020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(4), PPP_LCP); 15523020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(4), 15623020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | 15723020f04SLorenzo Bianconi PATN_EN_MASK); 15823020f04SLorenzo Bianconi 15923020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(6), PPP_IPCP); 16023020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(6), 16123020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | 16223020f04SLorenzo Bianconi PATN_EN_MASK); 16323020f04SLorenzo Bianconi 16423020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(7), PPP_CHAP); 16523020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(7), 16623020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | 16723020f04SLorenzo Bianconi PATN_EN_MASK); 16823020f04SLorenzo Bianconi 16923020f04SLorenzo Bianconi /* BOOTP (0x43) */ 17023020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(8), 0x43); 17123020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(8), 17223020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | 17323020f04SLorenzo Bianconi FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); 17423020f04SLorenzo Bianconi 17523020f04SLorenzo Bianconi /* BOOTP (0x44) */ 17623020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(9), 0x44); 17723020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(9), 17823020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | 17923020f04SLorenzo Bianconi FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); 18023020f04SLorenzo Bianconi 18123020f04SLorenzo Bianconi /* ISAKMP */ 18223020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(10), 0x1f401f4); 18323020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(10), 18423020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | 18523020f04SLorenzo Bianconi FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); 18623020f04SLorenzo Bianconi 18723020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(11), PPP_IPV6CP); 18823020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(11), 18923020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | 19023020f04SLorenzo Bianconi PATN_EN_MASK); 19123020f04SLorenzo Bianconi 19223020f04SLorenzo Bianconi /* DHCPv6 */ 19323020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(12), 0x2220223); 19423020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(12), 19523020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | 19623020f04SLorenzo Bianconi FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); 19723020f04SLorenzo Bianconi 19823020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(19), PPP_PAP); 19923020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(19), 20023020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | 20123020f04SLorenzo Bianconi PATN_EN_MASK); 20223020f04SLorenzo Bianconi 20323020f04SLorenzo Bianconi /* ETH->ETH_P_1905 (0x893a) */ 20423020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(20), 0x893a); 20523020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(20), 20623020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | PATN_EN_MASK); 20723020f04SLorenzo Bianconi 20823020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_PATN(21), ETH_P_LLDP); 20923020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_VIP_EN(21), 21023020f04SLorenzo Bianconi PATN_FCPU_EN_MASK | PATN_EN_MASK); 21123020f04SLorenzo Bianconi } 21223020f04SLorenzo Bianconi 21323020f04SLorenzo Bianconi static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth, 21423020f04SLorenzo Bianconi u32 port, u32 queue) 21523020f04SLorenzo Bianconi { 21623020f04SLorenzo Bianconi u32 val; 21723020f04SLorenzo Bianconi 21823020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, 21923020f04SLorenzo Bianconi PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK, 22023020f04SLorenzo Bianconi FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | 22123020f04SLorenzo Bianconi FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue)); 22223020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL); 22323020f04SLorenzo Bianconi 22423020f04SLorenzo Bianconi return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val); 22523020f04SLorenzo Bianconi } 22623020f04SLorenzo Bianconi 22723020f04SLorenzo Bianconi static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, 22823020f04SLorenzo Bianconi u32 port, u32 queue, u32 val) 22923020f04SLorenzo Bianconi { 23023020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_VAL, PSE_CFG_OQ_RSV_MASK, 23123020f04SLorenzo Bianconi FIELD_PREP(PSE_CFG_OQ_RSV_MASK, val)); 23223020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, 23323020f04SLorenzo Bianconi PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK | 23423020f04SLorenzo Bianconi PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK, 23523020f04SLorenzo Bianconi FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | 23623020f04SLorenzo Bianconi FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue) | 23723020f04SLorenzo Bianconi PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK); 23823020f04SLorenzo Bianconi } 23923020f04SLorenzo Bianconi 2401f3e7ff4SLorenzo Bianconi static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth) 2411f3e7ff4SLorenzo Bianconi { 2421f3e7ff4SLorenzo Bianconi u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); 2431f3e7ff4SLorenzo Bianconi 2441f3e7ff4SLorenzo Bianconi return FIELD_GET(PSE_ALLRSV_MASK, val); 2451f3e7ff4SLorenzo Bianconi } 2461f3e7ff4SLorenzo Bianconi 24723020f04SLorenzo Bianconi static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, 24823020f04SLorenzo Bianconi u32 port, u32 queue, u32 val) 24923020f04SLorenzo Bianconi { 2501f3e7ff4SLorenzo Bianconi u32 orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue); 2511f3e7ff4SLorenzo Bianconi u32 tmp, all_rsv, fq_limit; 25223020f04SLorenzo Bianconi 25323020f04SLorenzo Bianconi airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val); 25423020f04SLorenzo Bianconi 25523020f04SLorenzo Bianconi /* modify all rsv */ 2561f3e7ff4SLorenzo Bianconi all_rsv = airoha_fe_get_pse_all_rsv(eth); 25723020f04SLorenzo Bianconi all_rsv += (val - orig_val); 25823020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK, 25923020f04SLorenzo Bianconi FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); 26023020f04SLorenzo Bianconi 26123020f04SLorenzo Bianconi /* modify hthd */ 26223020f04SLorenzo Bianconi tmp = airoha_fe_rr(eth, PSE_FQ_CFG); 26323020f04SLorenzo Bianconi fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp); 26423020f04SLorenzo Bianconi tmp = fq_limit - all_rsv - 0x20; 26523020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, 26623020f04SLorenzo Bianconi PSE_SHARE_USED_HTHD_MASK, 26723020f04SLorenzo Bianconi FIELD_PREP(PSE_SHARE_USED_HTHD_MASK, tmp)); 26823020f04SLorenzo Bianconi 26923020f04SLorenzo Bianconi tmp = fq_limit - all_rsv - 0x100; 27023020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, 27123020f04SLorenzo Bianconi PSE_SHARE_USED_MTHD_MASK, 27223020f04SLorenzo Bianconi FIELD_PREP(PSE_SHARE_USED_MTHD_MASK, tmp)); 27323020f04SLorenzo Bianconi tmp = (3 * tmp) >> 2; 27423020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, 27523020f04SLorenzo Bianconi PSE_SHARE_USED_LTHD_MASK, 27623020f04SLorenzo Bianconi FIELD_PREP(PSE_SHARE_USED_LTHD_MASK, tmp)); 27723020f04SLorenzo Bianconi 27823020f04SLorenzo Bianconi return 0; 27923020f04SLorenzo Bianconi } 28023020f04SLorenzo Bianconi 28123020f04SLorenzo Bianconi static void airoha_fe_pse_ports_init(struct airoha_eth *eth) 28223020f04SLorenzo Bianconi { 28323020f04SLorenzo Bianconi const u32 pse_port_num_queues[] = { 28423020f04SLorenzo Bianconi [FE_PSE_PORT_CDM1] = 6, 28523020f04SLorenzo Bianconi [FE_PSE_PORT_GDM1] = 6, 28623020f04SLorenzo Bianconi [FE_PSE_PORT_GDM2] = 32, 28723020f04SLorenzo Bianconi [FE_PSE_PORT_GDM3] = 6, 28823020f04SLorenzo Bianconi [FE_PSE_PORT_PPE1] = 4, 28923020f04SLorenzo Bianconi [FE_PSE_PORT_CDM2] = 6, 29023020f04SLorenzo Bianconi [FE_PSE_PORT_CDM3] = 8, 29123020f04SLorenzo Bianconi [FE_PSE_PORT_CDM4] = 10, 29223020f04SLorenzo Bianconi [FE_PSE_PORT_PPE2] = 4, 29323020f04SLorenzo Bianconi [FE_PSE_PORT_GDM4] = 2, 29423020f04SLorenzo Bianconi [FE_PSE_PORT_CDM5] = 2, 29523020f04SLorenzo Bianconi }; 2968e38e08fSLorenzo Bianconi u32 all_rsv; 29723020f04SLorenzo Bianconi int q; 29823020f04SLorenzo Bianconi 2998e38e08fSLorenzo Bianconi all_rsv = airoha_fe_get_pse_all_rsv(eth); 30023020f04SLorenzo Bianconi /* hw misses PPE2 oq rsv */ 3018e38e08fSLorenzo Bianconi all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]; 3028e38e08fSLorenzo Bianconi airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv); 30323020f04SLorenzo Bianconi 30423020f04SLorenzo Bianconi /* CMD1 */ 30523020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) 30623020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q, 30723020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 30823020f04SLorenzo Bianconi /* GMD1 */ 30923020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++) 31023020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q, 31123020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 31223020f04SLorenzo Bianconi /* GMD2 */ 31323020f04SLorenzo Bianconi for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++) 31423020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0); 31523020f04SLorenzo Bianconi /* GMD3 */ 31623020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++) 31723020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q, 31823020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 31923020f04SLorenzo Bianconi /* PPE1 */ 32023020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { 32123020f04SLorenzo Bianconi if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) 32223020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 32323020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 32423020f04SLorenzo Bianconi else 32523020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 0); 32623020f04SLorenzo Bianconi } 32723020f04SLorenzo Bianconi /* CDM2 */ 32823020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM2]; q++) 32923020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM2, q, 33023020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 33123020f04SLorenzo Bianconi /* CDM3 */ 33223020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM3] - 1; q++) 33323020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM3, q, 0); 33423020f04SLorenzo Bianconi /* CDM4 */ 33523020f04SLorenzo Bianconi for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++) 33623020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q, 33723020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 33823020f04SLorenzo Bianconi /* PPE2 */ 33923020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) { 34023020f04SLorenzo Bianconi if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2) 34123020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 34223020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 34323020f04SLorenzo Bianconi else 34423020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0); 34523020f04SLorenzo Bianconi } 34623020f04SLorenzo Bianconi /* GMD4 */ 34723020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++) 34823020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q, 34923020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 35023020f04SLorenzo Bianconi /* CDM5 */ 35123020f04SLorenzo Bianconi for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM5]; q++) 35223020f04SLorenzo Bianconi airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM5, q, 35323020f04SLorenzo Bianconi PSE_QUEUE_RSV_PAGES); 35423020f04SLorenzo Bianconi } 35523020f04SLorenzo Bianconi 35623020f04SLorenzo Bianconi static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth) 35723020f04SLorenzo Bianconi { 35823020f04SLorenzo Bianconi int i; 35923020f04SLorenzo Bianconi 36023020f04SLorenzo Bianconi for (i = 0; i < AIROHA_FE_MC_MAX_VLAN_TABLE; i++) { 36123020f04SLorenzo Bianconi int err, j; 36223020f04SLorenzo Bianconi u32 val; 36323020f04SLorenzo Bianconi 36423020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); 36523020f04SLorenzo Bianconi 36623020f04SLorenzo Bianconi val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | 36723020f04SLorenzo Bianconi MC_VLAN_CFG_TABLE_SEL_MASK | MC_VLAN_CFG_RW_MASK; 36823020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); 36923020f04SLorenzo Bianconi err = read_poll_timeout(airoha_fe_rr, val, 37023020f04SLorenzo Bianconi val & MC_VLAN_CFG_CMD_DONE_MASK, 37123020f04SLorenzo Bianconi USEC_PER_MSEC, 5 * USEC_PER_MSEC, 37223020f04SLorenzo Bianconi false, eth, REG_MC_VLAN_CFG); 37323020f04SLorenzo Bianconi if (err) 37423020f04SLorenzo Bianconi return err; 37523020f04SLorenzo Bianconi 37623020f04SLorenzo Bianconi for (j = 0; j < AIROHA_FE_MC_MAX_VLAN_PORT; j++) { 37723020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); 37823020f04SLorenzo Bianconi 37923020f04SLorenzo Bianconi val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | 38023020f04SLorenzo Bianconi FIELD_PREP(MC_VLAN_CFG_PORT_ID_MASK, j) | 38123020f04SLorenzo Bianconi MC_VLAN_CFG_RW_MASK; 38223020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); 38323020f04SLorenzo Bianconi err = read_poll_timeout(airoha_fe_rr, val, 38423020f04SLorenzo Bianconi val & MC_VLAN_CFG_CMD_DONE_MASK, 38523020f04SLorenzo Bianconi USEC_PER_MSEC, 38623020f04SLorenzo Bianconi 5 * USEC_PER_MSEC, false, eth, 38723020f04SLorenzo Bianconi REG_MC_VLAN_CFG); 38823020f04SLorenzo Bianconi if (err) 38923020f04SLorenzo Bianconi return err; 39023020f04SLorenzo Bianconi } 39123020f04SLorenzo Bianconi } 39223020f04SLorenzo Bianconi 39323020f04SLorenzo Bianconi return 0; 39423020f04SLorenzo Bianconi } 39523020f04SLorenzo Bianconi 39623020f04SLorenzo Bianconi static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth) 39723020f04SLorenzo Bianconi { 39823020f04SLorenzo Bianconi /* CDM1_CRSN_QSEL */ 39923020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2), 40023020f04SLorenzo Bianconi CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), 40123020f04SLorenzo Bianconi FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), 40223020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 40323020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2), 40423020f04SLorenzo Bianconi CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), 40523020f04SLorenzo Bianconi FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), 40623020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 40723020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2), 40823020f04SLorenzo Bianconi CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), 40923020f04SLorenzo Bianconi FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), 41023020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 41123020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2), 41223020f04SLorenzo Bianconi CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), 41323020f04SLorenzo Bianconi FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), 41423020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q6)); 41523020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2), 41623020f04SLorenzo Bianconi CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), 41723020f04SLorenzo Bianconi FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), 41823020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 41923020f04SLorenzo Bianconi /* CDM2_CRSN_QSEL */ 42023020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2), 42123020f04SLorenzo Bianconi CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), 42223020f04SLorenzo Bianconi FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), 42323020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 42423020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2), 42523020f04SLorenzo Bianconi CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), 42623020f04SLorenzo Bianconi FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), 42723020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 42823020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2), 42923020f04SLorenzo Bianconi CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), 43023020f04SLorenzo Bianconi FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), 43123020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 43223020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2), 43323020f04SLorenzo Bianconi CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), 43423020f04SLorenzo Bianconi FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), 43523020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q6)); 43623020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2), 43723020f04SLorenzo Bianconi CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), 43823020f04SLorenzo Bianconi FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), 43923020f04SLorenzo Bianconi CDM_CRSN_QSEL_Q1)); 44023020f04SLorenzo Bianconi } 44123020f04SLorenzo Bianconi 44223020f04SLorenzo Bianconi static int airoha_fe_init(struct airoha_eth *eth) 44323020f04SLorenzo Bianconi { 44423020f04SLorenzo Bianconi airoha_fe_maccr_init(eth); 44523020f04SLorenzo Bianconi 44623020f04SLorenzo Bianconi /* PSE IQ reserve */ 44723020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_PSE_IQ_REV1, PSE_IQ_RES1_P2_MASK, 44823020f04SLorenzo Bianconi FIELD_PREP(PSE_IQ_RES1_P2_MASK, 0x10)); 44923020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_PSE_IQ_REV2, 45023020f04SLorenzo Bianconi PSE_IQ_RES2_P5_MASK | PSE_IQ_RES2_P4_MASK, 45123020f04SLorenzo Bianconi FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) | 45223020f04SLorenzo Bianconi FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34)); 45323020f04SLorenzo Bianconi 45423020f04SLorenzo Bianconi /* enable FE copy engine for MC/KA/DPI */ 45523020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_PCE_CFG, 45623020f04SLorenzo Bianconi PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK); 45723020f04SLorenzo Bianconi /* set vip queue selection to ring 1 */ 45823020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK, 45923020f04SLorenzo Bianconi FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4)); 46023020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK, 46123020f04SLorenzo Bianconi FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4)); 46223020f04SLorenzo Bianconi /* set GDM4 source interface offset to 8 */ 46323020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET, 46423020f04SLorenzo Bianconi GDM4_SPORT_OFF2_MASK | 46523020f04SLorenzo Bianconi GDM4_SPORT_OFF1_MASK | 46623020f04SLorenzo Bianconi GDM4_SPORT_OFF0_MASK, 46723020f04SLorenzo Bianconi FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) | 46823020f04SLorenzo Bianconi FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) | 46923020f04SLorenzo Bianconi FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8)); 47023020f04SLorenzo Bianconi 47123020f04SLorenzo Bianconi /* set PSE Page as 128B */ 47223020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG, 47323020f04SLorenzo Bianconi FE_DMA_GLO_L2_SPACE_MASK | FE_DMA_GLO_PG_SZ_MASK, 47423020f04SLorenzo Bianconi FIELD_PREP(FE_DMA_GLO_L2_SPACE_MASK, 2) | 47523020f04SLorenzo Bianconi FE_DMA_GLO_PG_SZ_MASK); 47623020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_RST_GLO_CFG, 47723020f04SLorenzo Bianconi FE_RST_CORE_MASK | FE_RST_GDM3_MBI_ARB_MASK | 47823020f04SLorenzo Bianconi FE_RST_GDM4_MBI_ARB_MASK); 47923020f04SLorenzo Bianconi usleep_range(1000, 2000); 48023020f04SLorenzo Bianconi 48123020f04SLorenzo Bianconi /* connect RxRing1 and RxRing15 to PSE Port0 OQ-1 48223020f04SLorenzo Bianconi * connect other rings to PSE Port0 OQ-0 48323020f04SLorenzo Bianconi */ 48423020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP0, BIT(4)); 48523020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP1, BIT(28)); 48623020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP2, BIT(4)); 48723020f04SLorenzo Bianconi airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP3, BIT(28)); 48823020f04SLorenzo Bianconi 48923020f04SLorenzo Bianconi airoha_fe_vip_setup(eth); 49023020f04SLorenzo Bianconi airoha_fe_pse_ports_init(eth); 49123020f04SLorenzo Bianconi 49223020f04SLorenzo Bianconi airoha_fe_set(eth, REG_GDM_MISC_CFG, 49323020f04SLorenzo Bianconi GDM2_RDM_ACK_WAIT_PREF_MASK | 49423020f04SLorenzo Bianconi GDM2_CHN_VLD_MODE_MASK); 49530d9d8f6SLorenzo Bianconi airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, 49630d9d8f6SLorenzo Bianconi FIELD_PREP(CDM2_OAM_QSEL_MASK, 15)); 49723020f04SLorenzo Bianconi 49823020f04SLorenzo Bianconi /* init fragment and assemble Force Port */ 49923020f04SLorenzo Bianconi /* NPU Core-3, NPU Bridge Channel-3 */ 50023020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_IP_FRAG_FP, 50123020f04SLorenzo Bianconi IP_FRAGMENT_PORT_MASK | IP_FRAGMENT_NBQ_MASK, 50223020f04SLorenzo Bianconi FIELD_PREP(IP_FRAGMENT_PORT_MASK, 6) | 50323020f04SLorenzo Bianconi FIELD_PREP(IP_FRAGMENT_NBQ_MASK, 3)); 50423020f04SLorenzo Bianconi /* QDMA LAN, RX Ring-22 */ 50523020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_IP_FRAG_FP, 50623020f04SLorenzo Bianconi IP_ASSEMBLE_PORT_MASK | IP_ASSEMBLE_NBQ_MASK, 50723020f04SLorenzo Bianconi FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | 50823020f04SLorenzo Bianconi FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); 50923020f04SLorenzo Bianconi 51023020f04SLorenzo Bianconi airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK); 51123020f04SLorenzo Bianconi airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK); 51223020f04SLorenzo Bianconi 51323020f04SLorenzo Bianconi airoha_fe_crsn_qsel_init(eth); 51423020f04SLorenzo Bianconi 51523020f04SLorenzo Bianconi airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK); 51623020f04SLorenzo Bianconi airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK); 51723020f04SLorenzo Bianconi 51823020f04SLorenzo Bianconi /* default aging mode for mbi unlock issue */ 51923020f04SLorenzo Bianconi airoha_fe_rmw(eth, REG_GDM2_CHN_RLS, 52023020f04SLorenzo Bianconi MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK, 52123020f04SLorenzo Bianconi FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) | 52223020f04SLorenzo Bianconi FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3)); 52323020f04SLorenzo Bianconi 52423020f04SLorenzo Bianconi /* disable IFC by default */ 52523020f04SLorenzo Bianconi airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); 52623020f04SLorenzo Bianconi 527df8398fbSLorenzo Bianconi airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), 528df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) | 529df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) | 530df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) | 531df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) | 532df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) | 533df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) | 534df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) | 535df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1)); 536df8398fbSLorenzo Bianconi airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1), 537df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) | 538df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) | 539df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) | 540df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) | 541df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) | 542df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) | 543df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) | 544df8398fbSLorenzo Bianconi FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2)); 545df8398fbSLorenzo Bianconi 54623020f04SLorenzo Bianconi /* enable 1:N vlan action, init vlan table */ 54723020f04SLorenzo Bianconi airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); 54823020f04SLorenzo Bianconi 54923020f04SLorenzo Bianconi return airoha_fe_mc_vlan_clear(eth); 55023020f04SLorenzo Bianconi } 55123020f04SLorenzo Bianconi 55223020f04SLorenzo Bianconi static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) 55323020f04SLorenzo Bianconi { 55423020f04SLorenzo Bianconi enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); 5559a2500abSLorenzo Bianconi struct airoha_qdma *qdma = q->qdma; 5569a2500abSLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 557245c7bc8SLorenzo Bianconi int qid = q - &qdma->q_rx[0]; 55823020f04SLorenzo Bianconi int nframes = 0; 55923020f04SLorenzo Bianconi 56023020f04SLorenzo Bianconi while (q->queued < q->ndesc - 1) { 56123020f04SLorenzo Bianconi struct airoha_queue_entry *e = &q->entry[q->head]; 56223020f04SLorenzo Bianconi struct airoha_qdma_desc *desc = &q->desc[q->head]; 56323020f04SLorenzo Bianconi struct page *page; 56423020f04SLorenzo Bianconi int offset; 56523020f04SLorenzo Bianconi u32 val; 56623020f04SLorenzo Bianconi 56723020f04SLorenzo Bianconi page = page_pool_dev_alloc_frag(q->page_pool, &offset, 56823020f04SLorenzo Bianconi q->buf_size); 56923020f04SLorenzo Bianconi if (!page) 57023020f04SLorenzo Bianconi break; 57123020f04SLorenzo Bianconi 57223020f04SLorenzo Bianconi q->head = (q->head + 1) % q->ndesc; 57323020f04SLorenzo Bianconi q->queued++; 57423020f04SLorenzo Bianconi nframes++; 57523020f04SLorenzo Bianconi 57623020f04SLorenzo Bianconi e->buf = page_address(page) + offset; 57723020f04SLorenzo Bianconi e->dma_addr = page_pool_get_dma_addr(page) + offset; 57823020f04SLorenzo Bianconi e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); 57923020f04SLorenzo Bianconi 58023020f04SLorenzo Bianconi dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len, 58123020f04SLorenzo Bianconi dir); 58223020f04SLorenzo Bianconi 58323020f04SLorenzo Bianconi val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); 58423020f04SLorenzo Bianconi WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); 58523020f04SLorenzo Bianconi WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr)); 58623020f04SLorenzo Bianconi val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, q->head); 58723020f04SLorenzo Bianconi WRITE_ONCE(desc->data, cpu_to_le32(val)); 58823020f04SLorenzo Bianconi WRITE_ONCE(desc->msg0, 0); 58923020f04SLorenzo Bianconi WRITE_ONCE(desc->msg1, 0); 59023020f04SLorenzo Bianconi WRITE_ONCE(desc->msg2, 0); 59123020f04SLorenzo Bianconi WRITE_ONCE(desc->msg3, 0); 59223020f04SLorenzo Bianconi 59316874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), 59416874d1cSLorenzo Bianconi RX_RING_CPU_IDX_MASK, 59523020f04SLorenzo Bianconi FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); 59623020f04SLorenzo Bianconi } 59723020f04SLorenzo Bianconi 59823020f04SLorenzo Bianconi return nframes; 59923020f04SLorenzo Bianconi } 60023020f04SLorenzo Bianconi 60123020f04SLorenzo Bianconi static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, 60223020f04SLorenzo Bianconi struct airoha_qdma_desc *desc) 60323020f04SLorenzo Bianconi { 60423020f04SLorenzo Bianconi u32 port, sport, msg1 = le32_to_cpu(desc->msg1); 60523020f04SLorenzo Bianconi 60623020f04SLorenzo Bianconi sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); 60723020f04SLorenzo Bianconi switch (sport) { 60835ea4f06SLorenzo Bianconi case 0x10 ... 0x14: 60923020f04SLorenzo Bianconi port = 0; 61023020f04SLorenzo Bianconi break; 61123020f04SLorenzo Bianconi case 0x2 ... 0x4: 61223020f04SLorenzo Bianconi port = sport - 1; 61323020f04SLorenzo Bianconi break; 61423020f04SLorenzo Bianconi default: 61523020f04SLorenzo Bianconi return -EINVAL; 61623020f04SLorenzo Bianconi } 61723020f04SLorenzo Bianconi 61823020f04SLorenzo Bianconi return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; 61923020f04SLorenzo Bianconi } 62023020f04SLorenzo Bianconi 62123020f04SLorenzo Bianconi static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) 62223020f04SLorenzo Bianconi { 62323020f04SLorenzo Bianconi enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); 6249a2500abSLorenzo Bianconi struct airoha_qdma *qdma = q->qdma; 6259a2500abSLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 626245c7bc8SLorenzo Bianconi int qid = q - &qdma->q_rx[0]; 62723020f04SLorenzo Bianconi int done = 0; 62823020f04SLorenzo Bianconi 62923020f04SLorenzo Bianconi while (done < budget) { 63023020f04SLorenzo Bianconi struct airoha_queue_entry *e = &q->entry[q->tail]; 63123020f04SLorenzo Bianconi struct airoha_qdma_desc *desc = &q->desc[q->tail]; 63200a76783SLorenzo Bianconi u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); 633e12182ddSLorenzo Bianconi struct page *page = virt_to_head_page(e->buf); 63423020f04SLorenzo Bianconi u32 desc_ctrl = le32_to_cpu(desc->ctrl); 635af3cf757SLorenzo Bianconi struct airoha_gdm_port *port; 636e12182ddSLorenzo Bianconi int data_len, len, p; 63723020f04SLorenzo Bianconi 63823020f04SLorenzo Bianconi if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) 63923020f04SLorenzo Bianconi break; 64023020f04SLorenzo Bianconi 64123020f04SLorenzo Bianconi q->tail = (q->tail + 1) % q->ndesc; 64223020f04SLorenzo Bianconi q->queued--; 64323020f04SLorenzo Bianconi 644d6d2b0e1SLorenzo Bianconi dma_sync_single_for_cpu(eth->dev, e->dma_addr, 64523020f04SLorenzo Bianconi SKB_WITH_OVERHEAD(q->buf_size), dir); 64623020f04SLorenzo Bianconi 647d6d2b0e1SLorenzo Bianconi len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); 648e12182ddSLorenzo Bianconi data_len = q->skb ? q->buf_size 649e12182ddSLorenzo Bianconi : SKB_WITH_OVERHEAD(q->buf_size); 650d6d2b0e1SLorenzo Bianconi if (!len || data_len < len) 651e12182ddSLorenzo Bianconi goto free_frag; 652e12182ddSLorenzo Bianconi 65323020f04SLorenzo Bianconi p = airoha_qdma_get_gdm_port(eth, desc); 654e12182ddSLorenzo Bianconi if (p < 0 || !eth->ports[p]) 655e12182ddSLorenzo Bianconi goto free_frag; 65623020f04SLorenzo Bianconi 657af3cf757SLorenzo Bianconi port = eth->ports[p]; 658e12182ddSLorenzo Bianconi if (!q->skb) { /* first buffer */ 659e12182ddSLorenzo Bianconi q->skb = napi_build_skb(e->buf, q->buf_size); 660e12182ddSLorenzo Bianconi if (!q->skb) 661e12182ddSLorenzo Bianconi goto free_frag; 662e12182ddSLorenzo Bianconi 663e12182ddSLorenzo Bianconi __skb_put(q->skb, len); 664e12182ddSLorenzo Bianconi skb_mark_for_recycle(q->skb); 665e12182ddSLorenzo Bianconi q->skb->dev = port->dev; 666e12182ddSLorenzo Bianconi q->skb->protocol = eth_type_trans(q->skb, port->dev); 667e12182ddSLorenzo Bianconi q->skb->ip_summed = CHECKSUM_UNNECESSARY; 668e12182ddSLorenzo Bianconi skb_record_rx_queue(q->skb, qid); 669e12182ddSLorenzo Bianconi } else { /* scattered frame */ 670e12182ddSLorenzo Bianconi struct skb_shared_info *shinfo = skb_shinfo(q->skb); 671e12182ddSLorenzo Bianconi int nr_frags = shinfo->nr_frags; 672e12182ddSLorenzo Bianconi 673e12182ddSLorenzo Bianconi if (nr_frags >= ARRAY_SIZE(shinfo->frags)) 674e12182ddSLorenzo Bianconi goto free_frag; 675e12182ddSLorenzo Bianconi 676e12182ddSLorenzo Bianconi skb_add_rx_frag(q->skb, nr_frags, page, 677e12182ddSLorenzo Bianconi e->buf - page_address(page), len, 678e12182ddSLorenzo Bianconi q->buf_size); 67923020f04SLorenzo Bianconi } 68023020f04SLorenzo Bianconi 681e12182ddSLorenzo Bianconi if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl)) 682e12182ddSLorenzo Bianconi continue; 683af3cf757SLorenzo Bianconi 684af3cf757SLorenzo Bianconi if (netdev_uses_dsa(port->dev)) { 685af3cf757SLorenzo Bianconi /* PPE module requires untagged packets to work 686af3cf757SLorenzo Bianconi * properly and it provides DSA port index via the 687af3cf757SLorenzo Bianconi * DMA descriptor. Report DSA tag to the DSA stack 688af3cf757SLorenzo Bianconi * via skb dst info. 689af3cf757SLorenzo Bianconi */ 690af3cf757SLorenzo Bianconi u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG, 691af3cf757SLorenzo Bianconi le32_to_cpu(desc->msg0)); 692af3cf757SLorenzo Bianconi 693af3cf757SLorenzo Bianconi if (sptag < ARRAY_SIZE(port->dsa_meta) && 694af3cf757SLorenzo Bianconi port->dsa_meta[sptag]) 695e12182ddSLorenzo Bianconi skb_dst_set_noref(q->skb, 696af3cf757SLorenzo Bianconi &port->dsa_meta[sptag]->dst); 697af3cf757SLorenzo Bianconi } 698af3cf757SLorenzo Bianconi 69900a76783SLorenzo Bianconi hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1); 70000a76783SLorenzo Bianconi if (hash != AIROHA_RXD4_FOE_ENTRY) 701e12182ddSLorenzo Bianconi skb_set_hash(q->skb, jhash_1word(hash, 0), 70200a76783SLorenzo Bianconi PKT_HASH_TYPE_L4); 70300a76783SLorenzo Bianconi 70400a76783SLorenzo Bianconi reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); 70500a76783SLorenzo Bianconi if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) 706cd53f622SLorenzo Bianconi airoha_ppe_check_skb(eth->ppe, q->skb, hash); 70700a76783SLorenzo Bianconi 70823020f04SLorenzo Bianconi done++; 709e12182ddSLorenzo Bianconi napi_gro_receive(&q->napi, q->skb); 710e12182ddSLorenzo Bianconi q->skb = NULL; 711e12182ddSLorenzo Bianconi continue; 712e12182ddSLorenzo Bianconi free_frag: 713d6d2b0e1SLorenzo Bianconi if (q->skb) { 714e12182ddSLorenzo Bianconi dev_kfree_skb(q->skb); 715e12182ddSLorenzo Bianconi q->skb = NULL; 716d6d2b0e1SLorenzo Bianconi } else { 717d6d2b0e1SLorenzo Bianconi page_pool_put_full_page(q->page_pool, page, true); 718d6d2b0e1SLorenzo Bianconi } 71923020f04SLorenzo Bianconi } 72023020f04SLorenzo Bianconi airoha_qdma_fill_rx_queue(q); 72123020f04SLorenzo Bianconi 72223020f04SLorenzo Bianconi return done; 72323020f04SLorenzo Bianconi } 72423020f04SLorenzo Bianconi 72523020f04SLorenzo Bianconi static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) 72623020f04SLorenzo Bianconi { 72723020f04SLorenzo Bianconi struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); 72823020f04SLorenzo Bianconi int cur, done = 0; 72923020f04SLorenzo Bianconi 73023020f04SLorenzo Bianconi do { 73123020f04SLorenzo Bianconi cur = airoha_qdma_rx_process(q, budget - done); 73223020f04SLorenzo Bianconi done += cur; 73323020f04SLorenzo Bianconi } while (cur && done < budget); 73423020f04SLorenzo Bianconi 735f252493eSLorenzo Bianconi if (done < budget && napi_complete(napi)) { 736f252493eSLorenzo Bianconi struct airoha_qdma *qdma = q->qdma; 737f252493eSLorenzo Bianconi int i, qid = q - &qdma->q_rx[0]; 738f252493eSLorenzo Bianconi int intr_reg = qid < RX_DONE_HIGH_OFFSET ? QDMA_INT_REG_IDX1 739f252493eSLorenzo Bianconi : QDMA_INT_REG_IDX2; 740f252493eSLorenzo Bianconi 741f252493eSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { 742f252493eSLorenzo Bianconi if (!(BIT(qid) & RX_IRQ_BANK_PIN_MASK(i))) 743f252493eSLorenzo Bianconi continue; 744f252493eSLorenzo Bianconi 745f252493eSLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[i], intr_reg, 746f252493eSLorenzo Bianconi BIT(qid % RX_DONE_HIGH_OFFSET)); 747f252493eSLorenzo Bianconi } 748f252493eSLorenzo Bianconi } 74923020f04SLorenzo Bianconi 75023020f04SLorenzo Bianconi return done; 75123020f04SLorenzo Bianconi } 75223020f04SLorenzo Bianconi 7539a2500abSLorenzo Bianconi static int airoha_qdma_init_rx_queue(struct airoha_queue *q, 75416874d1cSLorenzo Bianconi struct airoha_qdma *qdma, int ndesc) 75523020f04SLorenzo Bianconi { 75623020f04SLorenzo Bianconi const struct page_pool_params pp_params = { 75723020f04SLorenzo Bianconi .order = 0, 75823020f04SLorenzo Bianconi .pool_size = 256, 75923020f04SLorenzo Bianconi .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 76023020f04SLorenzo Bianconi .dma_dir = DMA_FROM_DEVICE, 76123020f04SLorenzo Bianconi .max_len = PAGE_SIZE, 76223020f04SLorenzo Bianconi .nid = NUMA_NO_NODE, 7639a2500abSLorenzo Bianconi .dev = qdma->eth->dev, 76423020f04SLorenzo Bianconi .napi = &q->napi, 76523020f04SLorenzo Bianconi }; 7669a2500abSLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 767245c7bc8SLorenzo Bianconi int qid = q - &qdma->q_rx[0], thr; 76823020f04SLorenzo Bianconi dma_addr_t dma_addr; 76923020f04SLorenzo Bianconi 77023020f04SLorenzo Bianconi q->buf_size = PAGE_SIZE / 2; 77123020f04SLorenzo Bianconi q->ndesc = ndesc; 7729a2500abSLorenzo Bianconi q->qdma = qdma; 77323020f04SLorenzo Bianconi 77423020f04SLorenzo Bianconi q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), 77523020f04SLorenzo Bianconi GFP_KERNEL); 77623020f04SLorenzo Bianconi if (!q->entry) 77723020f04SLorenzo Bianconi return -ENOMEM; 77823020f04SLorenzo Bianconi 77923020f04SLorenzo Bianconi q->page_pool = page_pool_create(&pp_params); 78023020f04SLorenzo Bianconi if (IS_ERR(q->page_pool)) { 78123020f04SLorenzo Bianconi int err = PTR_ERR(q->page_pool); 78223020f04SLorenzo Bianconi 78323020f04SLorenzo Bianconi q->page_pool = NULL; 78423020f04SLorenzo Bianconi return err; 78523020f04SLorenzo Bianconi } 78623020f04SLorenzo Bianconi 78723020f04SLorenzo Bianconi q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), 78823020f04SLorenzo Bianconi &dma_addr, GFP_KERNEL); 78923020f04SLorenzo Bianconi if (!q->desc) 79023020f04SLorenzo Bianconi return -ENOMEM; 79123020f04SLorenzo Bianconi 79223020f04SLorenzo Bianconi netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); 79323020f04SLorenzo Bianconi 79416874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); 79516874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), 79616874d1cSLorenzo Bianconi RX_RING_SIZE_MASK, 79723020f04SLorenzo Bianconi FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); 79823020f04SLorenzo Bianconi 79923020f04SLorenzo Bianconi thr = clamp(ndesc >> 3, 1, 32); 80016874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, 80123020f04SLorenzo Bianconi FIELD_PREP(RX_RING_THR_MASK, thr)); 80216874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, 80323020f04SLorenzo Bianconi FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); 804e12182ddSLorenzo Bianconi airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid), RX_RING_SG_EN_MASK); 80523020f04SLorenzo Bianconi 80623020f04SLorenzo Bianconi airoha_qdma_fill_rx_queue(q); 80723020f04SLorenzo Bianconi 80823020f04SLorenzo Bianconi return 0; 80923020f04SLorenzo Bianconi } 81023020f04SLorenzo Bianconi 81123020f04SLorenzo Bianconi static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) 81223020f04SLorenzo Bianconi { 8139a2500abSLorenzo Bianconi struct airoha_eth *eth = q->qdma->eth; 81423020f04SLorenzo Bianconi 81523020f04SLorenzo Bianconi while (q->queued) { 81623020f04SLorenzo Bianconi struct airoha_queue_entry *e = &q->entry[q->tail]; 81723020f04SLorenzo Bianconi struct page *page = virt_to_head_page(e->buf); 81823020f04SLorenzo Bianconi 81923020f04SLorenzo Bianconi dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, 8204e076ff6SLorenzo Bianconi page_pool_get_dma_dir(q->page_pool)); 82123020f04SLorenzo Bianconi page_pool_put_full_page(q->page_pool, page, false); 82223020f04SLorenzo Bianconi q->tail = (q->tail + 1) % q->ndesc; 82323020f04SLorenzo Bianconi q->queued--; 82423020f04SLorenzo Bianconi } 82523020f04SLorenzo Bianconi } 82623020f04SLorenzo Bianconi 8279a2500abSLorenzo Bianconi static int airoha_qdma_init_rx(struct airoha_qdma *qdma) 82823020f04SLorenzo Bianconi { 82923020f04SLorenzo Bianconi int i; 83023020f04SLorenzo Bianconi 831245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 83223020f04SLorenzo Bianconi int err; 83323020f04SLorenzo Bianconi 83423020f04SLorenzo Bianconi if (!(RX_DONE_INT_MASK & BIT(i))) { 83523020f04SLorenzo Bianconi /* rx-queue not binded to irq */ 83623020f04SLorenzo Bianconi continue; 83723020f04SLorenzo Bianconi } 83823020f04SLorenzo Bianconi 8399a2500abSLorenzo Bianconi err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma, 8409a2500abSLorenzo Bianconi RX_DSCP_NUM(i)); 84123020f04SLorenzo Bianconi if (err) 84223020f04SLorenzo Bianconi return err; 84323020f04SLorenzo Bianconi } 84423020f04SLorenzo Bianconi 84523020f04SLorenzo Bianconi return 0; 84623020f04SLorenzo Bianconi } 84723020f04SLorenzo Bianconi 84823020f04SLorenzo Bianconi static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) 84923020f04SLorenzo Bianconi { 85023020f04SLorenzo Bianconi struct airoha_tx_irq_queue *irq_q; 8513affa310SLorenzo Bianconi int id, done = 0, irq_queued; 85216874d1cSLorenzo Bianconi struct airoha_qdma *qdma; 85323020f04SLorenzo Bianconi struct airoha_eth *eth; 8543affa310SLorenzo Bianconi u32 status, head; 85523020f04SLorenzo Bianconi 85623020f04SLorenzo Bianconi irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); 8579a2500abSLorenzo Bianconi qdma = irq_q->qdma; 858245c7bc8SLorenzo Bianconi id = irq_q - &qdma->q_tx_irq[0]; 8599a2500abSLorenzo Bianconi eth = qdma->eth; 86023020f04SLorenzo Bianconi 8613affa310SLorenzo Bianconi status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(id)); 8623affa310SLorenzo Bianconi head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); 8633affa310SLorenzo Bianconi head = head % irq_q->size; 8643affa310SLorenzo Bianconi irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); 8653affa310SLorenzo Bianconi 8663affa310SLorenzo Bianconi while (irq_queued > 0 && done < budget) { 8670c729f53SLorenzo Bianconi u32 qid, val = irq_q->q[head]; 8680c729f53SLorenzo Bianconi struct airoha_qdma_desc *desc; 8690c729f53SLorenzo Bianconi struct airoha_queue_entry *e; 87023020f04SLorenzo Bianconi struct airoha_queue *q; 8710c729f53SLorenzo Bianconi u32 index, desc_ctrl; 8720c729f53SLorenzo Bianconi struct sk_buff *skb; 87323020f04SLorenzo Bianconi 87423020f04SLorenzo Bianconi if (val == 0xff) 87523020f04SLorenzo Bianconi break; 87623020f04SLorenzo Bianconi 8773affa310SLorenzo Bianconi irq_q->q[head] = 0xff; /* mark as done */ 8783affa310SLorenzo Bianconi head = (head + 1) % irq_q->size; 8793affa310SLorenzo Bianconi irq_queued--; 88023020f04SLorenzo Bianconi done++; 88123020f04SLorenzo Bianconi 88223020f04SLorenzo Bianconi qid = FIELD_GET(IRQ_RING_IDX_MASK, val); 883245c7bc8SLorenzo Bianconi if (qid >= ARRAY_SIZE(qdma->q_tx)) 88423020f04SLorenzo Bianconi continue; 88523020f04SLorenzo Bianconi 886245c7bc8SLorenzo Bianconi q = &qdma->q_tx[qid]; 88723020f04SLorenzo Bianconi if (!q->ndesc) 88823020f04SLorenzo Bianconi continue; 88923020f04SLorenzo Bianconi 8900c729f53SLorenzo Bianconi index = FIELD_GET(IRQ_DESC_IDX_MASK, val); 8910c729f53SLorenzo Bianconi if (index >= q->ndesc) 8920c729f53SLorenzo Bianconi continue; 8930c729f53SLorenzo Bianconi 89423020f04SLorenzo Bianconi spin_lock_bh(&q->lock); 89523020f04SLorenzo Bianconi 8960c729f53SLorenzo Bianconi if (!q->queued) 8970c729f53SLorenzo Bianconi goto unlock; 8980c729f53SLorenzo Bianconi 8990c729f53SLorenzo Bianconi desc = &q->desc[index]; 9000c729f53SLorenzo Bianconi desc_ctrl = le32_to_cpu(desc->ctrl); 90123020f04SLorenzo Bianconi 90223020f04SLorenzo Bianconi if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && 90323020f04SLorenzo Bianconi !(desc_ctrl & QDMA_DESC_DROP_MASK)) 9040c729f53SLorenzo Bianconi goto unlock; 90523020f04SLorenzo Bianconi 9060c729f53SLorenzo Bianconi e = &q->entry[index]; 9070c729f53SLorenzo Bianconi skb = e->skb; 90823020f04SLorenzo Bianconi 90923020f04SLorenzo Bianconi dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, 91023020f04SLorenzo Bianconi DMA_TO_DEVICE); 9110c729f53SLorenzo Bianconi memset(e, 0, sizeof(*e)); 91223020f04SLorenzo Bianconi WRITE_ONCE(desc->msg0, 0); 91323020f04SLorenzo Bianconi WRITE_ONCE(desc->msg1, 0); 9140c729f53SLorenzo Bianconi q->queued--; 9150c729f53SLorenzo Bianconi 9160c729f53SLorenzo Bianconi /* completion ring can report out-of-order indexes if hw QoS 9170c729f53SLorenzo Bianconi * is enabled and packets with different priority are queued 9180c729f53SLorenzo Bianconi * to same DMA ring. Take into account possible out-of-order 9190c729f53SLorenzo Bianconi * reports incrementing DMA ring tail pointer 9200c729f53SLorenzo Bianconi */ 9210c729f53SLorenzo Bianconi while (q->tail != q->head && !q->entry[q->tail].dma_addr) 9220c729f53SLorenzo Bianconi q->tail = (q->tail + 1) % q->ndesc; 92323020f04SLorenzo Bianconi 92423020f04SLorenzo Bianconi if (skb) { 9251d304174SLorenzo Bianconi u16 queue = skb_get_queue_mapping(skb); 92623020f04SLorenzo Bianconi struct netdev_queue *txq; 92723020f04SLorenzo Bianconi 9281d304174SLorenzo Bianconi txq = netdev_get_tx_queue(skb->dev, queue); 9291d304174SLorenzo Bianconi netdev_tx_completed_queue(txq, 1, skb->len); 93023020f04SLorenzo Bianconi if (netif_tx_queue_stopped(txq) && 93123020f04SLorenzo Bianconi q->ndesc - q->queued >= q->free_thr) 93223020f04SLorenzo Bianconi netif_tx_wake_queue(txq); 93323020f04SLorenzo Bianconi 93423020f04SLorenzo Bianconi dev_kfree_skb_any(skb); 93523020f04SLorenzo Bianconi } 9360c729f53SLorenzo Bianconi unlock: 93723020f04SLorenzo Bianconi spin_unlock_bh(&q->lock); 93823020f04SLorenzo Bianconi } 93923020f04SLorenzo Bianconi 94023020f04SLorenzo Bianconi if (done) { 94123020f04SLorenzo Bianconi int i, len = done >> 7; 94223020f04SLorenzo Bianconi 94323020f04SLorenzo Bianconi for (i = 0; i < len; i++) 94416874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), 94523020f04SLorenzo Bianconi IRQ_CLEAR_LEN_MASK, 0x80); 94616874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), 94723020f04SLorenzo Bianconi IRQ_CLEAR_LEN_MASK, (done & 0x7f)); 94823020f04SLorenzo Bianconi } 94923020f04SLorenzo Bianconi 95023020f04SLorenzo Bianconi if (done < budget && napi_complete(napi)) 9519439db26SLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, 95223020f04SLorenzo Bianconi TX_DONE_INT_MASK(id)); 95323020f04SLorenzo Bianconi 95423020f04SLorenzo Bianconi return done; 95523020f04SLorenzo Bianconi } 95623020f04SLorenzo Bianconi 9579a2500abSLorenzo Bianconi static int airoha_qdma_init_tx_queue(struct airoha_queue *q, 95816874d1cSLorenzo Bianconi struct airoha_qdma *qdma, int size) 95923020f04SLorenzo Bianconi { 9609a2500abSLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 961245c7bc8SLorenzo Bianconi int i, qid = q - &qdma->q_tx[0]; 96223020f04SLorenzo Bianconi dma_addr_t dma_addr; 96323020f04SLorenzo Bianconi 96423020f04SLorenzo Bianconi spin_lock_init(&q->lock); 96523020f04SLorenzo Bianconi q->ndesc = size; 9669a2500abSLorenzo Bianconi q->qdma = qdma; 96723020f04SLorenzo Bianconi q->free_thr = 1 + MAX_SKB_FRAGS; 96823020f04SLorenzo Bianconi 96923020f04SLorenzo Bianconi q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), 97023020f04SLorenzo Bianconi GFP_KERNEL); 97123020f04SLorenzo Bianconi if (!q->entry) 97223020f04SLorenzo Bianconi return -ENOMEM; 97323020f04SLorenzo Bianconi 97423020f04SLorenzo Bianconi q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), 97523020f04SLorenzo Bianconi &dma_addr, GFP_KERNEL); 97623020f04SLorenzo Bianconi if (!q->desc) 97723020f04SLorenzo Bianconi return -ENOMEM; 97823020f04SLorenzo Bianconi 97923020f04SLorenzo Bianconi for (i = 0; i < q->ndesc; i++) { 98023020f04SLorenzo Bianconi u32 val; 98123020f04SLorenzo Bianconi 98223020f04SLorenzo Bianconi val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); 98323020f04SLorenzo Bianconi WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); 98423020f04SLorenzo Bianconi } 98523020f04SLorenzo Bianconi 9865f795590SLorenzo Bianconi /* xmit ring drop default setting */ 9875f795590SLorenzo Bianconi airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), 9885f795590SLorenzo Bianconi TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK); 9895f795590SLorenzo Bianconi 99016874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); 99116874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, 99223020f04SLorenzo Bianconi FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); 99316874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, 99423020f04SLorenzo Bianconi FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); 99523020f04SLorenzo Bianconi 99623020f04SLorenzo Bianconi return 0; 99723020f04SLorenzo Bianconi } 99823020f04SLorenzo Bianconi 9999a2500abSLorenzo Bianconi static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, 100016874d1cSLorenzo Bianconi struct airoha_qdma *qdma, int size) 100123020f04SLorenzo Bianconi { 1002245c7bc8SLorenzo Bianconi int id = irq_q - &qdma->q_tx_irq[0]; 10039a2500abSLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 100423020f04SLorenzo Bianconi dma_addr_t dma_addr; 100523020f04SLorenzo Bianconi 100623020f04SLorenzo Bianconi netif_napi_add_tx(eth->napi_dev, &irq_q->napi, 100723020f04SLorenzo Bianconi airoha_qdma_tx_napi_poll); 100823020f04SLorenzo Bianconi irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), 100923020f04SLorenzo Bianconi &dma_addr, GFP_KERNEL); 101023020f04SLorenzo Bianconi if (!irq_q->q) 101123020f04SLorenzo Bianconi return -ENOMEM; 101223020f04SLorenzo Bianconi 101323020f04SLorenzo Bianconi memset(irq_q->q, 0xff, size * sizeof(u32)); 101423020f04SLorenzo Bianconi irq_q->size = size; 10159a2500abSLorenzo Bianconi irq_q->qdma = qdma; 101623020f04SLorenzo Bianconi 101716874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); 101816874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, 101923020f04SLorenzo Bianconi FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); 102016874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK, 102123020f04SLorenzo Bianconi FIELD_PREP(TX_IRQ_THR_MASK, 1)); 102223020f04SLorenzo Bianconi 102323020f04SLorenzo Bianconi return 0; 102423020f04SLorenzo Bianconi } 102523020f04SLorenzo Bianconi 10269a2500abSLorenzo Bianconi static int airoha_qdma_init_tx(struct airoha_qdma *qdma) 102723020f04SLorenzo Bianconi { 102823020f04SLorenzo Bianconi int i, err; 102923020f04SLorenzo Bianconi 1030245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { 10319a2500abSLorenzo Bianconi err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma, 10329a2500abSLorenzo Bianconi IRQ_QUEUE_LEN(i)); 103323020f04SLorenzo Bianconi if (err) 103423020f04SLorenzo Bianconi return err; 103523020f04SLorenzo Bianconi } 103623020f04SLorenzo Bianconi 1037245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { 10389a2500abSLorenzo Bianconi err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma, 10399a2500abSLorenzo Bianconi TX_DSCP_NUM); 104023020f04SLorenzo Bianconi if (err) 104123020f04SLorenzo Bianconi return err; 104223020f04SLorenzo Bianconi } 104323020f04SLorenzo Bianconi 104423020f04SLorenzo Bianconi return 0; 104523020f04SLorenzo Bianconi } 104623020f04SLorenzo Bianconi 104723020f04SLorenzo Bianconi static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) 104823020f04SLorenzo Bianconi { 10499a2500abSLorenzo Bianconi struct airoha_eth *eth = q->qdma->eth; 105023020f04SLorenzo Bianconi 105123020f04SLorenzo Bianconi spin_lock_bh(&q->lock); 105223020f04SLorenzo Bianconi while (q->queued) { 105323020f04SLorenzo Bianconi struct airoha_queue_entry *e = &q->entry[q->tail]; 105423020f04SLorenzo Bianconi 105523020f04SLorenzo Bianconi dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, 105623020f04SLorenzo Bianconi DMA_TO_DEVICE); 105723020f04SLorenzo Bianconi dev_kfree_skb_any(e->skb); 105823020f04SLorenzo Bianconi e->skb = NULL; 105923020f04SLorenzo Bianconi 106023020f04SLorenzo Bianconi q->tail = (q->tail + 1) % q->ndesc; 106123020f04SLorenzo Bianconi q->queued--; 106223020f04SLorenzo Bianconi } 106323020f04SLorenzo Bianconi spin_unlock_bh(&q->lock); 106423020f04SLorenzo Bianconi } 106523020f04SLorenzo Bianconi 10669a2500abSLorenzo Bianconi static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) 106723020f04SLorenzo Bianconi { 1068edf8afeeSLorenzo Bianconi int size, index, num_desc = HW_DSCP_NUM; 10699a2500abSLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 10703a1ce9e3SLorenzo Bianconi int id = qdma - ð->qdma[0]; 10717b46bdaeSLorenzo Bianconi u32 status, buf_size; 107223020f04SLorenzo Bianconi dma_addr_t dma_addr; 10733a1ce9e3SLorenzo Bianconi const char *name; 107423020f04SLorenzo Bianconi 10753a1ce9e3SLorenzo Bianconi name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); 10763a1ce9e3SLorenzo Bianconi if (!name) 107723020f04SLorenzo Bianconi return -ENOMEM; 107823020f04SLorenzo Bianconi 10797b46bdaeSLorenzo Bianconi buf_size = id ? AIROHA_MAX_PACKET_SIZE / 2 : AIROHA_MAX_PACKET_SIZE; 10803a1ce9e3SLorenzo Bianconi index = of_property_match_string(eth->dev->of_node, 10813a1ce9e3SLorenzo Bianconi "memory-region-names", name); 10823a1ce9e3SLorenzo Bianconi if (index >= 0) { 10833a1ce9e3SLorenzo Bianconi struct reserved_mem *rmem; 10843a1ce9e3SLorenzo Bianconi struct device_node *np; 10853a1ce9e3SLorenzo Bianconi 10863a1ce9e3SLorenzo Bianconi /* Consume reserved memory for hw forwarding buffers queue if 10873a1ce9e3SLorenzo Bianconi * available in the DTS 10883a1ce9e3SLorenzo Bianconi */ 10893a1ce9e3SLorenzo Bianconi np = of_parse_phandle(eth->dev->of_node, "memory-region", 10903a1ce9e3SLorenzo Bianconi index); 10913a1ce9e3SLorenzo Bianconi if (!np) 10923a1ce9e3SLorenzo Bianconi return -ENODEV; 10933a1ce9e3SLorenzo Bianconi 10943a1ce9e3SLorenzo Bianconi rmem = of_reserved_mem_lookup(np); 10953a1ce9e3SLorenzo Bianconi of_node_put(np); 10963a1ce9e3SLorenzo Bianconi dma_addr = rmem->base; 1097edf8afeeSLorenzo Bianconi /* Compute the number of hw descriptors according to the 1098edf8afeeSLorenzo Bianconi * reserved memory size and the payload buffer size 1099edf8afeeSLorenzo Bianconi */ 11007b46bdaeSLorenzo Bianconi num_desc = div_u64(rmem->size, buf_size); 11013a1ce9e3SLorenzo Bianconi } else { 11027b46bdaeSLorenzo Bianconi size = buf_size * num_desc; 11033a1ce9e3SLorenzo Bianconi if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, 11043a1ce9e3SLorenzo Bianconi GFP_KERNEL)) 11053a1ce9e3SLorenzo Bianconi return -ENOMEM; 11063a1ce9e3SLorenzo Bianconi } 11073a1ce9e3SLorenzo Bianconi 110816874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); 110923020f04SLorenzo Bianconi 1110edf8afeeSLorenzo Bianconi size = num_desc * sizeof(struct airoha_qdma_fwd_desc); 1111edf8afeeSLorenzo Bianconi if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) 1112edf8afeeSLorenzo Bianconi return -ENOMEM; 1113edf8afeeSLorenzo Bianconi 1114edf8afeeSLorenzo Bianconi airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); 11157b46bdaeSLorenzo Bianconi /* QDMA0: 2KB. QDMA1: 1KB */ 111616874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, 111723020f04SLorenzo Bianconi HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 11187b46bdaeSLorenzo Bianconi FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, !!id)); 111916874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, 112023020f04SLorenzo Bianconi FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); 112116874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, 112223020f04SLorenzo Bianconi LMGR_INIT_START | LMGR_SRAM_MODE_MASK | 112323020f04SLorenzo Bianconi HW_FWD_DESC_NUM_MASK, 1124edf8afeeSLorenzo Bianconi FIELD_PREP(HW_FWD_DESC_NUM_MASK, num_desc) | 1125c683e378SLorenzo Bianconi LMGR_INIT_START | LMGR_SRAM_MODE_MASK); 112623020f04SLorenzo Bianconi 112723020f04SLorenzo Bianconi return read_poll_timeout(airoha_qdma_rr, status, 112823020f04SLorenzo Bianconi !(status & LMGR_INIT_START), USEC_PER_MSEC, 112916874d1cSLorenzo Bianconi 30 * USEC_PER_MSEC, true, qdma, 113023020f04SLorenzo Bianconi REG_LMGR_INIT_CFG); 113123020f04SLorenzo Bianconi } 113223020f04SLorenzo Bianconi 11339a2500abSLorenzo Bianconi static void airoha_qdma_init_qos(struct airoha_qdma *qdma) 113423020f04SLorenzo Bianconi { 113516874d1cSLorenzo Bianconi airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); 113616874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); 113723020f04SLorenzo Bianconi 113816874d1cSLorenzo Bianconi airoha_qdma_clear(qdma, REG_PSE_BUF_USAGE_CFG, 113923020f04SLorenzo Bianconi PSE_BUF_ESTIMATE_EN_MASK); 114023020f04SLorenzo Bianconi 114116874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_EGRESS_RATE_METER_CFG, 114223020f04SLorenzo Bianconi EGRESS_RATE_METER_EN_MASK | 114323020f04SLorenzo Bianconi EGRESS_RATE_METER_EQ_RATE_EN_MASK); 114423020f04SLorenzo Bianconi /* 2047us x 31 = 63.457ms */ 114516874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, 114623020f04SLorenzo Bianconi EGRESS_RATE_METER_WINDOW_SZ_MASK, 114723020f04SLorenzo Bianconi FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f)); 114816874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, 114923020f04SLorenzo Bianconi EGRESS_RATE_METER_TIMESLICE_MASK, 115023020f04SLorenzo Bianconi FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff)); 115123020f04SLorenzo Bianconi 115223020f04SLorenzo Bianconi /* ratelimit init */ 115316874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK); 115423020f04SLorenzo Bianconi /* fast-tick 25us */ 115516874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK, 115623020f04SLorenzo Bianconi FIELD_PREP(GLB_FAST_TICK_MASK, 25)); 115716874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK, 115823020f04SLorenzo Bianconi FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40)); 115923020f04SLorenzo Bianconi 116016874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK); 116116874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK, 116223020f04SLorenzo Bianconi FIELD_PREP(EGRESS_FAST_TICK_MASK, 25)); 116316874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, 116423020f04SLorenzo Bianconi EGRESS_SLOW_TICK_RATIO_MASK, 116523020f04SLorenzo Bianconi FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40)); 116623020f04SLorenzo Bianconi 116716874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK); 116816874d1cSLorenzo Bianconi airoha_qdma_clear(qdma, REG_INGRESS_TRTCM_CFG, 116923020f04SLorenzo Bianconi INGRESS_TRTCM_MODE_MASK); 117016874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK, 117123020f04SLorenzo Bianconi FIELD_PREP(INGRESS_FAST_TICK_MASK, 125)); 117216874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, 117323020f04SLorenzo Bianconi INGRESS_SLOW_TICK_RATIO_MASK, 117423020f04SLorenzo Bianconi FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8)); 117523020f04SLorenzo Bianconi 117616874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK); 117716874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK, 117823020f04SLorenzo Bianconi FIELD_PREP(SLA_FAST_TICK_MASK, 25)); 117916874d1cSLorenzo Bianconi airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK, 118023020f04SLorenzo Bianconi FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); 118123020f04SLorenzo Bianconi } 118223020f04SLorenzo Bianconi 118320bf7d07SLorenzo Bianconi static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) 118420bf7d07SLorenzo Bianconi { 118520bf7d07SLorenzo Bianconi int i; 118620bf7d07SLorenzo Bianconi 118720bf7d07SLorenzo Bianconi for (i = 0; i < AIROHA_NUM_QOS_CHANNELS; i++) { 118820bf7d07SLorenzo Bianconi /* Tx-cpu transferred count */ 118920bf7d07SLorenzo Bianconi airoha_qdma_wr(qdma, REG_CNTR_VAL(i << 1), 0); 119020bf7d07SLorenzo Bianconi airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), 119120bf7d07SLorenzo Bianconi CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | 119220bf7d07SLorenzo Bianconi CNTR_ALL_DSCP_RING_EN_MASK | 119320bf7d07SLorenzo Bianconi FIELD_PREP(CNTR_CHAN_MASK, i)); 119420bf7d07SLorenzo Bianconi /* Tx-fwd transferred count */ 119520bf7d07SLorenzo Bianconi airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); 119620bf7d07SLorenzo Bianconi airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), 119720bf7d07SLorenzo Bianconi CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | 119820bf7d07SLorenzo Bianconi CNTR_ALL_DSCP_RING_EN_MASK | 119920bf7d07SLorenzo Bianconi FIELD_PREP(CNTR_SRC_MASK, 1) | 120020bf7d07SLorenzo Bianconi FIELD_PREP(CNTR_CHAN_MASK, i)); 120120bf7d07SLorenzo Bianconi } 120220bf7d07SLorenzo Bianconi } 120320bf7d07SLorenzo Bianconi 12049a2500abSLorenzo Bianconi static int airoha_qdma_hw_init(struct airoha_qdma *qdma) 120523020f04SLorenzo Bianconi { 120623020f04SLorenzo Bianconi int i; 120723020f04SLorenzo Bianconi 1208f252493eSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { 120923020f04SLorenzo Bianconi /* clear pending irqs */ 121016874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); 1211f252493eSLorenzo Bianconi /* setup rx irqs */ 1212f252493eSLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX0, 1213f252493eSLorenzo Bianconi INT_RX0_MASK(RX_IRQ_BANK_PIN_MASK(i))); 1214f252493eSLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX1, 1215f252493eSLorenzo Bianconi INT_RX1_MASK(RX_IRQ_BANK_PIN_MASK(i))); 1216f252493eSLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX2, 1217f252493eSLorenzo Bianconi INT_RX2_MASK(RX_IRQ_BANK_PIN_MASK(i))); 1218f252493eSLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX3, 1219f252493eSLorenzo Bianconi INT_RX3_MASK(RX_IRQ_BANK_PIN_MASK(i))); 1220f252493eSLorenzo Bianconi } 1221f252493eSLorenzo Bianconi /* setup tx irqs */ 12229439db26SLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, 1223f252493eSLorenzo Bianconi TX_COHERENT_LOW_INT_MASK | INT_TX_MASK); 12249439db26SLorenzo Bianconi airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX4, 1225f252493eSLorenzo Bianconi TX_COHERENT_HIGH_INT_MASK); 122623020f04SLorenzo Bianconi 122723020f04SLorenzo Bianconi /* setup irq binding */ 1228245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { 1229245c7bc8SLorenzo Bianconi if (!qdma->q_tx[i].ndesc) 123023020f04SLorenzo Bianconi continue; 123123020f04SLorenzo Bianconi 123223020f04SLorenzo Bianconi if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i)) 123316874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(i), 123423020f04SLorenzo Bianconi TX_RING_IRQ_BLOCKING_CFG_MASK); 123523020f04SLorenzo Bianconi else 123616874d1cSLorenzo Bianconi airoha_qdma_clear(qdma, REG_TX_RING_BLOCKING(i), 123723020f04SLorenzo Bianconi TX_RING_IRQ_BLOCKING_CFG_MASK); 123823020f04SLorenzo Bianconi } 123923020f04SLorenzo Bianconi 124016874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, 124123020f04SLorenzo Bianconi FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | 124223020f04SLorenzo Bianconi GLOBAL_CFG_CPU_TXR_RR_MASK | 124323020f04SLorenzo Bianconi GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | 124423020f04SLorenzo Bianconi GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK | 124523020f04SLorenzo Bianconi GLOBAL_CFG_MULTICAST_EN_MASK | 124623020f04SLorenzo Bianconi GLOBAL_CFG_IRQ0_EN_MASK | GLOBAL_CFG_IRQ1_EN_MASK | 124723020f04SLorenzo Bianconi GLOBAL_CFG_TX_WB_DONE_MASK | 124823020f04SLorenzo Bianconi FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2)); 124923020f04SLorenzo Bianconi 12509a2500abSLorenzo Bianconi airoha_qdma_init_qos(qdma); 125123020f04SLorenzo Bianconi 125223020f04SLorenzo Bianconi /* disable qdma rx delay interrupt */ 1253245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 1254245c7bc8SLorenzo Bianconi if (!qdma->q_rx[i].ndesc) 125523020f04SLorenzo Bianconi continue; 125623020f04SLorenzo Bianconi 125716874d1cSLorenzo Bianconi airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), 125823020f04SLorenzo Bianconi RX_DELAY_INT_MASK); 125923020f04SLorenzo Bianconi } 126023020f04SLorenzo Bianconi 126116874d1cSLorenzo Bianconi airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG, 126223020f04SLorenzo Bianconi TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN); 126320bf7d07SLorenzo Bianconi airoha_qdma_init_qos_stats(qdma); 126423020f04SLorenzo Bianconi 126523020f04SLorenzo Bianconi return 0; 126623020f04SLorenzo Bianconi } 126723020f04SLorenzo Bianconi 126823020f04SLorenzo Bianconi static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) 126923020f04SLorenzo Bianconi { 12709439db26SLorenzo Bianconi struct airoha_irq_bank *irq_bank = dev_instance; 12719439db26SLorenzo Bianconi struct airoha_qdma *qdma = irq_bank->qdma; 1272f252493eSLorenzo Bianconi u32 rx_intr_mask = 0, rx_intr1, rx_intr2; 12739439db26SLorenzo Bianconi u32 intr[ARRAY_SIZE(irq_bank->irqmask)]; 127423020f04SLorenzo Bianconi int i; 127523020f04SLorenzo Bianconi 12769439db26SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(intr); i++) { 127716874d1cSLorenzo Bianconi intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); 12789439db26SLorenzo Bianconi intr[i] &= irq_bank->irqmask[i]; 127916874d1cSLorenzo Bianconi airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); 128023020f04SLorenzo Bianconi } 128123020f04SLorenzo Bianconi 1282e3d6bfdfSLorenzo Bianconi if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) 128323020f04SLorenzo Bianconi return IRQ_NONE; 128423020f04SLorenzo Bianconi 1285f252493eSLorenzo Bianconi rx_intr1 = intr[1] & RX_DONE_LOW_INT_MASK; 1286f252493eSLorenzo Bianconi if (rx_intr1) { 1287f252493eSLorenzo Bianconi airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, rx_intr1); 1288f252493eSLorenzo Bianconi rx_intr_mask |= rx_intr1; 1289f252493eSLorenzo Bianconi } 129023020f04SLorenzo Bianconi 1291f252493eSLorenzo Bianconi rx_intr2 = intr[2] & RX_DONE_HIGH_INT_MASK; 1292f252493eSLorenzo Bianconi if (rx_intr2) { 1293f252493eSLorenzo Bianconi airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX2, rx_intr2); 1294f252493eSLorenzo Bianconi rx_intr_mask |= (rx_intr2 << 16); 1295f252493eSLorenzo Bianconi } 1296f252493eSLorenzo Bianconi 1297f252493eSLorenzo Bianconi for (i = 0; rx_intr_mask && i < ARRAY_SIZE(qdma->q_rx); i++) { 1298245c7bc8SLorenzo Bianconi if (!qdma->q_rx[i].ndesc) 129923020f04SLorenzo Bianconi continue; 130023020f04SLorenzo Bianconi 1301f252493eSLorenzo Bianconi if (rx_intr_mask & BIT(i)) 1302245c7bc8SLorenzo Bianconi napi_schedule(&qdma->q_rx[i].napi); 130323020f04SLorenzo Bianconi } 130423020f04SLorenzo Bianconi 130523020f04SLorenzo Bianconi if (intr[0] & INT_TX_MASK) { 1306245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { 130723020f04SLorenzo Bianconi if (!(intr[0] & TX_DONE_INT_MASK(i))) 130823020f04SLorenzo Bianconi continue; 130923020f04SLorenzo Bianconi 13109439db26SLorenzo Bianconi airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX0, 131123020f04SLorenzo Bianconi TX_DONE_INT_MASK(i)); 1312245c7bc8SLorenzo Bianconi napi_schedule(&qdma->q_tx_irq[i].napi); 131323020f04SLorenzo Bianconi } 131423020f04SLorenzo Bianconi } 131523020f04SLorenzo Bianconi 131623020f04SLorenzo Bianconi return IRQ_HANDLED; 131723020f04SLorenzo Bianconi } 131823020f04SLorenzo Bianconi 13199439db26SLorenzo Bianconi static int airoha_qdma_init_irq_banks(struct platform_device *pdev, 13209439db26SLorenzo Bianconi struct airoha_qdma *qdma) 13219439db26SLorenzo Bianconi { 13229439db26SLorenzo Bianconi struct airoha_eth *eth = qdma->eth; 13239439db26SLorenzo Bianconi int i, id = qdma - ð->qdma[0]; 13249439db26SLorenzo Bianconi 13259439db26SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { 13269439db26SLorenzo Bianconi struct airoha_irq_bank *irq_bank = &qdma->irq_banks[i]; 13279439db26SLorenzo Bianconi int err, irq_index = 4 * id + i; 13289439db26SLorenzo Bianconi const char *name; 13299439db26SLorenzo Bianconi 13309439db26SLorenzo Bianconi spin_lock_init(&irq_bank->irq_lock); 13319439db26SLorenzo Bianconi irq_bank->qdma = qdma; 13329439db26SLorenzo Bianconi 13339439db26SLorenzo Bianconi irq_bank->irq = platform_get_irq(pdev, irq_index); 13349439db26SLorenzo Bianconi if (irq_bank->irq < 0) 13359439db26SLorenzo Bianconi return irq_bank->irq; 13369439db26SLorenzo Bianconi 13379439db26SLorenzo Bianconi name = devm_kasprintf(eth->dev, GFP_KERNEL, 13389439db26SLorenzo Bianconi KBUILD_MODNAME ".%d", irq_index); 13399439db26SLorenzo Bianconi if (!name) 13409439db26SLorenzo Bianconi return -ENOMEM; 13419439db26SLorenzo Bianconi 13429439db26SLorenzo Bianconi err = devm_request_irq(eth->dev, irq_bank->irq, 13439439db26SLorenzo Bianconi airoha_irq_handler, IRQF_SHARED, name, 13449439db26SLorenzo Bianconi irq_bank); 13459439db26SLorenzo Bianconi if (err) 13469439db26SLorenzo Bianconi return err; 13479439db26SLorenzo Bianconi } 13489439db26SLorenzo Bianconi 13499439db26SLorenzo Bianconi return 0; 13509439db26SLorenzo Bianconi } 13519439db26SLorenzo Bianconi 135219e47fc2SLorenzo Bianconi static int airoha_qdma_init(struct platform_device *pdev, 1353e618447cSLorenzo Bianconi struct airoha_eth *eth, 1354e618447cSLorenzo Bianconi struct airoha_qdma *qdma) 135523020f04SLorenzo Bianconi { 1356e618447cSLorenzo Bianconi int err, id = qdma - ð->qdma[0]; 1357e618447cSLorenzo Bianconi const char *res; 135823020f04SLorenzo Bianconi 13599a2500abSLorenzo Bianconi qdma->eth = eth; 1360e618447cSLorenzo Bianconi res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id); 1361e618447cSLorenzo Bianconi if (!res) 1362e618447cSLorenzo Bianconi return -ENOMEM; 1363e618447cSLorenzo Bianconi 1364e618447cSLorenzo Bianconi qdma->regs = devm_platform_ioremap_resource_byname(pdev, res); 1365e618447cSLorenzo Bianconi if (IS_ERR(qdma->regs)) 1366e618447cSLorenzo Bianconi return dev_err_probe(eth->dev, PTR_ERR(qdma->regs), 1367e618447cSLorenzo Bianconi "failed to iomap qdma%d regs\n", id); 1368e618447cSLorenzo Bianconi 13699439db26SLorenzo Bianconi err = airoha_qdma_init_irq_banks(pdev, qdma); 137023020f04SLorenzo Bianconi if (err) 137123020f04SLorenzo Bianconi return err; 137223020f04SLorenzo Bianconi 13739a2500abSLorenzo Bianconi err = airoha_qdma_init_rx(qdma); 137423020f04SLorenzo Bianconi if (err) 137523020f04SLorenzo Bianconi return err; 137623020f04SLorenzo Bianconi 13779a2500abSLorenzo Bianconi err = airoha_qdma_init_tx(qdma); 137823020f04SLorenzo Bianconi if (err) 137923020f04SLorenzo Bianconi return err; 138023020f04SLorenzo Bianconi 13819a2500abSLorenzo Bianconi err = airoha_qdma_init_hfwd_queues(qdma); 138223020f04SLorenzo Bianconi if (err) 138323020f04SLorenzo Bianconi return err; 138423020f04SLorenzo Bianconi 1385e618447cSLorenzo Bianconi return airoha_qdma_hw_init(qdma); 138623020f04SLorenzo Bianconi } 138723020f04SLorenzo Bianconi 138819e47fc2SLorenzo Bianconi static int airoha_hw_init(struct platform_device *pdev, 138919e47fc2SLorenzo Bianconi struct airoha_eth *eth) 139023020f04SLorenzo Bianconi { 1391e618447cSLorenzo Bianconi int err, i; 139223020f04SLorenzo Bianconi 139323020f04SLorenzo Bianconi /* disable xsi */ 139463a796b4SLorenzo Bianconi err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), 139563a796b4SLorenzo Bianconi eth->xsi_rsts); 139663a796b4SLorenzo Bianconi if (err) 139763a796b4SLorenzo Bianconi return err; 139823020f04SLorenzo Bianconi 139963a796b4SLorenzo Bianconi err = reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts); 140063a796b4SLorenzo Bianconi if (err) 140163a796b4SLorenzo Bianconi return err; 140223020f04SLorenzo Bianconi 140363a796b4SLorenzo Bianconi msleep(20); 140463a796b4SLorenzo Bianconi err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); 140563a796b4SLorenzo Bianconi if (err) 140663a796b4SLorenzo Bianconi return err; 140763a796b4SLorenzo Bianconi 140863a796b4SLorenzo Bianconi msleep(20); 140923020f04SLorenzo Bianconi err = airoha_fe_init(eth); 141023020f04SLorenzo Bianconi if (err) 141123020f04SLorenzo Bianconi return err; 141223020f04SLorenzo Bianconi 1413e618447cSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { 1414e618447cSLorenzo Bianconi err = airoha_qdma_init(pdev, eth, ð->qdma[i]); 1415e618447cSLorenzo Bianconi if (err) 1416e618447cSLorenzo Bianconi return err; 141723020f04SLorenzo Bianconi } 141823020f04SLorenzo Bianconi 141900a76783SLorenzo Bianconi err = airoha_ppe_init(eth); 142000a76783SLorenzo Bianconi if (err) 142100a76783SLorenzo Bianconi return err; 142200a76783SLorenzo Bianconi 1423e618447cSLorenzo Bianconi set_bit(DEV_STATE_INITIALIZED, ð->state); 1424e618447cSLorenzo Bianconi 1425e618447cSLorenzo Bianconi return 0; 1426e618447cSLorenzo Bianconi } 1427e618447cSLorenzo Bianconi 1428e618447cSLorenzo Bianconi static void airoha_hw_cleanup(struct airoha_qdma *qdma) 142923020f04SLorenzo Bianconi { 143023020f04SLorenzo Bianconi int i; 143123020f04SLorenzo Bianconi 1432245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 1433245c7bc8SLorenzo Bianconi if (!qdma->q_rx[i].ndesc) 143423020f04SLorenzo Bianconi continue; 143523020f04SLorenzo Bianconi 1436245c7bc8SLorenzo Bianconi netif_napi_del(&qdma->q_rx[i].napi); 1437245c7bc8SLorenzo Bianconi airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]); 1438245c7bc8SLorenzo Bianconi if (qdma->q_rx[i].page_pool) 1439245c7bc8SLorenzo Bianconi page_pool_destroy(qdma->q_rx[i].page_pool); 144023020f04SLorenzo Bianconi } 144123020f04SLorenzo Bianconi 14420c7469eeSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) 1443245c7bc8SLorenzo Bianconi netif_napi_del(&qdma->q_tx_irq[i].napi); 144423020f04SLorenzo Bianconi 1445245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { 1446245c7bc8SLorenzo Bianconi if (!qdma->q_tx[i].ndesc) 144723020f04SLorenzo Bianconi continue; 144823020f04SLorenzo Bianconi 1449245c7bc8SLorenzo Bianconi airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); 145023020f04SLorenzo Bianconi } 145123020f04SLorenzo Bianconi } 145223020f04SLorenzo Bianconi 1453160231e3SLorenzo Bianconi static void airoha_qdma_start_napi(struct airoha_qdma *qdma) 145423020f04SLorenzo Bianconi { 145523020f04SLorenzo Bianconi int i; 145623020f04SLorenzo Bianconi 1457245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) 1458245c7bc8SLorenzo Bianconi napi_enable(&qdma->q_tx_irq[i].napi); 145923020f04SLorenzo Bianconi 1460245c7bc8SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 1461245c7bc8SLorenzo Bianconi if (!qdma->q_rx[i].ndesc) 146223020f04SLorenzo Bianconi continue; 146323020f04SLorenzo Bianconi 1464245c7bc8SLorenzo Bianconi napi_enable(&qdma->q_rx[i].napi); 146523020f04SLorenzo Bianconi } 146623020f04SLorenzo Bianconi } 146723020f04SLorenzo Bianconi 14680c7469eeSLorenzo Bianconi static void airoha_qdma_stop_napi(struct airoha_qdma *qdma) 14690c7469eeSLorenzo Bianconi { 14700c7469eeSLorenzo Bianconi int i; 14710c7469eeSLorenzo Bianconi 14720c7469eeSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) 14730c7469eeSLorenzo Bianconi napi_disable(&qdma->q_tx_irq[i].napi); 14740c7469eeSLorenzo Bianconi 14750c7469eeSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 14760c7469eeSLorenzo Bianconi if (!qdma->q_rx[i].ndesc) 14770c7469eeSLorenzo Bianconi continue; 14780c7469eeSLorenzo Bianconi 14790c7469eeSLorenzo Bianconi napi_disable(&qdma->q_rx[i].napi); 14800c7469eeSLorenzo Bianconi } 14810c7469eeSLorenzo Bianconi } 14820c7469eeSLorenzo Bianconi 148323020f04SLorenzo Bianconi static void airoha_update_hw_stats(struct airoha_gdm_port *port) 148423020f04SLorenzo Bianconi { 14859304640fSLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 148623020f04SLorenzo Bianconi u32 val, i = 0; 148723020f04SLorenzo Bianconi 148823020f04SLorenzo Bianconi spin_lock(&port->stats.lock); 148923020f04SLorenzo Bianconi u64_stats_update_begin(&port->stats.syncp); 149023020f04SLorenzo Bianconi 149123020f04SLorenzo Bianconi /* TX */ 149223020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); 149323020f04SLorenzo Bianconi port->stats.tx_ok_pkts += ((u64)val << 32); 149423020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); 149523020f04SLorenzo Bianconi port->stats.tx_ok_pkts += val; 149623020f04SLorenzo Bianconi 149723020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); 149823020f04SLorenzo Bianconi port->stats.tx_ok_bytes += ((u64)val << 32); 149923020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); 150023020f04SLorenzo Bianconi port->stats.tx_ok_bytes += val; 150123020f04SLorenzo Bianconi 150223020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); 150323020f04SLorenzo Bianconi port->stats.tx_drops += val; 150423020f04SLorenzo Bianconi 150523020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); 150623020f04SLorenzo Bianconi port->stats.tx_broadcast += val; 150723020f04SLorenzo Bianconi 150823020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); 150923020f04SLorenzo Bianconi port->stats.tx_multicast += val; 151023020f04SLorenzo Bianconi 151123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); 151223020f04SLorenzo Bianconi port->stats.tx_len[i] += val; 151323020f04SLorenzo Bianconi 151423020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); 151523020f04SLorenzo Bianconi port->stats.tx_len[i] += ((u64)val << 32); 151623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); 151723020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 151823020f04SLorenzo Bianconi 151923020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); 152023020f04SLorenzo Bianconi port->stats.tx_len[i] += ((u64)val << 32); 152123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); 152223020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 152323020f04SLorenzo Bianconi 152423020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); 152523020f04SLorenzo Bianconi port->stats.tx_len[i] += ((u64)val << 32); 152623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); 152723020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 152823020f04SLorenzo Bianconi 152923020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); 153023020f04SLorenzo Bianconi port->stats.tx_len[i] += ((u64)val << 32); 153123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); 153223020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 153323020f04SLorenzo Bianconi 153423020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); 153523020f04SLorenzo Bianconi port->stats.tx_len[i] += ((u64)val << 32); 153623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); 153723020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 153823020f04SLorenzo Bianconi 153923020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); 154023020f04SLorenzo Bianconi port->stats.tx_len[i] += ((u64)val << 32); 154123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); 154223020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 154323020f04SLorenzo Bianconi 154423020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); 154523020f04SLorenzo Bianconi port->stats.tx_len[i++] += val; 154623020f04SLorenzo Bianconi 154723020f04SLorenzo Bianconi /* RX */ 154823020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); 154923020f04SLorenzo Bianconi port->stats.rx_ok_pkts += ((u64)val << 32); 155023020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); 155123020f04SLorenzo Bianconi port->stats.rx_ok_pkts += val; 155223020f04SLorenzo Bianconi 155323020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); 155423020f04SLorenzo Bianconi port->stats.rx_ok_bytes += ((u64)val << 32); 155523020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); 155623020f04SLorenzo Bianconi port->stats.rx_ok_bytes += val; 155723020f04SLorenzo Bianconi 155823020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); 155923020f04SLorenzo Bianconi port->stats.rx_drops += val; 156023020f04SLorenzo Bianconi 156123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); 156223020f04SLorenzo Bianconi port->stats.rx_broadcast += val; 156323020f04SLorenzo Bianconi 156423020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); 156523020f04SLorenzo Bianconi port->stats.rx_multicast += val; 156623020f04SLorenzo Bianconi 156723020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); 156823020f04SLorenzo Bianconi port->stats.rx_errors += val; 156923020f04SLorenzo Bianconi 157023020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); 157123020f04SLorenzo Bianconi port->stats.rx_crc_error += val; 157223020f04SLorenzo Bianconi 157323020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); 157423020f04SLorenzo Bianconi port->stats.rx_over_errors += val; 157523020f04SLorenzo Bianconi 157623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); 157723020f04SLorenzo Bianconi port->stats.rx_fragment += val; 157823020f04SLorenzo Bianconi 157923020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); 158023020f04SLorenzo Bianconi port->stats.rx_jabber += val; 158123020f04SLorenzo Bianconi 158223020f04SLorenzo Bianconi i = 0; 158323020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); 158423020f04SLorenzo Bianconi port->stats.rx_len[i] += val; 158523020f04SLorenzo Bianconi 158623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); 158723020f04SLorenzo Bianconi port->stats.rx_len[i] += ((u64)val << 32); 158823020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); 158923020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 159023020f04SLorenzo Bianconi 159123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); 159223020f04SLorenzo Bianconi port->stats.rx_len[i] += ((u64)val << 32); 159323020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); 159423020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 159523020f04SLorenzo Bianconi 159623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); 159723020f04SLorenzo Bianconi port->stats.rx_len[i] += ((u64)val << 32); 159823020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); 159923020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 160023020f04SLorenzo Bianconi 160123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); 160223020f04SLorenzo Bianconi port->stats.rx_len[i] += ((u64)val << 32); 160323020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); 160423020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 160523020f04SLorenzo Bianconi 160623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); 160723020f04SLorenzo Bianconi port->stats.rx_len[i] += ((u64)val << 32); 160823020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); 160923020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 161023020f04SLorenzo Bianconi 161123020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); 161223020f04SLorenzo Bianconi port->stats.rx_len[i] += ((u64)val << 32); 161323020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); 161423020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 161523020f04SLorenzo Bianconi 161623020f04SLorenzo Bianconi val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); 161723020f04SLorenzo Bianconi port->stats.rx_len[i++] += val; 161823020f04SLorenzo Bianconi 161923020f04SLorenzo Bianconi /* reset mib counters */ 162023020f04SLorenzo Bianconi airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id), 162123020f04SLorenzo Bianconi FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); 162223020f04SLorenzo Bianconi 162323020f04SLorenzo Bianconi u64_stats_update_end(&port->stats.syncp); 162423020f04SLorenzo Bianconi spin_unlock(&port->stats.lock); 162523020f04SLorenzo Bianconi } 162623020f04SLorenzo Bianconi 162723020f04SLorenzo Bianconi static int airoha_dev_open(struct net_device *dev) 162823020f04SLorenzo Bianconi { 162954d989d5SLorenzo Bianconi int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN; 163023020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 16319304640fSLorenzo Bianconi struct airoha_qdma *qdma = port->qdma; 163223020f04SLorenzo Bianconi 163323020f04SLorenzo Bianconi netif_tx_start_all_queues(dev); 1634c28b8375SLorenzo Bianconi err = airoha_set_vip_for_gdm_port(port, true); 163523020f04SLorenzo Bianconi if (err) 163623020f04SLorenzo Bianconi return err; 163723020f04SLorenzo Bianconi 163823020f04SLorenzo Bianconi if (netdev_uses_dsa(dev)) 16399304640fSLorenzo Bianconi airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), 164023020f04SLorenzo Bianconi GDM_STAG_EN_MASK); 164123020f04SLorenzo Bianconi else 16429304640fSLorenzo Bianconi airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), 164323020f04SLorenzo Bianconi GDM_STAG_EN_MASK); 164423020f04SLorenzo Bianconi 164554d989d5SLorenzo Bianconi airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), 164654d989d5SLorenzo Bianconi GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, 164754d989d5SLorenzo Bianconi FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | 164854d989d5SLorenzo Bianconi FIELD_PREP(GDM_LONG_LEN_MASK, len)); 164954d989d5SLorenzo Bianconi 16509304640fSLorenzo Bianconi airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, 165116874d1cSLorenzo Bianconi GLOBAL_CFG_TX_DMA_EN_MASK | 165216874d1cSLorenzo Bianconi GLOBAL_CFG_RX_DMA_EN_MASK); 165380369686SLorenzo Bianconi atomic_inc(&qdma->users); 165423020f04SLorenzo Bianconi 165523020f04SLorenzo Bianconi return 0; 165623020f04SLorenzo Bianconi } 165723020f04SLorenzo Bianconi 165823020f04SLorenzo Bianconi static int airoha_dev_stop(struct net_device *dev) 165923020f04SLorenzo Bianconi { 166023020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 16619304640fSLorenzo Bianconi struct airoha_qdma *qdma = port->qdma; 1662c9f94776SLorenzo Bianconi int i, err; 166323020f04SLorenzo Bianconi 166423020f04SLorenzo Bianconi netif_tx_disable(dev); 1665c28b8375SLorenzo Bianconi err = airoha_set_vip_for_gdm_port(port, false); 166623020f04SLorenzo Bianconi if (err) 166723020f04SLorenzo Bianconi return err; 166823020f04SLorenzo Bianconi 166980369686SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) 167080369686SLorenzo Bianconi netdev_tx_reset_subqueue(dev, i); 167180369686SLorenzo Bianconi 167280369686SLorenzo Bianconi if (atomic_dec_and_test(&qdma->users)) { 16739304640fSLorenzo Bianconi airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, 167416874d1cSLorenzo Bianconi GLOBAL_CFG_TX_DMA_EN_MASK | 167516874d1cSLorenzo Bianconi GLOBAL_CFG_RX_DMA_EN_MASK); 167623020f04SLorenzo Bianconi 1677c9f94776SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { 1678c9f94776SLorenzo Bianconi if (!qdma->q_tx[i].ndesc) 1679c9f94776SLorenzo Bianconi continue; 1680c9f94776SLorenzo Bianconi 1681c9f94776SLorenzo Bianconi airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); 168280369686SLorenzo Bianconi } 1683c9f94776SLorenzo Bianconi } 1684c9f94776SLorenzo Bianconi 168523020f04SLorenzo Bianconi return 0; 168623020f04SLorenzo Bianconi } 168723020f04SLorenzo Bianconi 168823020f04SLorenzo Bianconi static int airoha_dev_set_macaddr(struct net_device *dev, void *p) 168923020f04SLorenzo Bianconi { 169023020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 169123020f04SLorenzo Bianconi int err; 169223020f04SLorenzo Bianconi 169323020f04SLorenzo Bianconi err = eth_mac_addr(dev, p); 169423020f04SLorenzo Bianconi if (err) 169523020f04SLorenzo Bianconi return err; 169623020f04SLorenzo Bianconi 1697812a2751SLorenzo Bianconi airoha_set_macaddr(port, dev->dev_addr); 169823020f04SLorenzo Bianconi 169923020f04SLorenzo Bianconi return 0; 170023020f04SLorenzo Bianconi } 170123020f04SLorenzo Bianconi 17029cd451d4SLorenzo Bianconi static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) 17039cd451d4SLorenzo Bianconi { 17049cd451d4SLorenzo Bianconi u32 pse_port = port->id == 3 ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4; 17059cd451d4SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 17069cd451d4SLorenzo Bianconi u32 chan = port->id == 3 ? 4 : 0; 17079cd451d4SLorenzo Bianconi 17089cd451d4SLorenzo Bianconi /* Forward the traffic to the proper GDM port */ 17099cd451d4SLorenzo Bianconi airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port); 17109cd451d4SLorenzo Bianconi airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC); 17119cd451d4SLorenzo Bianconi 17129cd451d4SLorenzo Bianconi /* Enable GDM2 loopback */ 17139cd451d4SLorenzo Bianconi airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff); 17149cd451d4SLorenzo Bianconi airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff); 17159cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2), 17169cd451d4SLorenzo Bianconi LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK, 17179cd451d4SLorenzo Bianconi FIELD_PREP(LPBK_CHAN_MASK, chan) | LPBK_EN_MASK); 17189cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, REG_GDM_LEN_CFG(2), 17199cd451d4SLorenzo Bianconi GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, 17209cd451d4SLorenzo Bianconi FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | 17219cd451d4SLorenzo Bianconi FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); 17229cd451d4SLorenzo Bianconi 17239cd451d4SLorenzo Bianconi /* Disable VIP and IFC for GDM2 */ 17249cd451d4SLorenzo Bianconi airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2)); 17259cd451d4SLorenzo Bianconi airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2)); 17269cd451d4SLorenzo Bianconi 17279cd451d4SLorenzo Bianconi if (port->id == 3) { 17289cd451d4SLorenzo Bianconi /* FIXME: handle XSI_PCE1_PORT */ 17299cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_WAN_PORT, 17309cd451d4SLorenzo Bianconi WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, 17319cd451d4SLorenzo Bianconi FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT)); 17329cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, 17339cd451d4SLorenzo Bianconi REG_SP_DFT_CPORT(HSGMII_LAN_PCIE0_SRCPORT >> 3), 17349cd451d4SLorenzo Bianconi SP_CPORT_PCIE0_MASK, 17359cd451d4SLorenzo Bianconi FIELD_PREP(SP_CPORT_PCIE0_MASK, 17369cd451d4SLorenzo Bianconi FE_PSE_PORT_CDM2)); 17379cd451d4SLorenzo Bianconi } else { 17389cd451d4SLorenzo Bianconi /* FIXME: handle XSI_USB_PORT */ 17399cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, 17409cd451d4SLorenzo Bianconi FC_ID_OF_SRC_PORT24_MASK, 17419cd451d4SLorenzo Bianconi FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2)); 17429cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, REG_FE_WAN_PORT, 17439cd451d4SLorenzo Bianconi WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, 17449cd451d4SLorenzo Bianconi FIELD_PREP(WAN0_MASK, HSGMII_LAN_ETH_SRCPORT)); 17459cd451d4SLorenzo Bianconi airoha_fe_rmw(eth, 17469cd451d4SLorenzo Bianconi REG_SP_DFT_CPORT(HSGMII_LAN_ETH_SRCPORT >> 3), 17479cd451d4SLorenzo Bianconi SP_CPORT_ETH_MASK, 17489cd451d4SLorenzo Bianconi FIELD_PREP(SP_CPORT_ETH_MASK, FE_PSE_PORT_CDM2)); 17499cd451d4SLorenzo Bianconi } 17509cd451d4SLorenzo Bianconi } 17519cd451d4SLorenzo Bianconi 175223020f04SLorenzo Bianconi static int airoha_dev_init(struct net_device *dev) 175323020f04SLorenzo Bianconi { 175423020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 175567fde5d5SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 17569cd451d4SLorenzo Bianconi u32 pse_port; 175723020f04SLorenzo Bianconi 1758812a2751SLorenzo Bianconi airoha_set_macaddr(port, dev->dev_addr); 17599cd451d4SLorenzo Bianconi 17609cd451d4SLorenzo Bianconi switch (port->id) { 17619cd451d4SLorenzo Bianconi case 3: 17629cd451d4SLorenzo Bianconi case 4: 17639cd451d4SLorenzo Bianconi /* If GDM2 is active we can't enable loopback */ 17649cd451d4SLorenzo Bianconi if (!eth->ports[1]) 17659cd451d4SLorenzo Bianconi airhoha_set_gdm2_loopback(port); 17669cd451d4SLorenzo Bianconi fallthrough; 17679cd451d4SLorenzo Bianconi case 2: 17689cd451d4SLorenzo Bianconi pse_port = FE_PSE_PORT_PPE2; 17699cd451d4SLorenzo Bianconi break; 17709cd451d4SLorenzo Bianconi default: 17719cd451d4SLorenzo Bianconi pse_port = FE_PSE_PORT_PPE1; 17729cd451d4SLorenzo Bianconi break; 17739cd451d4SLorenzo Bianconi } 17749cd451d4SLorenzo Bianconi 17759cd451d4SLorenzo Bianconi airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port); 177623020f04SLorenzo Bianconi 177723020f04SLorenzo Bianconi return 0; 177823020f04SLorenzo Bianconi } 177923020f04SLorenzo Bianconi 178023020f04SLorenzo Bianconi static void airoha_dev_get_stats64(struct net_device *dev, 178123020f04SLorenzo Bianconi struct rtnl_link_stats64 *storage) 178223020f04SLorenzo Bianconi { 178323020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 178423020f04SLorenzo Bianconi unsigned int start; 178523020f04SLorenzo Bianconi 178623020f04SLorenzo Bianconi airoha_update_hw_stats(port); 178723020f04SLorenzo Bianconi do { 178823020f04SLorenzo Bianconi start = u64_stats_fetch_begin(&port->stats.syncp); 178923020f04SLorenzo Bianconi storage->rx_packets = port->stats.rx_ok_pkts; 179023020f04SLorenzo Bianconi storage->tx_packets = port->stats.tx_ok_pkts; 179123020f04SLorenzo Bianconi storage->rx_bytes = port->stats.rx_ok_bytes; 179223020f04SLorenzo Bianconi storage->tx_bytes = port->stats.tx_ok_bytes; 179323020f04SLorenzo Bianconi storage->multicast = port->stats.rx_multicast; 179423020f04SLorenzo Bianconi storage->rx_errors = port->stats.rx_errors; 179523020f04SLorenzo Bianconi storage->rx_dropped = port->stats.rx_drops; 179623020f04SLorenzo Bianconi storage->tx_dropped = port->stats.tx_drops; 179723020f04SLorenzo Bianconi storage->rx_crc_errors = port->stats.rx_crc_error; 179823020f04SLorenzo Bianconi storage->rx_over_errors = port->stats.rx_over_errors; 179923020f04SLorenzo Bianconi } while (u64_stats_fetch_retry(&port->stats.syncp, start)); 180023020f04SLorenzo Bianconi } 180123020f04SLorenzo Bianconi 180203b1b69fSLorenzo Bianconi static int airoha_dev_change_mtu(struct net_device *dev, int mtu) 180303b1b69fSLorenzo Bianconi { 180403b1b69fSLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 180503b1b69fSLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 180603b1b69fSLorenzo Bianconi u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; 180703b1b69fSLorenzo Bianconi 180803b1b69fSLorenzo Bianconi airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), 180903b1b69fSLorenzo Bianconi GDM_LONG_LEN_MASK, 181003b1b69fSLorenzo Bianconi FIELD_PREP(GDM_LONG_LEN_MASK, len)); 181103b1b69fSLorenzo Bianconi WRITE_ONCE(dev->mtu, mtu); 181203b1b69fSLorenzo Bianconi 181303b1b69fSLorenzo Bianconi return 0; 181403b1b69fSLorenzo Bianconi } 181503b1b69fSLorenzo Bianconi 18162b288b81SLorenzo Bianconi static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, 18172b288b81SLorenzo Bianconi struct net_device *sb_dev) 18182b288b81SLorenzo Bianconi { 18192b288b81SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 18202b288b81SLorenzo Bianconi int queue, channel; 18212b288b81SLorenzo Bianconi 18222b288b81SLorenzo Bianconi /* For dsa device select QoS channel according to the dsa user port 18232b288b81SLorenzo Bianconi * index, rely on port id otherwise. Select QoS queue based on the 18242b288b81SLorenzo Bianconi * skb priority. 18252b288b81SLorenzo Bianconi */ 18262b288b81SLorenzo Bianconi channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; 18272b288b81SLorenzo Bianconi channel = channel % AIROHA_NUM_QOS_CHANNELS; 18282b288b81SLorenzo Bianconi queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ 18292b288b81SLorenzo Bianconi queue = channel * AIROHA_NUM_QOS_QUEUES + queue; 18302b288b81SLorenzo Bianconi 18312b288b81SLorenzo Bianconi return queue < dev->num_tx_queues ? queue : 0; 18322b288b81SLorenzo Bianconi } 18332b288b81SLorenzo Bianconi 1834af3cf757SLorenzo Bianconi static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev) 1835af3cf757SLorenzo Bianconi { 1836af3cf757SLorenzo Bianconi #if IS_ENABLED(CONFIG_NET_DSA) 1837af3cf757SLorenzo Bianconi struct ethhdr *ehdr; 1838af3cf757SLorenzo Bianconi u8 xmit_tpid; 1839af3cf757SLorenzo Bianconi u16 tag; 1840af3cf757SLorenzo Bianconi 1841af3cf757SLorenzo Bianconi if (!netdev_uses_dsa(dev)) 1842af3cf757SLorenzo Bianconi return 0; 1843af3cf757SLorenzo Bianconi 1844e368d2a1SLorenzo Bianconi if (dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) 1845af3cf757SLorenzo Bianconi return 0; 1846af3cf757SLorenzo Bianconi 1847af3cf757SLorenzo Bianconi if (skb_cow_head(skb, 0)) 1848af3cf757SLorenzo Bianconi return 0; 1849af3cf757SLorenzo Bianconi 1850af3cf757SLorenzo Bianconi ehdr = (struct ethhdr *)skb->data; 1851af3cf757SLorenzo Bianconi tag = be16_to_cpu(ehdr->h_proto); 1852af3cf757SLorenzo Bianconi xmit_tpid = tag >> 8; 1853af3cf757SLorenzo Bianconi 1854af3cf757SLorenzo Bianconi switch (xmit_tpid) { 1855af3cf757SLorenzo Bianconi case MTK_HDR_XMIT_TAGGED_TPID_8100: 1856af3cf757SLorenzo Bianconi ehdr->h_proto = cpu_to_be16(ETH_P_8021Q); 1857af3cf757SLorenzo Bianconi tag &= ~(MTK_HDR_XMIT_TAGGED_TPID_8100 << 8); 1858af3cf757SLorenzo Bianconi break; 1859af3cf757SLorenzo Bianconi case MTK_HDR_XMIT_TAGGED_TPID_88A8: 1860af3cf757SLorenzo Bianconi ehdr->h_proto = cpu_to_be16(ETH_P_8021AD); 1861af3cf757SLorenzo Bianconi tag &= ~(MTK_HDR_XMIT_TAGGED_TPID_88A8 << 8); 1862af3cf757SLorenzo Bianconi break; 1863af3cf757SLorenzo Bianconi default: 1864af3cf757SLorenzo Bianconi /* PPE module requires untagged DSA packets to work properly, 1865af3cf757SLorenzo Bianconi * so move DSA tag to DMA descriptor. 1866af3cf757SLorenzo Bianconi */ 1867af3cf757SLorenzo Bianconi memmove(skb->data + MTK_HDR_LEN, skb->data, 2 * ETH_ALEN); 1868af3cf757SLorenzo Bianconi __skb_pull(skb, MTK_HDR_LEN); 1869af3cf757SLorenzo Bianconi break; 1870af3cf757SLorenzo Bianconi } 1871af3cf757SLorenzo Bianconi 1872af3cf757SLorenzo Bianconi return tag; 1873af3cf757SLorenzo Bianconi #else 1874af3cf757SLorenzo Bianconi return 0; 1875af3cf757SLorenzo Bianconi #endif 1876af3cf757SLorenzo Bianconi } 1877af3cf757SLorenzo Bianconi 187823020f04SLorenzo Bianconi static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, 187923020f04SLorenzo Bianconi struct net_device *dev) 188023020f04SLorenzo Bianconi { 188123020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 18829304640fSLorenzo Bianconi struct airoha_qdma *qdma = port->qdma; 1883af3cf757SLorenzo Bianconi u32 nr_frags, tag, msg0, msg1, len; 188423020f04SLorenzo Bianconi struct netdev_queue *txq; 188523020f04SLorenzo Bianconi struct airoha_queue *q; 1886af3cf757SLorenzo Bianconi void *data; 18872b288b81SLorenzo Bianconi int i, qid; 188823020f04SLorenzo Bianconi u16 index; 188923020f04SLorenzo Bianconi u8 fport; 189023020f04SLorenzo Bianconi 18912b288b81SLorenzo Bianconi qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); 1892af3cf757SLorenzo Bianconi tag = airoha_get_dsa_tag(skb, dev); 1893af3cf757SLorenzo Bianconi 18942b288b81SLorenzo Bianconi msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, 18952b288b81SLorenzo Bianconi qid / AIROHA_NUM_QOS_QUEUES) | 18962b288b81SLorenzo Bianconi FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK, 1897af3cf757SLorenzo Bianconi qid % AIROHA_NUM_QOS_QUEUES) | 1898af3cf757SLorenzo Bianconi FIELD_PREP(QDMA_ETH_TXMSG_SP_TAG_MASK, tag); 189923020f04SLorenzo Bianconi if (skb->ip_summed == CHECKSUM_PARTIAL) 190023020f04SLorenzo Bianconi msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) | 190123020f04SLorenzo Bianconi FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) | 190223020f04SLorenzo Bianconi FIELD_PREP(QDMA_ETH_TXMSG_ICO_MASK, 1); 190323020f04SLorenzo Bianconi 190423020f04SLorenzo Bianconi /* TSO: fill MSS info in tcp checksum field */ 190523020f04SLorenzo Bianconi if (skb_is_gso(skb)) { 190623020f04SLorenzo Bianconi if (skb_cow_head(skb, 0)) 190723020f04SLorenzo Bianconi goto error; 190823020f04SLorenzo Bianconi 1909c6287e1aSLorenzo Bianconi if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | 1910c6287e1aSLorenzo Bianconi SKB_GSO_TCPV6)) { 1911c6287e1aSLorenzo Bianconi __be16 csum = cpu_to_be16(skb_shinfo(skb)->gso_size); 191223020f04SLorenzo Bianconi 191323020f04SLorenzo Bianconi tcp_hdr(skb)->check = (__force __sum16)csum; 191423020f04SLorenzo Bianconi msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1); 191523020f04SLorenzo Bianconi } 191623020f04SLorenzo Bianconi } 191723020f04SLorenzo Bianconi 191823020f04SLorenzo Bianconi fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; 191923020f04SLorenzo Bianconi msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | 192023020f04SLorenzo Bianconi FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); 192123020f04SLorenzo Bianconi 1922245c7bc8SLorenzo Bianconi q = &qdma->q_tx[qid]; 192323020f04SLorenzo Bianconi if (WARN_ON_ONCE(!q->ndesc)) 192423020f04SLorenzo Bianconi goto error; 192523020f04SLorenzo Bianconi 192623020f04SLorenzo Bianconi spin_lock_bh(&q->lock); 192723020f04SLorenzo Bianconi 192823020f04SLorenzo Bianconi txq = netdev_get_tx_queue(dev, qid); 1929af3cf757SLorenzo Bianconi nr_frags = 1 + skb_shinfo(skb)->nr_frags; 1930af3cf757SLorenzo Bianconi 193123020f04SLorenzo Bianconi if (q->queued + nr_frags > q->ndesc) { 193223020f04SLorenzo Bianconi /* not enough space in the queue */ 193323020f04SLorenzo Bianconi netif_tx_stop_queue(txq); 193423020f04SLorenzo Bianconi spin_unlock_bh(&q->lock); 193523020f04SLorenzo Bianconi return NETDEV_TX_BUSY; 193623020f04SLorenzo Bianconi } 193723020f04SLorenzo Bianconi 1938af3cf757SLorenzo Bianconi len = skb_headlen(skb); 1939af3cf757SLorenzo Bianconi data = skb->data; 194023020f04SLorenzo Bianconi index = q->head; 1941af3cf757SLorenzo Bianconi 194223020f04SLorenzo Bianconi for (i = 0; i < nr_frags; i++) { 194323020f04SLorenzo Bianconi struct airoha_qdma_desc *desc = &q->desc[index]; 194423020f04SLorenzo Bianconi struct airoha_queue_entry *e = &q->entry[index]; 1945c6287e1aSLorenzo Bianconi skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 194623020f04SLorenzo Bianconi dma_addr_t addr; 194723020f04SLorenzo Bianconi u32 val; 194823020f04SLorenzo Bianconi 194923020f04SLorenzo Bianconi addr = dma_map_single(dev->dev.parent, data, len, 195023020f04SLorenzo Bianconi DMA_TO_DEVICE); 195123020f04SLorenzo Bianconi if (unlikely(dma_mapping_error(dev->dev.parent, addr))) 195223020f04SLorenzo Bianconi goto error_unmap; 195323020f04SLorenzo Bianconi 195423020f04SLorenzo Bianconi index = (index + 1) % q->ndesc; 195523020f04SLorenzo Bianconi 195623020f04SLorenzo Bianconi val = FIELD_PREP(QDMA_DESC_LEN_MASK, len); 195723020f04SLorenzo Bianconi if (i < nr_frags - 1) 195823020f04SLorenzo Bianconi val |= FIELD_PREP(QDMA_DESC_MORE_MASK, 1); 195923020f04SLorenzo Bianconi WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); 196023020f04SLorenzo Bianconi WRITE_ONCE(desc->addr, cpu_to_le32(addr)); 196123020f04SLorenzo Bianconi val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index); 196223020f04SLorenzo Bianconi WRITE_ONCE(desc->data, cpu_to_le32(val)); 196323020f04SLorenzo Bianconi WRITE_ONCE(desc->msg0, cpu_to_le32(msg0)); 196423020f04SLorenzo Bianconi WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); 196523020f04SLorenzo Bianconi WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); 196623020f04SLorenzo Bianconi 196723020f04SLorenzo Bianconi e->skb = i ? NULL : skb; 196823020f04SLorenzo Bianconi e->dma_addr = addr; 196923020f04SLorenzo Bianconi e->dma_len = len; 197023020f04SLorenzo Bianconi 197123020f04SLorenzo Bianconi data = skb_frag_address(frag); 197223020f04SLorenzo Bianconi len = skb_frag_size(frag); 197323020f04SLorenzo Bianconi } 197423020f04SLorenzo Bianconi 197523020f04SLorenzo Bianconi q->head = index; 197623020f04SLorenzo Bianconi q->queued += i; 197723020f04SLorenzo Bianconi 197823020f04SLorenzo Bianconi skb_tx_timestamp(skb); 19791d304174SLorenzo Bianconi netdev_tx_sent_queue(txq, skb->len); 19801d304174SLorenzo Bianconi 19811d304174SLorenzo Bianconi if (netif_xmit_stopped(txq) || !netdev_xmit_more()) 19823dc6e998SLorenzo Bianconi airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), 19833dc6e998SLorenzo Bianconi TX_RING_CPU_IDX_MASK, 19843dc6e998SLorenzo Bianconi FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); 19853dc6e998SLorenzo Bianconi 198623020f04SLorenzo Bianconi if (q->ndesc - q->queued < q->free_thr) 198723020f04SLorenzo Bianconi netif_tx_stop_queue(txq); 198823020f04SLorenzo Bianconi 198923020f04SLorenzo Bianconi spin_unlock_bh(&q->lock); 199023020f04SLorenzo Bianconi 199123020f04SLorenzo Bianconi return NETDEV_TX_OK; 199223020f04SLorenzo Bianconi 199323020f04SLorenzo Bianconi error_unmap: 19941f038d58SLorenzo Bianconi for (i--; i >= 0; i--) { 19951f038d58SLorenzo Bianconi index = (q->head + i) % q->ndesc; 19961f038d58SLorenzo Bianconi dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr, 19971f038d58SLorenzo Bianconi q->entry[index].dma_len, DMA_TO_DEVICE); 19981f038d58SLorenzo Bianconi } 199923020f04SLorenzo Bianconi 200023020f04SLorenzo Bianconi spin_unlock_bh(&q->lock); 200123020f04SLorenzo Bianconi error: 200223020f04SLorenzo Bianconi dev_kfree_skb_any(skb); 200323020f04SLorenzo Bianconi dev->stats.tx_dropped++; 200423020f04SLorenzo Bianconi 200523020f04SLorenzo Bianconi return NETDEV_TX_OK; 200623020f04SLorenzo Bianconi } 200723020f04SLorenzo Bianconi 200823020f04SLorenzo Bianconi static void airoha_ethtool_get_drvinfo(struct net_device *dev, 200923020f04SLorenzo Bianconi struct ethtool_drvinfo *info) 201023020f04SLorenzo Bianconi { 201123020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 20129304640fSLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 201323020f04SLorenzo Bianconi 201423020f04SLorenzo Bianconi strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); 201523020f04SLorenzo Bianconi strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); 201623020f04SLorenzo Bianconi } 201723020f04SLorenzo Bianconi 201823020f04SLorenzo Bianconi static void airoha_ethtool_get_mac_stats(struct net_device *dev, 201923020f04SLorenzo Bianconi struct ethtool_eth_mac_stats *stats) 202023020f04SLorenzo Bianconi { 202123020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 202223020f04SLorenzo Bianconi unsigned int start; 202323020f04SLorenzo Bianconi 202423020f04SLorenzo Bianconi airoha_update_hw_stats(port); 202523020f04SLorenzo Bianconi do { 202623020f04SLorenzo Bianconi start = u64_stats_fetch_begin(&port->stats.syncp); 202723020f04SLorenzo Bianconi stats->MulticastFramesXmittedOK = port->stats.tx_multicast; 202823020f04SLorenzo Bianconi stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; 202923020f04SLorenzo Bianconi stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; 203023020f04SLorenzo Bianconi } while (u64_stats_fetch_retry(&port->stats.syncp, start)); 203123020f04SLorenzo Bianconi } 203223020f04SLorenzo Bianconi 203323020f04SLorenzo Bianconi static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { 203423020f04SLorenzo Bianconi { 0, 64 }, 203523020f04SLorenzo Bianconi { 65, 127 }, 203623020f04SLorenzo Bianconi { 128, 255 }, 203723020f04SLorenzo Bianconi { 256, 511 }, 203823020f04SLorenzo Bianconi { 512, 1023 }, 203923020f04SLorenzo Bianconi { 1024, 1518 }, 204023020f04SLorenzo Bianconi { 1519, 10239 }, 204123020f04SLorenzo Bianconi {}, 204223020f04SLorenzo Bianconi }; 204323020f04SLorenzo Bianconi 204423020f04SLorenzo Bianconi static void 204523020f04SLorenzo Bianconi airoha_ethtool_get_rmon_stats(struct net_device *dev, 204623020f04SLorenzo Bianconi struct ethtool_rmon_stats *stats, 204723020f04SLorenzo Bianconi const struct ethtool_rmon_hist_range **ranges) 204823020f04SLorenzo Bianconi { 204923020f04SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 205023020f04SLorenzo Bianconi struct airoha_hw_stats *hw_stats = &port->stats; 205123020f04SLorenzo Bianconi unsigned int start; 205223020f04SLorenzo Bianconi 205323020f04SLorenzo Bianconi BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != 205423020f04SLorenzo Bianconi ARRAY_SIZE(hw_stats->tx_len) + 1); 205523020f04SLorenzo Bianconi BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != 205623020f04SLorenzo Bianconi ARRAY_SIZE(hw_stats->rx_len) + 1); 205723020f04SLorenzo Bianconi 205823020f04SLorenzo Bianconi *ranges = airoha_ethtool_rmon_ranges; 205923020f04SLorenzo Bianconi airoha_update_hw_stats(port); 206023020f04SLorenzo Bianconi do { 206123020f04SLorenzo Bianconi int i; 206223020f04SLorenzo Bianconi 206323020f04SLorenzo Bianconi start = u64_stats_fetch_begin(&port->stats.syncp); 206423020f04SLorenzo Bianconi stats->fragments = hw_stats->rx_fragment; 206523020f04SLorenzo Bianconi stats->jabbers = hw_stats->rx_jabber; 206623020f04SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1; 206723020f04SLorenzo Bianconi i++) { 206823020f04SLorenzo Bianconi stats->hist[i] = hw_stats->rx_len[i]; 206923020f04SLorenzo Bianconi stats->hist_tx[i] = hw_stats->tx_len[i]; 207023020f04SLorenzo Bianconi } 207123020f04SLorenzo Bianconi } while (u64_stats_fetch_retry(&port->stats.syncp, start)); 207223020f04SLorenzo Bianconi } 207323020f04SLorenzo Bianconi 207420bf7d07SLorenzo Bianconi static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port, 207520bf7d07SLorenzo Bianconi int channel, enum tx_sched_mode mode, 207620bf7d07SLorenzo Bianconi const u16 *weights, u8 n_weights) 207720bf7d07SLorenzo Bianconi { 207820bf7d07SLorenzo Bianconi int i; 207920bf7d07SLorenzo Bianconi 208020bf7d07SLorenzo Bianconi for (i = 0; i < AIROHA_NUM_TX_RING; i++) 208120bf7d07SLorenzo Bianconi airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel), 208220bf7d07SLorenzo Bianconi TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); 208320bf7d07SLorenzo Bianconi 208420bf7d07SLorenzo Bianconi for (i = 0; i < n_weights; i++) { 208520bf7d07SLorenzo Bianconi u32 status; 208620bf7d07SLorenzo Bianconi int err; 208720bf7d07SLorenzo Bianconi 208820bf7d07SLorenzo Bianconi airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG, 208920bf7d07SLorenzo Bianconi TWRR_RW_CMD_MASK | 209020bf7d07SLorenzo Bianconi FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | 209120bf7d07SLorenzo Bianconi FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | 209220bf7d07SLorenzo Bianconi FIELD_PREP(TWRR_VALUE_MASK, weights[i])); 209320bf7d07SLorenzo Bianconi err = read_poll_timeout(airoha_qdma_rr, status, 209420bf7d07SLorenzo Bianconi status & TWRR_RW_CMD_DONE, 209520bf7d07SLorenzo Bianconi USEC_PER_MSEC, 10 * USEC_PER_MSEC, 209620bf7d07SLorenzo Bianconi true, port->qdma, 209720bf7d07SLorenzo Bianconi REG_TXWRR_WEIGHT_CFG); 209820bf7d07SLorenzo Bianconi if (err) 209920bf7d07SLorenzo Bianconi return err; 210020bf7d07SLorenzo Bianconi } 210120bf7d07SLorenzo Bianconi 210220bf7d07SLorenzo Bianconi airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), 210320bf7d07SLorenzo Bianconi CHAN_QOS_MODE_MASK(channel), 210420bf7d07SLorenzo Bianconi mode << __ffs(CHAN_QOS_MODE_MASK(channel))); 210520bf7d07SLorenzo Bianconi 210620bf7d07SLorenzo Bianconi return 0; 210720bf7d07SLorenzo Bianconi } 210820bf7d07SLorenzo Bianconi 210920bf7d07SLorenzo Bianconi static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port, 211020bf7d07SLorenzo Bianconi int channel) 211120bf7d07SLorenzo Bianconi { 211220bf7d07SLorenzo Bianconi static const u16 w[AIROHA_NUM_QOS_QUEUES] = {}; 211320bf7d07SLorenzo Bianconi 211420bf7d07SLorenzo Bianconi return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w, 211520bf7d07SLorenzo Bianconi ARRAY_SIZE(w)); 211620bf7d07SLorenzo Bianconi } 211720bf7d07SLorenzo Bianconi 211820bf7d07SLorenzo Bianconi static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port, 211920bf7d07SLorenzo Bianconi int channel, 212020bf7d07SLorenzo Bianconi struct tc_ets_qopt_offload *opt) 212120bf7d07SLorenzo Bianconi { 212220bf7d07SLorenzo Bianconi struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; 212320bf7d07SLorenzo Bianconi enum tx_sched_mode mode = TC_SCH_SP; 212420bf7d07SLorenzo Bianconi u16 w[AIROHA_NUM_QOS_QUEUES] = {}; 212536757927SLorenzo Bianconi int i, nstrict = 0; 212620bf7d07SLorenzo Bianconi 212720bf7d07SLorenzo Bianconi if (p->bands > AIROHA_NUM_QOS_QUEUES) 212820bf7d07SLorenzo Bianconi return -EINVAL; 212920bf7d07SLorenzo Bianconi 213020bf7d07SLorenzo Bianconi for (i = 0; i < p->bands; i++) { 213120bf7d07SLorenzo Bianconi if (!p->quanta[i]) 213220bf7d07SLorenzo Bianconi nstrict++; 213320bf7d07SLorenzo Bianconi } 213420bf7d07SLorenzo Bianconi 213520bf7d07SLorenzo Bianconi /* this configuration is not supported by the hw */ 213620bf7d07SLorenzo Bianconi if (nstrict == AIROHA_NUM_QOS_QUEUES - 1) 213720bf7d07SLorenzo Bianconi return -EINVAL; 213820bf7d07SLorenzo Bianconi 2139b56e4d66SLorenzo Bianconi /* EN7581 SoC supports fixed QoS band priority where WRR queues have 2140b56e4d66SLorenzo Bianconi * lowest priorities with respect to SP ones. 2141b56e4d66SLorenzo Bianconi * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn 2142b56e4d66SLorenzo Bianconi */ 214336757927SLorenzo Bianconi for (i = 0; i < nstrict; i++) { 214436757927SLorenzo Bianconi if (p->priomap[p->bands - i - 1] != i) 2145b56e4d66SLorenzo Bianconi return -EINVAL; 2146b56e4d66SLorenzo Bianconi } 2147b56e4d66SLorenzo Bianconi 214836757927SLorenzo Bianconi for (i = 0; i < p->bands - nstrict; i++) { 214936757927SLorenzo Bianconi if (p->priomap[i] != nstrict + i) 215036757927SLorenzo Bianconi return -EINVAL; 215136757927SLorenzo Bianconi 215220bf7d07SLorenzo Bianconi w[i] = p->weights[nstrict + i]; 215336757927SLorenzo Bianconi } 215420bf7d07SLorenzo Bianconi 215520bf7d07SLorenzo Bianconi if (!nstrict) 215620bf7d07SLorenzo Bianconi mode = TC_SCH_WRR8; 215720bf7d07SLorenzo Bianconi else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1) 215820bf7d07SLorenzo Bianconi mode = nstrict + 1; 215920bf7d07SLorenzo Bianconi 216020bf7d07SLorenzo Bianconi return airoha_qdma_set_chan_tx_sched(port, channel, mode, w, 216120bf7d07SLorenzo Bianconi ARRAY_SIZE(w)); 216220bf7d07SLorenzo Bianconi } 216320bf7d07SLorenzo Bianconi 216420bf7d07SLorenzo Bianconi static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port, 216520bf7d07SLorenzo Bianconi int channel, 216620bf7d07SLorenzo Bianconi struct tc_ets_qopt_offload *opt) 216720bf7d07SLorenzo Bianconi { 216820bf7d07SLorenzo Bianconi u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, 216920bf7d07SLorenzo Bianconi REG_CNTR_VAL(channel << 1)); 217020bf7d07SLorenzo Bianconi u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, 217120bf7d07SLorenzo Bianconi REG_CNTR_VAL((channel << 1) + 1)); 217220bf7d07SLorenzo Bianconi u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + 217320bf7d07SLorenzo Bianconi (fwd_tx_packets - port->fwd_tx_packets); 217420bf7d07SLorenzo Bianconi _bstats_update(opt->stats.bstats, 0, tx_packets); 217520bf7d07SLorenzo Bianconi 217620bf7d07SLorenzo Bianconi port->cpu_tx_packets = cpu_tx_packets; 217720bf7d07SLorenzo Bianconi port->fwd_tx_packets = fwd_tx_packets; 217820bf7d07SLorenzo Bianconi 217920bf7d07SLorenzo Bianconi return 0; 218020bf7d07SLorenzo Bianconi } 218120bf7d07SLorenzo Bianconi 218220bf7d07SLorenzo Bianconi static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, 218320bf7d07SLorenzo Bianconi struct tc_ets_qopt_offload *opt) 218420bf7d07SLorenzo Bianconi { 21857d0da8f8SLorenzo Bianconi int channel; 218620bf7d07SLorenzo Bianconi 218720bf7d07SLorenzo Bianconi if (opt->parent == TC_H_ROOT) 218820bf7d07SLorenzo Bianconi return -EINVAL; 218920bf7d07SLorenzo Bianconi 21907d0da8f8SLorenzo Bianconi channel = TC_H_MAJ(opt->handle) >> 16; 21917d0da8f8SLorenzo Bianconi channel = channel % AIROHA_NUM_QOS_CHANNELS; 21927d0da8f8SLorenzo Bianconi 219320bf7d07SLorenzo Bianconi switch (opt->command) { 219420bf7d07SLorenzo Bianconi case TC_ETS_REPLACE: 219520bf7d07SLorenzo Bianconi return airoha_qdma_set_tx_ets_sched(port, channel, opt); 219620bf7d07SLorenzo Bianconi case TC_ETS_DESTROY: 219720bf7d07SLorenzo Bianconi /* PRIO is default qdisc scheduler */ 219820bf7d07SLorenzo Bianconi return airoha_qdma_set_tx_prio_sched(port, channel); 219920bf7d07SLorenzo Bianconi case TC_ETS_STATS: 220020bf7d07SLorenzo Bianconi return airoha_qdma_get_tx_ets_stats(port, channel, opt); 220120bf7d07SLorenzo Bianconi default: 220220bf7d07SLorenzo Bianconi return -EOPNOTSUPP; 220320bf7d07SLorenzo Bianconi } 220420bf7d07SLorenzo Bianconi } 220520bf7d07SLorenzo Bianconi 2206df8398fbSLorenzo Bianconi static int airoha_qdma_get_rl_param(struct airoha_qdma *qdma, int queue_id, 2207df8398fbSLorenzo Bianconi u32 addr, enum trtcm_param_type param, 2208df8398fbSLorenzo Bianconi u32 *val_low, u32 *val_high) 2209df8398fbSLorenzo Bianconi { 2210df8398fbSLorenzo Bianconi u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); 2211df8398fbSLorenzo Bianconi u32 val, config = FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | 2212df8398fbSLorenzo Bianconi FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | 2213df8398fbSLorenzo Bianconi FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); 2214df8398fbSLorenzo Bianconi 2215df8398fbSLorenzo Bianconi airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); 2216df8398fbSLorenzo Bianconi if (read_poll_timeout(airoha_qdma_rr, val, 2217df8398fbSLorenzo Bianconi val & RATE_LIMIT_PARAM_RW_DONE_MASK, 2218df8398fbSLorenzo Bianconi USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, qdma, 2219df8398fbSLorenzo Bianconi REG_TRTCM_CFG_PARAM(addr))) 2220df8398fbSLorenzo Bianconi return -ETIMEDOUT; 2221df8398fbSLorenzo Bianconi 2222df8398fbSLorenzo Bianconi *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); 2223df8398fbSLorenzo Bianconi if (val_high) 2224df8398fbSLorenzo Bianconi *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); 2225df8398fbSLorenzo Bianconi 2226df8398fbSLorenzo Bianconi return 0; 2227df8398fbSLorenzo Bianconi } 2228df8398fbSLorenzo Bianconi 2229df8398fbSLorenzo Bianconi static int airoha_qdma_set_rl_param(struct airoha_qdma *qdma, int queue_id, 2230df8398fbSLorenzo Bianconi u32 addr, enum trtcm_param_type param, 2231df8398fbSLorenzo Bianconi u32 val) 2232df8398fbSLorenzo Bianconi { 2233df8398fbSLorenzo Bianconi u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); 2234df8398fbSLorenzo Bianconi u32 config = RATE_LIMIT_PARAM_RW_MASK | 2235df8398fbSLorenzo Bianconi FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | 2236df8398fbSLorenzo Bianconi FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | 2237df8398fbSLorenzo Bianconi FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); 2238df8398fbSLorenzo Bianconi 2239df8398fbSLorenzo Bianconi airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); 2240df8398fbSLorenzo Bianconi airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); 2241df8398fbSLorenzo Bianconi 2242df8398fbSLorenzo Bianconi return read_poll_timeout(airoha_qdma_rr, val, 2243df8398fbSLorenzo Bianconi val & RATE_LIMIT_PARAM_RW_DONE_MASK, 2244df8398fbSLorenzo Bianconi USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, 2245df8398fbSLorenzo Bianconi qdma, REG_TRTCM_CFG_PARAM(addr)); 2246df8398fbSLorenzo Bianconi } 2247df8398fbSLorenzo Bianconi 2248df8398fbSLorenzo Bianconi static int airoha_qdma_set_rl_config(struct airoha_qdma *qdma, int queue_id, 2249df8398fbSLorenzo Bianconi u32 addr, bool enable, u32 enable_mask) 2250df8398fbSLorenzo Bianconi { 2251df8398fbSLorenzo Bianconi u32 val; 2252df8398fbSLorenzo Bianconi int err; 2253df8398fbSLorenzo Bianconi 2254df8398fbSLorenzo Bianconi err = airoha_qdma_get_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, 2255df8398fbSLorenzo Bianconi &val, NULL); 2256df8398fbSLorenzo Bianconi if (err) 2257df8398fbSLorenzo Bianconi return err; 2258df8398fbSLorenzo Bianconi 2259df8398fbSLorenzo Bianconi val = enable ? val | enable_mask : val & ~enable_mask; 2260df8398fbSLorenzo Bianconi 2261df8398fbSLorenzo Bianconi return airoha_qdma_set_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, 2262df8398fbSLorenzo Bianconi val); 2263df8398fbSLorenzo Bianconi } 2264df8398fbSLorenzo Bianconi 2265df8398fbSLorenzo Bianconi static int airoha_qdma_set_rl_token_bucket(struct airoha_qdma *qdma, 2266df8398fbSLorenzo Bianconi int queue_id, u32 rate_val, 2267df8398fbSLorenzo Bianconi u32 bucket_size) 2268df8398fbSLorenzo Bianconi { 2269df8398fbSLorenzo Bianconi u32 val, config, tick, unit, rate, rate_frac; 2270df8398fbSLorenzo Bianconi int err; 2271df8398fbSLorenzo Bianconi 2272df8398fbSLorenzo Bianconi err = airoha_qdma_get_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2273df8398fbSLorenzo Bianconi TRTCM_MISC_MODE, &config, NULL); 2274df8398fbSLorenzo Bianconi if (err) 2275df8398fbSLorenzo Bianconi return err; 2276df8398fbSLorenzo Bianconi 2277df8398fbSLorenzo Bianconi val = airoha_qdma_rr(qdma, REG_INGRESS_TRTCM_CFG); 2278df8398fbSLorenzo Bianconi tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); 2279df8398fbSLorenzo Bianconi if (config & TRTCM_TICK_SEL) 2280df8398fbSLorenzo Bianconi tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); 2281df8398fbSLorenzo Bianconi if (!tick) 2282df8398fbSLorenzo Bianconi return -EINVAL; 2283df8398fbSLorenzo Bianconi 2284df8398fbSLorenzo Bianconi unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; 2285df8398fbSLorenzo Bianconi if (!unit) 2286df8398fbSLorenzo Bianconi return -EINVAL; 2287df8398fbSLorenzo Bianconi 2288df8398fbSLorenzo Bianconi rate = rate_val / unit; 2289df8398fbSLorenzo Bianconi rate_frac = rate_val % unit; 2290df8398fbSLorenzo Bianconi rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; 2291df8398fbSLorenzo Bianconi rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | 2292df8398fbSLorenzo Bianconi FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); 2293df8398fbSLorenzo Bianconi 2294df8398fbSLorenzo Bianconi err = airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2295df8398fbSLorenzo Bianconi TRTCM_TOKEN_RATE_MODE, rate); 2296df8398fbSLorenzo Bianconi if (err) 2297df8398fbSLorenzo Bianconi return err; 2298df8398fbSLorenzo Bianconi 2299df8398fbSLorenzo Bianconi val = bucket_size; 2300df8398fbSLorenzo Bianconi if (!(config & TRTCM_PKT_MODE)) 2301df8398fbSLorenzo Bianconi val = max_t(u32, val, MIN_TOKEN_SIZE); 2302df8398fbSLorenzo Bianconi val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); 2303df8398fbSLorenzo Bianconi 2304df8398fbSLorenzo Bianconi return airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2305df8398fbSLorenzo Bianconi TRTCM_BUCKETSIZE_SHIFT_MODE, val); 2306df8398fbSLorenzo Bianconi } 2307df8398fbSLorenzo Bianconi 2308df8398fbSLorenzo Bianconi static int airoha_qdma_init_rl_config(struct airoha_qdma *qdma, int queue_id, 2309df8398fbSLorenzo Bianconi bool enable, enum trtcm_unit_type unit) 2310df8398fbSLorenzo Bianconi { 2311df8398fbSLorenzo Bianconi bool tick_sel = queue_id == 0 || queue_id == 2 || queue_id == 8; 2312df8398fbSLorenzo Bianconi enum trtcm_param mode = TRTCM_METER_MODE; 2313df8398fbSLorenzo Bianconi int err; 2314df8398fbSLorenzo Bianconi 2315df8398fbSLorenzo Bianconi mode |= unit == TRTCM_PACKET_UNIT ? TRTCM_PKT_MODE : 0; 2316df8398fbSLorenzo Bianconi err = airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2317df8398fbSLorenzo Bianconi enable, mode); 2318df8398fbSLorenzo Bianconi if (err) 2319df8398fbSLorenzo Bianconi return err; 2320df8398fbSLorenzo Bianconi 2321df8398fbSLorenzo Bianconi return airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2322df8398fbSLorenzo Bianconi tick_sel, TRTCM_TICK_SEL); 2323df8398fbSLorenzo Bianconi } 2324df8398fbSLorenzo Bianconi 2325ef1ca927SLorenzo Bianconi static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, 2326ef1ca927SLorenzo Bianconi u32 addr, enum trtcm_param_type param, 2327ef1ca927SLorenzo Bianconi enum trtcm_mode_type mode, 2328ef1ca927SLorenzo Bianconi u32 *val_low, u32 *val_high) 2329ef1ca927SLorenzo Bianconi { 2330ef1ca927SLorenzo Bianconi u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); 2331ef1ca927SLorenzo Bianconi u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | 2332ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | 2333ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | 2334ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); 2335ef1ca927SLorenzo Bianconi 2336ef1ca927SLorenzo Bianconi airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); 2337ef1ca927SLorenzo Bianconi if (read_poll_timeout(airoha_qdma_rr, val, 2338ef1ca927SLorenzo Bianconi val & TRTCM_PARAM_RW_DONE_MASK, 2339ef1ca927SLorenzo Bianconi USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, 2340ef1ca927SLorenzo Bianconi qdma, REG_TRTCM_CFG_PARAM(addr))) 2341ef1ca927SLorenzo Bianconi return -ETIMEDOUT; 2342ef1ca927SLorenzo Bianconi 2343ef1ca927SLorenzo Bianconi *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); 2344ef1ca927SLorenzo Bianconi if (val_high) 2345ef1ca927SLorenzo Bianconi *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); 2346ef1ca927SLorenzo Bianconi 2347ef1ca927SLorenzo Bianconi return 0; 2348ef1ca927SLorenzo Bianconi } 2349ef1ca927SLorenzo Bianconi 2350ef1ca927SLorenzo Bianconi static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel, 2351ef1ca927SLorenzo Bianconi u32 addr, enum trtcm_param_type param, 2352ef1ca927SLorenzo Bianconi enum trtcm_mode_type mode, u32 val) 2353ef1ca927SLorenzo Bianconi { 2354ef1ca927SLorenzo Bianconi u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); 2355ef1ca927SLorenzo Bianconi u32 config = TRTCM_PARAM_RW_MASK | 2356ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | 2357ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | 2358ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | 2359ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); 2360ef1ca927SLorenzo Bianconi 2361ef1ca927SLorenzo Bianconi airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); 2362ef1ca927SLorenzo Bianconi airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); 2363ef1ca927SLorenzo Bianconi 2364ef1ca927SLorenzo Bianconi return read_poll_timeout(airoha_qdma_rr, val, 2365ef1ca927SLorenzo Bianconi val & TRTCM_PARAM_RW_DONE_MASK, 2366ef1ca927SLorenzo Bianconi USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, 2367ef1ca927SLorenzo Bianconi qdma, REG_TRTCM_CFG_PARAM(addr)); 2368ef1ca927SLorenzo Bianconi } 2369ef1ca927SLorenzo Bianconi 2370ef1ca927SLorenzo Bianconi static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel, 2371ef1ca927SLorenzo Bianconi u32 addr, enum trtcm_mode_type mode, 2372ef1ca927SLorenzo Bianconi bool enable, u32 enable_mask) 2373ef1ca927SLorenzo Bianconi { 2374ef1ca927SLorenzo Bianconi u32 val; 2375ef1ca927SLorenzo Bianconi 2376ef1ca927SLorenzo Bianconi if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, 2377ef1ca927SLorenzo Bianconi mode, &val, NULL)) 2378ef1ca927SLorenzo Bianconi return -EINVAL; 2379ef1ca927SLorenzo Bianconi 2380ef1ca927SLorenzo Bianconi val = enable ? val | enable_mask : val & ~enable_mask; 2381ef1ca927SLorenzo Bianconi 2382ef1ca927SLorenzo Bianconi return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, 2383ef1ca927SLorenzo Bianconi mode, val); 2384ef1ca927SLorenzo Bianconi } 2385ef1ca927SLorenzo Bianconi 2386ef1ca927SLorenzo Bianconi static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma, 2387ef1ca927SLorenzo Bianconi int channel, u32 addr, 2388ef1ca927SLorenzo Bianconi enum trtcm_mode_type mode, 2389ef1ca927SLorenzo Bianconi u32 rate_val, u32 bucket_size) 2390ef1ca927SLorenzo Bianconi { 2391ef1ca927SLorenzo Bianconi u32 val, config, tick, unit, rate, rate_frac; 2392ef1ca927SLorenzo Bianconi int err; 2393ef1ca927SLorenzo Bianconi 2394ef1ca927SLorenzo Bianconi if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, 2395ef1ca927SLorenzo Bianconi mode, &config, NULL)) 2396ef1ca927SLorenzo Bianconi return -EINVAL; 2397ef1ca927SLorenzo Bianconi 2398ef1ca927SLorenzo Bianconi val = airoha_qdma_rr(qdma, addr); 2399ef1ca927SLorenzo Bianconi tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); 2400ef1ca927SLorenzo Bianconi if (config & TRTCM_TICK_SEL) 2401ef1ca927SLorenzo Bianconi tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); 2402ef1ca927SLorenzo Bianconi if (!tick) 2403ef1ca927SLorenzo Bianconi return -EINVAL; 2404ef1ca927SLorenzo Bianconi 2405ef1ca927SLorenzo Bianconi unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; 2406ef1ca927SLorenzo Bianconi if (!unit) 2407ef1ca927SLorenzo Bianconi return -EINVAL; 2408ef1ca927SLorenzo Bianconi 2409ef1ca927SLorenzo Bianconi rate = rate_val / unit; 2410ef1ca927SLorenzo Bianconi rate_frac = rate_val % unit; 2411ef1ca927SLorenzo Bianconi rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; 2412ef1ca927SLorenzo Bianconi rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | 2413ef1ca927SLorenzo Bianconi FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); 2414ef1ca927SLorenzo Bianconi 2415ef1ca927SLorenzo Bianconi err = airoha_qdma_set_trtcm_param(qdma, channel, addr, 2416ef1ca927SLorenzo Bianconi TRTCM_TOKEN_RATE_MODE, mode, rate); 2417ef1ca927SLorenzo Bianconi if (err) 2418ef1ca927SLorenzo Bianconi return err; 2419ef1ca927SLorenzo Bianconi 2420ef1ca927SLorenzo Bianconi val = max_t(u32, bucket_size, MIN_TOKEN_SIZE); 2421ef1ca927SLorenzo Bianconi val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); 2422ef1ca927SLorenzo Bianconi 2423ef1ca927SLorenzo Bianconi return airoha_qdma_set_trtcm_param(qdma, channel, addr, 2424ef1ca927SLorenzo Bianconi TRTCM_BUCKETSIZE_SHIFT_MODE, 2425ef1ca927SLorenzo Bianconi mode, val); 2426ef1ca927SLorenzo Bianconi } 2427ef1ca927SLorenzo Bianconi 2428ef1ca927SLorenzo Bianconi static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port, 2429ef1ca927SLorenzo Bianconi int channel, u32 rate, 2430ef1ca927SLorenzo Bianconi u32 bucket_size) 2431ef1ca927SLorenzo Bianconi { 2432ef1ca927SLorenzo Bianconi int i, err; 2433ef1ca927SLorenzo Bianconi 2434ef1ca927SLorenzo Bianconi for (i = 0; i <= TRTCM_PEAK_MODE; i++) { 2435ef1ca927SLorenzo Bianconi err = airoha_qdma_set_trtcm_config(port->qdma, channel, 2436ef1ca927SLorenzo Bianconi REG_EGRESS_TRTCM_CFG, i, 2437ef1ca927SLorenzo Bianconi !!rate, TRTCM_METER_MODE); 2438ef1ca927SLorenzo Bianconi if (err) 2439ef1ca927SLorenzo Bianconi return err; 2440ef1ca927SLorenzo Bianconi 2441ef1ca927SLorenzo Bianconi err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel, 2442ef1ca927SLorenzo Bianconi REG_EGRESS_TRTCM_CFG, 2443ef1ca927SLorenzo Bianconi i, rate, bucket_size); 2444ef1ca927SLorenzo Bianconi if (err) 2445ef1ca927SLorenzo Bianconi return err; 2446ef1ca927SLorenzo Bianconi } 2447ef1ca927SLorenzo Bianconi 2448ef1ca927SLorenzo Bianconi return 0; 2449ef1ca927SLorenzo Bianconi } 2450ef1ca927SLorenzo Bianconi 2451ef1ca927SLorenzo Bianconi static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port, 2452ef1ca927SLorenzo Bianconi struct tc_htb_qopt_offload *opt) 2453ef1ca927SLorenzo Bianconi { 2454ef1ca927SLorenzo Bianconi u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; 2455ef1ca927SLorenzo Bianconi u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ 2456ef1ca927SLorenzo Bianconi struct net_device *dev = port->dev; 2457ef1ca927SLorenzo Bianconi int num_tx_queues = dev->real_num_tx_queues; 2458ef1ca927SLorenzo Bianconi int err; 2459ef1ca927SLorenzo Bianconi 2460ef1ca927SLorenzo Bianconi if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { 2461ef1ca927SLorenzo Bianconi NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); 2462ef1ca927SLorenzo Bianconi return -EINVAL; 2463ef1ca927SLorenzo Bianconi } 2464ef1ca927SLorenzo Bianconi 2465ef1ca927SLorenzo Bianconi err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum); 2466ef1ca927SLorenzo Bianconi if (err) { 2467ef1ca927SLorenzo Bianconi NL_SET_ERR_MSG_MOD(opt->extack, 2468ef1ca927SLorenzo Bianconi "failed configuring htb offload"); 2469ef1ca927SLorenzo Bianconi return err; 2470ef1ca927SLorenzo Bianconi } 2471ef1ca927SLorenzo Bianconi 2472ef1ca927SLorenzo Bianconi if (opt->command == TC_HTB_NODE_MODIFY) 2473ef1ca927SLorenzo Bianconi return 0; 2474ef1ca927SLorenzo Bianconi 2475ef1ca927SLorenzo Bianconi err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); 2476ef1ca927SLorenzo Bianconi if (err) { 2477ef1ca927SLorenzo Bianconi airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum); 2478ef1ca927SLorenzo Bianconi NL_SET_ERR_MSG_MOD(opt->extack, 2479ef1ca927SLorenzo Bianconi "failed setting real_num_tx_queues"); 2480ef1ca927SLorenzo Bianconi return err; 2481ef1ca927SLorenzo Bianconi } 2482ef1ca927SLorenzo Bianconi 2483ef1ca927SLorenzo Bianconi set_bit(channel, port->qos_sq_bmap); 2484ef1ca927SLorenzo Bianconi opt->qid = AIROHA_NUM_TX_RING + channel; 2485ef1ca927SLorenzo Bianconi 2486ef1ca927SLorenzo Bianconi return 0; 2487ef1ca927SLorenzo Bianconi } 2488ef1ca927SLorenzo Bianconi 2489df8398fbSLorenzo Bianconi static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port, 2490df8398fbSLorenzo Bianconi u32 rate, u32 bucket_size, 2491df8398fbSLorenzo Bianconi enum trtcm_unit_type unit_type) 2492df8398fbSLorenzo Bianconi { 2493df8398fbSLorenzo Bianconi struct airoha_qdma *qdma = port->qdma; 2494df8398fbSLorenzo Bianconi int i; 2495df8398fbSLorenzo Bianconi 2496df8398fbSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 2497df8398fbSLorenzo Bianconi int err; 2498df8398fbSLorenzo Bianconi 2499df8398fbSLorenzo Bianconi if (!qdma->q_rx[i].ndesc) 2500df8398fbSLorenzo Bianconi continue; 2501df8398fbSLorenzo Bianconi 2502df8398fbSLorenzo Bianconi err = airoha_qdma_init_rl_config(qdma, i, !!rate, unit_type); 2503df8398fbSLorenzo Bianconi if (err) 2504df8398fbSLorenzo Bianconi return err; 2505df8398fbSLorenzo Bianconi 2506df8398fbSLorenzo Bianconi err = airoha_qdma_set_rl_token_bucket(qdma, i, rate, 2507df8398fbSLorenzo Bianconi bucket_size); 2508df8398fbSLorenzo Bianconi if (err) 2509df8398fbSLorenzo Bianconi return err; 2510df8398fbSLorenzo Bianconi } 2511df8398fbSLorenzo Bianconi 2512df8398fbSLorenzo Bianconi return 0; 2513df8398fbSLorenzo Bianconi } 2514df8398fbSLorenzo Bianconi 2515df8398fbSLorenzo Bianconi static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f) 2516df8398fbSLorenzo Bianconi { 2517df8398fbSLorenzo Bianconi const struct flow_action *actions = &f->rule->action; 2518df8398fbSLorenzo Bianconi const struct flow_action_entry *act; 2519df8398fbSLorenzo Bianconi 2520df8398fbSLorenzo Bianconi if (!flow_action_has_entries(actions)) { 2521df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, 2522df8398fbSLorenzo Bianconi "filter run with no actions"); 2523df8398fbSLorenzo Bianconi return -EINVAL; 2524df8398fbSLorenzo Bianconi } 2525df8398fbSLorenzo Bianconi 2526df8398fbSLorenzo Bianconi if (!flow_offload_has_one_action(actions)) { 2527df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, 2528df8398fbSLorenzo Bianconi "only once action per filter is supported"); 2529df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2530df8398fbSLorenzo Bianconi } 2531df8398fbSLorenzo Bianconi 2532df8398fbSLorenzo Bianconi act = &actions->entries[0]; 2533df8398fbSLorenzo Bianconi if (act->id != FLOW_ACTION_POLICE) { 2534df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, "unsupported action"); 2535df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2536df8398fbSLorenzo Bianconi } 2537df8398fbSLorenzo Bianconi 2538df8398fbSLorenzo Bianconi if (act->police.exceed.act_id != FLOW_ACTION_DROP) { 2539df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, 2540df8398fbSLorenzo Bianconi "invalid exceed action id"); 2541df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2542df8398fbSLorenzo Bianconi } 2543df8398fbSLorenzo Bianconi 2544df8398fbSLorenzo Bianconi if (act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { 2545df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, 2546df8398fbSLorenzo Bianconi "invalid notexceed action id"); 2547df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2548df8398fbSLorenzo Bianconi } 2549df8398fbSLorenzo Bianconi 2550df8398fbSLorenzo Bianconi if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && 2551df8398fbSLorenzo Bianconi !flow_action_is_last_entry(actions, act)) { 2552df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, 2553df8398fbSLorenzo Bianconi "action accept must be last"); 2554df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2555df8398fbSLorenzo Bianconi } 2556df8398fbSLorenzo Bianconi 2557df8398fbSLorenzo Bianconi if (act->police.peakrate_bytes_ps || act->police.avrate || 2558df8398fbSLorenzo Bianconi act->police.overhead || act->police.mtu) { 2559df8398fbSLorenzo Bianconi NL_SET_ERR_MSG_MOD(f->common.extack, 2560df8398fbSLorenzo Bianconi "peakrate/avrate/overhead/mtu unsupported"); 2561df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2562df8398fbSLorenzo Bianconi } 2563df8398fbSLorenzo Bianconi 2564df8398fbSLorenzo Bianconi return 0; 2565df8398fbSLorenzo Bianconi } 2566df8398fbSLorenzo Bianconi 2567df8398fbSLorenzo Bianconi static int airoha_dev_tc_matchall(struct net_device *dev, 2568df8398fbSLorenzo Bianconi struct tc_cls_matchall_offload *f) 2569df8398fbSLorenzo Bianconi { 2570df8398fbSLorenzo Bianconi enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; 2571df8398fbSLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 2572df8398fbSLorenzo Bianconi u32 rate = 0, bucket_size = 0; 2573df8398fbSLorenzo Bianconi 2574df8398fbSLorenzo Bianconi switch (f->command) { 2575df8398fbSLorenzo Bianconi case TC_CLSMATCHALL_REPLACE: { 2576df8398fbSLorenzo Bianconi const struct flow_action_entry *act; 2577df8398fbSLorenzo Bianconi int err; 2578df8398fbSLorenzo Bianconi 2579df8398fbSLorenzo Bianconi err = airoha_tc_matchall_act_validate(f); 2580df8398fbSLorenzo Bianconi if (err) 2581df8398fbSLorenzo Bianconi return err; 2582df8398fbSLorenzo Bianconi 2583df8398fbSLorenzo Bianconi act = &f->rule->action.entries[0]; 2584df8398fbSLorenzo Bianconi if (act->police.rate_pkt_ps) { 2585df8398fbSLorenzo Bianconi rate = act->police.rate_pkt_ps; 2586df8398fbSLorenzo Bianconi bucket_size = act->police.burst_pkt; 2587df8398fbSLorenzo Bianconi unit_type = TRTCM_PACKET_UNIT; 2588df8398fbSLorenzo Bianconi } else { 2589df8398fbSLorenzo Bianconi rate = div_u64(act->police.rate_bytes_ps, 1000); 2590df8398fbSLorenzo Bianconi rate = rate << 3; /* Kbps */ 2591df8398fbSLorenzo Bianconi bucket_size = act->police.burst; 2592df8398fbSLorenzo Bianconi } 2593df8398fbSLorenzo Bianconi fallthrough; 2594df8398fbSLorenzo Bianconi } 2595df8398fbSLorenzo Bianconi case TC_CLSMATCHALL_DESTROY: 2596df8398fbSLorenzo Bianconi return airoha_qdma_set_rx_meter(port, rate, bucket_size, 2597df8398fbSLorenzo Bianconi unit_type); 2598df8398fbSLorenzo Bianconi default: 2599df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2600df8398fbSLorenzo Bianconi } 2601df8398fbSLorenzo Bianconi } 2602df8398fbSLorenzo Bianconi 2603df8398fbSLorenzo Bianconi static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type, 2604df8398fbSLorenzo Bianconi void *type_data, void *cb_priv) 2605df8398fbSLorenzo Bianconi { 2606df8398fbSLorenzo Bianconi struct net_device *dev = cb_priv; 2607df8398fbSLorenzo Bianconi 2608df8398fbSLorenzo Bianconi if (!tc_can_offload(dev)) 2609df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2610df8398fbSLorenzo Bianconi 2611df8398fbSLorenzo Bianconi switch (type) { 2612df8398fbSLorenzo Bianconi case TC_SETUP_CLSFLOWER: 2613df8398fbSLorenzo Bianconi return airoha_ppe_setup_tc_block_cb(dev, type_data); 2614df8398fbSLorenzo Bianconi case TC_SETUP_CLSMATCHALL: 2615df8398fbSLorenzo Bianconi return airoha_dev_tc_matchall(dev, type_data); 2616df8398fbSLorenzo Bianconi default: 2617df8398fbSLorenzo Bianconi return -EOPNOTSUPP; 2618df8398fbSLorenzo Bianconi } 2619df8398fbSLorenzo Bianconi } 2620df8398fbSLorenzo Bianconi 262100a76783SLorenzo Bianconi static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port, 262200a76783SLorenzo Bianconi struct flow_block_offload *f) 262300a76783SLorenzo Bianconi { 2624df8398fbSLorenzo Bianconi flow_setup_cb_t *cb = airoha_dev_setup_tc_block_cb; 262500a76783SLorenzo Bianconi static LIST_HEAD(block_cb_list); 262600a76783SLorenzo Bianconi struct flow_block_cb *block_cb; 262700a76783SLorenzo Bianconi 262800a76783SLorenzo Bianconi if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 262900a76783SLorenzo Bianconi return -EOPNOTSUPP; 263000a76783SLorenzo Bianconi 263100a76783SLorenzo Bianconi f->driver_block_list = &block_cb_list; 263200a76783SLorenzo Bianconi switch (f->command) { 263300a76783SLorenzo Bianconi case FLOW_BLOCK_BIND: 263400a76783SLorenzo Bianconi block_cb = flow_block_cb_lookup(f->block, cb, port->dev); 263500a76783SLorenzo Bianconi if (block_cb) { 263600a76783SLorenzo Bianconi flow_block_cb_incref(block_cb); 263700a76783SLorenzo Bianconi return 0; 263800a76783SLorenzo Bianconi } 263900a76783SLorenzo Bianconi block_cb = flow_block_cb_alloc(cb, port->dev, port->dev, NULL); 264000a76783SLorenzo Bianconi if (IS_ERR(block_cb)) 264100a76783SLorenzo Bianconi return PTR_ERR(block_cb); 264200a76783SLorenzo Bianconi 264300a76783SLorenzo Bianconi flow_block_cb_incref(block_cb); 264400a76783SLorenzo Bianconi flow_block_cb_add(block_cb, f); 264500a76783SLorenzo Bianconi list_add_tail(&block_cb->driver_list, &block_cb_list); 264600a76783SLorenzo Bianconi return 0; 264700a76783SLorenzo Bianconi case FLOW_BLOCK_UNBIND: 264800a76783SLorenzo Bianconi block_cb = flow_block_cb_lookup(f->block, cb, port->dev); 264900a76783SLorenzo Bianconi if (!block_cb) 265000a76783SLorenzo Bianconi return -ENOENT; 265100a76783SLorenzo Bianconi 265200a76783SLorenzo Bianconi if (!flow_block_cb_decref(block_cb)) { 265300a76783SLorenzo Bianconi flow_block_cb_remove(block_cb, f); 265400a76783SLorenzo Bianconi list_del(&block_cb->driver_list); 265500a76783SLorenzo Bianconi } 265600a76783SLorenzo Bianconi return 0; 265700a76783SLorenzo Bianconi default: 265800a76783SLorenzo Bianconi return -EOPNOTSUPP; 265900a76783SLorenzo Bianconi } 266000a76783SLorenzo Bianconi } 266100a76783SLorenzo Bianconi 2662ef1ca927SLorenzo Bianconi static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue) 2663ef1ca927SLorenzo Bianconi { 2664ef1ca927SLorenzo Bianconi struct net_device *dev = port->dev; 2665ef1ca927SLorenzo Bianconi 2666ef1ca927SLorenzo Bianconi netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); 2667ef1ca927SLorenzo Bianconi airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0); 2668ef1ca927SLorenzo Bianconi clear_bit(queue, port->qos_sq_bmap); 2669ef1ca927SLorenzo Bianconi } 2670ef1ca927SLorenzo Bianconi 2671ef1ca927SLorenzo Bianconi static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port, 2672ef1ca927SLorenzo Bianconi struct tc_htb_qopt_offload *opt) 2673ef1ca927SLorenzo Bianconi { 2674ef1ca927SLorenzo Bianconi u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; 2675ef1ca927SLorenzo Bianconi 2676ef1ca927SLorenzo Bianconi if (!test_bit(channel, port->qos_sq_bmap)) { 2677ef1ca927SLorenzo Bianconi NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); 2678ef1ca927SLorenzo Bianconi return -EINVAL; 2679ef1ca927SLorenzo Bianconi } 2680ef1ca927SLorenzo Bianconi 2681ef1ca927SLorenzo Bianconi airoha_tc_remove_htb_queue(port, channel); 2682ef1ca927SLorenzo Bianconi 2683ef1ca927SLorenzo Bianconi return 0; 2684ef1ca927SLorenzo Bianconi } 2685ef1ca927SLorenzo Bianconi 2686ef1ca927SLorenzo Bianconi static int airoha_tc_htb_destroy(struct airoha_gdm_port *port) 2687ef1ca927SLorenzo Bianconi { 2688ef1ca927SLorenzo Bianconi int q; 2689ef1ca927SLorenzo Bianconi 2690ef1ca927SLorenzo Bianconi for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) 2691ef1ca927SLorenzo Bianconi airoha_tc_remove_htb_queue(port, q); 2692ef1ca927SLorenzo Bianconi 2693ef1ca927SLorenzo Bianconi return 0; 2694ef1ca927SLorenzo Bianconi } 2695ef1ca927SLorenzo Bianconi 2696ef1ca927SLorenzo Bianconi static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port, 2697ef1ca927SLorenzo Bianconi struct tc_htb_qopt_offload *opt) 2698ef1ca927SLorenzo Bianconi { 2699ef1ca927SLorenzo Bianconi u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; 2700ef1ca927SLorenzo Bianconi 2701ef1ca927SLorenzo Bianconi if (!test_bit(channel, port->qos_sq_bmap)) { 2702ef1ca927SLorenzo Bianconi NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); 2703ef1ca927SLorenzo Bianconi return -EINVAL; 2704ef1ca927SLorenzo Bianconi } 2705ef1ca927SLorenzo Bianconi 270657b290d9SLorenzo Bianconi opt->qid = AIROHA_NUM_TX_RING + channel; 2707ef1ca927SLorenzo Bianconi 2708ef1ca927SLorenzo Bianconi return 0; 2709ef1ca927SLorenzo Bianconi } 2710ef1ca927SLorenzo Bianconi 2711ef1ca927SLorenzo Bianconi static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port, 2712ef1ca927SLorenzo Bianconi struct tc_htb_qopt_offload *opt) 2713ef1ca927SLorenzo Bianconi { 2714ef1ca927SLorenzo Bianconi switch (opt->command) { 2715ef1ca927SLorenzo Bianconi case TC_HTB_CREATE: 2716ef1ca927SLorenzo Bianconi break; 2717ef1ca927SLorenzo Bianconi case TC_HTB_DESTROY: 2718ef1ca927SLorenzo Bianconi return airoha_tc_htb_destroy(port); 2719ef1ca927SLorenzo Bianconi case TC_HTB_NODE_MODIFY: 2720ef1ca927SLorenzo Bianconi case TC_HTB_LEAF_ALLOC_QUEUE: 2721ef1ca927SLorenzo Bianconi return airoha_tc_htb_alloc_leaf_queue(port, opt); 2722ef1ca927SLorenzo Bianconi case TC_HTB_LEAF_DEL: 2723ef1ca927SLorenzo Bianconi case TC_HTB_LEAF_DEL_LAST: 2724ef1ca927SLorenzo Bianconi case TC_HTB_LEAF_DEL_LAST_FORCE: 2725ef1ca927SLorenzo Bianconi return airoha_tc_htb_delete_leaf_queue(port, opt); 2726ef1ca927SLorenzo Bianconi case TC_HTB_LEAF_QUERY_QUEUE: 2727ef1ca927SLorenzo Bianconi return airoha_tc_get_htb_get_leaf_queue(port, opt); 2728ef1ca927SLorenzo Bianconi default: 2729ef1ca927SLorenzo Bianconi return -EOPNOTSUPP; 2730ef1ca927SLorenzo Bianconi } 2731ef1ca927SLorenzo Bianconi 2732ef1ca927SLorenzo Bianconi return 0; 2733ef1ca927SLorenzo Bianconi } 2734ef1ca927SLorenzo Bianconi 273520bf7d07SLorenzo Bianconi static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, 273620bf7d07SLorenzo Bianconi void *type_data) 273720bf7d07SLorenzo Bianconi { 273820bf7d07SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 273920bf7d07SLorenzo Bianconi 274020bf7d07SLorenzo Bianconi switch (type) { 274120bf7d07SLorenzo Bianconi case TC_SETUP_QDISC_ETS: 274220bf7d07SLorenzo Bianconi return airoha_tc_setup_qdisc_ets(port, type_data); 2743ef1ca927SLorenzo Bianconi case TC_SETUP_QDISC_HTB: 2744ef1ca927SLorenzo Bianconi return airoha_tc_setup_qdisc_htb(port, type_data); 274500a76783SLorenzo Bianconi case TC_SETUP_BLOCK: 274600a76783SLorenzo Bianconi case TC_SETUP_FT: 274700a76783SLorenzo Bianconi return airoha_dev_setup_tc_block(port, type_data); 274820bf7d07SLorenzo Bianconi default: 274920bf7d07SLorenzo Bianconi return -EOPNOTSUPP; 275020bf7d07SLorenzo Bianconi } 275120bf7d07SLorenzo Bianconi } 275220bf7d07SLorenzo Bianconi 275323020f04SLorenzo Bianconi static const struct net_device_ops airoha_netdev_ops = { 275423020f04SLorenzo Bianconi .ndo_init = airoha_dev_init, 275523020f04SLorenzo Bianconi .ndo_open = airoha_dev_open, 275623020f04SLorenzo Bianconi .ndo_stop = airoha_dev_stop, 275703b1b69fSLorenzo Bianconi .ndo_change_mtu = airoha_dev_change_mtu, 27582b288b81SLorenzo Bianconi .ndo_select_queue = airoha_dev_select_queue, 275923020f04SLorenzo Bianconi .ndo_start_xmit = airoha_dev_xmit, 276023020f04SLorenzo Bianconi .ndo_get_stats64 = airoha_dev_get_stats64, 276123020f04SLorenzo Bianconi .ndo_set_mac_address = airoha_dev_set_macaddr, 276220bf7d07SLorenzo Bianconi .ndo_setup_tc = airoha_dev_tc_setup, 276323020f04SLorenzo Bianconi }; 276423020f04SLorenzo Bianconi 276523020f04SLorenzo Bianconi static const struct ethtool_ops airoha_ethtool_ops = { 276623020f04SLorenzo Bianconi .get_drvinfo = airoha_ethtool_get_drvinfo, 276723020f04SLorenzo Bianconi .get_eth_mac_stats = airoha_ethtool_get_mac_stats, 276823020f04SLorenzo Bianconi .get_rmon_stats = airoha_ethtool_get_rmon_stats, 276923020f04SLorenzo Bianconi }; 277023020f04SLorenzo Bianconi 2771af3cf757SLorenzo Bianconi static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port) 2772af3cf757SLorenzo Bianconi { 2773af3cf757SLorenzo Bianconi int i; 2774af3cf757SLorenzo Bianconi 2775af3cf757SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(port->dsa_meta); i++) { 2776af3cf757SLorenzo Bianconi struct metadata_dst *md_dst; 2777af3cf757SLorenzo Bianconi 2778af3cf757SLorenzo Bianconi md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, 2779af3cf757SLorenzo Bianconi GFP_KERNEL); 2780af3cf757SLorenzo Bianconi if (!md_dst) 2781af3cf757SLorenzo Bianconi return -ENOMEM; 2782af3cf757SLorenzo Bianconi 2783af3cf757SLorenzo Bianconi md_dst->u.port_info.port_id = i; 2784af3cf757SLorenzo Bianconi port->dsa_meta[i] = md_dst; 2785af3cf757SLorenzo Bianconi } 2786af3cf757SLorenzo Bianconi 2787af3cf757SLorenzo Bianconi return 0; 2788af3cf757SLorenzo Bianconi } 2789af3cf757SLorenzo Bianconi 2790af3cf757SLorenzo Bianconi static void airoha_metadata_dst_free(struct airoha_gdm_port *port) 2791af3cf757SLorenzo Bianconi { 2792af3cf757SLorenzo Bianconi int i; 2793af3cf757SLorenzo Bianconi 2794af3cf757SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(port->dsa_meta); i++) { 2795af3cf757SLorenzo Bianconi if (!port->dsa_meta[i]) 2796af3cf757SLorenzo Bianconi continue; 2797af3cf757SLorenzo Bianconi 2798af3cf757SLorenzo Bianconi metadata_dst_free(port->dsa_meta[i]); 2799af3cf757SLorenzo Bianconi } 2800af3cf757SLorenzo Bianconi } 2801af3cf757SLorenzo Bianconi 280209bccf56SLorenzo Bianconi bool airoha_is_valid_gdm_port(struct airoha_eth *eth, 280309bccf56SLorenzo Bianconi struct airoha_gdm_port *port) 280409bccf56SLorenzo Bianconi { 280509bccf56SLorenzo Bianconi int i; 280609bccf56SLorenzo Bianconi 280709bccf56SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { 280809bccf56SLorenzo Bianconi if (eth->ports[i] == port) 280909bccf56SLorenzo Bianconi return true; 281009bccf56SLorenzo Bianconi } 281109bccf56SLorenzo Bianconi 281209bccf56SLorenzo Bianconi return false; 281309bccf56SLorenzo Bianconi } 281409bccf56SLorenzo Bianconi 281580369686SLorenzo Bianconi static int airoha_alloc_gdm_port(struct airoha_eth *eth, 281680369686SLorenzo Bianconi struct device_node *np, int index) 281723020f04SLorenzo Bianconi { 281823020f04SLorenzo Bianconi const __be32 *id_ptr = of_get_property(np, "reg", NULL); 281923020f04SLorenzo Bianconi struct airoha_gdm_port *port; 28209304640fSLorenzo Bianconi struct airoha_qdma *qdma; 282123020f04SLorenzo Bianconi struct net_device *dev; 282280369686SLorenzo Bianconi int err, p; 282323020f04SLorenzo Bianconi u32 id; 282423020f04SLorenzo Bianconi 282523020f04SLorenzo Bianconi if (!id_ptr) { 282623020f04SLorenzo Bianconi dev_err(eth->dev, "missing gdm port id\n"); 282723020f04SLorenzo Bianconi return -EINVAL; 282823020f04SLorenzo Bianconi } 282923020f04SLorenzo Bianconi 283023020f04SLorenzo Bianconi id = be32_to_cpup(id_ptr); 283180369686SLorenzo Bianconi p = id - 1; 283223020f04SLorenzo Bianconi 283323020f04SLorenzo Bianconi if (!id || id > ARRAY_SIZE(eth->ports)) { 283423020f04SLorenzo Bianconi dev_err(eth->dev, "invalid gdm port id: %d\n", id); 283523020f04SLorenzo Bianconi return -EINVAL; 283623020f04SLorenzo Bianconi } 283723020f04SLorenzo Bianconi 283880369686SLorenzo Bianconi if (eth->ports[p]) { 283923020f04SLorenzo Bianconi dev_err(eth->dev, "duplicate gdm port id: %d\n", id); 284023020f04SLorenzo Bianconi return -EINVAL; 284123020f04SLorenzo Bianconi } 284223020f04SLorenzo Bianconi 284323020f04SLorenzo Bianconi dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), 2844ef1ca927SLorenzo Bianconi AIROHA_NUM_NETDEV_TX_RINGS, 2845ef1ca927SLorenzo Bianconi AIROHA_NUM_RX_RING); 284623020f04SLorenzo Bianconi if (!dev) { 284723020f04SLorenzo Bianconi dev_err(eth->dev, "alloc_etherdev failed\n"); 284823020f04SLorenzo Bianconi return -ENOMEM; 284923020f04SLorenzo Bianconi } 285023020f04SLorenzo Bianconi 28519304640fSLorenzo Bianconi qdma = ð->qdma[index % AIROHA_MAX_NUM_QDMA]; 285223020f04SLorenzo Bianconi dev->netdev_ops = &airoha_netdev_ops; 285323020f04SLorenzo Bianconi dev->ethtool_ops = &airoha_ethtool_ops; 285423020f04SLorenzo Bianconi dev->max_mtu = AIROHA_MAX_MTU; 285523020f04SLorenzo Bianconi dev->watchdog_timeo = 5 * HZ; 285623020f04SLorenzo Bianconi dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | 285723020f04SLorenzo Bianconi NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | 285820bf7d07SLorenzo Bianconi NETIF_F_SG | NETIF_F_TSO | 285920bf7d07SLorenzo Bianconi NETIF_F_HW_TC; 286023020f04SLorenzo Bianconi dev->features |= dev->hw_features; 2861a202dfe3SLorenzo Bianconi dev->vlan_features = dev->hw_features; 286223020f04SLorenzo Bianconi dev->dev.of_node = np; 28639439db26SLorenzo Bianconi dev->irq = qdma->irq_banks[0].irq; 286423020f04SLorenzo Bianconi SET_NETDEV_DEV(dev, eth->dev); 286523020f04SLorenzo Bianconi 2866ef1ca927SLorenzo Bianconi /* reserve hw queues for HTB offloading */ 2867ef1ca927SLorenzo Bianconi err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING); 2868ef1ca927SLorenzo Bianconi if (err) 2869ef1ca927SLorenzo Bianconi return err; 2870ef1ca927SLorenzo Bianconi 287123020f04SLorenzo Bianconi err = of_get_ethdev_address(np, dev); 287223020f04SLorenzo Bianconi if (err) { 287323020f04SLorenzo Bianconi if (err == -EPROBE_DEFER) 287423020f04SLorenzo Bianconi return err; 287523020f04SLorenzo Bianconi 287623020f04SLorenzo Bianconi eth_hw_addr_random(dev); 287723020f04SLorenzo Bianconi dev_info(eth->dev, "generated random MAC address %pM\n", 287823020f04SLorenzo Bianconi dev->dev_addr); 287923020f04SLorenzo Bianconi } 288023020f04SLorenzo Bianconi 288123020f04SLorenzo Bianconi port = netdev_priv(dev); 288223020f04SLorenzo Bianconi u64_stats_init(&port->stats.syncp); 288323020f04SLorenzo Bianconi spin_lock_init(&port->stats.lock); 28849304640fSLorenzo Bianconi port->qdma = qdma; 288523020f04SLorenzo Bianconi port->dev = dev; 288623020f04SLorenzo Bianconi port->id = id; 288780369686SLorenzo Bianconi eth->ports[p] = port; 288823020f04SLorenzo Bianconi 2889af3cf757SLorenzo Bianconi err = airoha_metadata_dst_alloc(port); 2890af3cf757SLorenzo Bianconi if (err) 2891af3cf757SLorenzo Bianconi return err; 2892af3cf757SLorenzo Bianconi 2893c5978378SChristophe JAILLET err = register_netdev(dev); 2894c5978378SChristophe JAILLET if (err) 2895c5978378SChristophe JAILLET goto free_metadata_dst; 2896c5978378SChristophe JAILLET 2897c5978378SChristophe JAILLET return 0; 2898c5978378SChristophe JAILLET 2899c5978378SChristophe JAILLET free_metadata_dst: 2900c5978378SChristophe JAILLET airoha_metadata_dst_free(port); 2901c5978378SChristophe JAILLET return err; 290223020f04SLorenzo Bianconi } 290323020f04SLorenzo Bianconi 290423020f04SLorenzo Bianconi static int airoha_probe(struct platform_device *pdev) 290523020f04SLorenzo Bianconi { 290623020f04SLorenzo Bianconi struct device_node *np; 290723020f04SLorenzo Bianconi struct airoha_eth *eth; 290823020f04SLorenzo Bianconi int i, err; 290923020f04SLorenzo Bianconi 291023020f04SLorenzo Bianconi eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL); 291123020f04SLorenzo Bianconi if (!eth) 291223020f04SLorenzo Bianconi return -ENOMEM; 291323020f04SLorenzo Bianconi 291423020f04SLorenzo Bianconi eth->dev = &pdev->dev; 291523020f04SLorenzo Bianconi 291623020f04SLorenzo Bianconi err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32)); 291723020f04SLorenzo Bianconi if (err) { 291823020f04SLorenzo Bianconi dev_err(eth->dev, "failed configuring DMA mask\n"); 291923020f04SLorenzo Bianconi return err; 292023020f04SLorenzo Bianconi } 292123020f04SLorenzo Bianconi 292223020f04SLorenzo Bianconi eth->fe_regs = devm_platform_ioremap_resource_byname(pdev, "fe"); 292323020f04SLorenzo Bianconi if (IS_ERR(eth->fe_regs)) 292423020f04SLorenzo Bianconi return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs), 292523020f04SLorenzo Bianconi "failed to iomap fe regs\n"); 292623020f04SLorenzo Bianconi 292723020f04SLorenzo Bianconi eth->rsts[0].id = "fe"; 292823020f04SLorenzo Bianconi eth->rsts[1].id = "pdma"; 292923020f04SLorenzo Bianconi eth->rsts[2].id = "qdma"; 293023020f04SLorenzo Bianconi err = devm_reset_control_bulk_get_exclusive(eth->dev, 293123020f04SLorenzo Bianconi ARRAY_SIZE(eth->rsts), 293223020f04SLorenzo Bianconi eth->rsts); 293323020f04SLorenzo Bianconi if (err) { 293423020f04SLorenzo Bianconi dev_err(eth->dev, "failed to get bulk reset lines\n"); 293523020f04SLorenzo Bianconi return err; 293623020f04SLorenzo Bianconi } 293723020f04SLorenzo Bianconi 293823020f04SLorenzo Bianconi eth->xsi_rsts[0].id = "xsi-mac"; 293923020f04SLorenzo Bianconi eth->xsi_rsts[1].id = "hsi0-mac"; 294023020f04SLorenzo Bianconi eth->xsi_rsts[2].id = "hsi1-mac"; 294123020f04SLorenzo Bianconi eth->xsi_rsts[3].id = "hsi-mac"; 294223020f04SLorenzo Bianconi eth->xsi_rsts[4].id = "xfp-mac"; 294323020f04SLorenzo Bianconi err = devm_reset_control_bulk_get_exclusive(eth->dev, 294423020f04SLorenzo Bianconi ARRAY_SIZE(eth->xsi_rsts), 294523020f04SLorenzo Bianconi eth->xsi_rsts); 294623020f04SLorenzo Bianconi if (err) { 294723020f04SLorenzo Bianconi dev_err(eth->dev, "failed to get bulk xsi reset lines\n"); 294823020f04SLorenzo Bianconi return err; 294923020f04SLorenzo Bianconi } 295023020f04SLorenzo Bianconi 295123020f04SLorenzo Bianconi eth->napi_dev = alloc_netdev_dummy(0); 295223020f04SLorenzo Bianconi if (!eth->napi_dev) 295323020f04SLorenzo Bianconi return -ENOMEM; 295423020f04SLorenzo Bianconi 295523020f04SLorenzo Bianconi /* Enable threaded NAPI by default */ 295623020f04SLorenzo Bianconi eth->napi_dev->threaded = true; 295723020f04SLorenzo Bianconi strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name)); 295823020f04SLorenzo Bianconi platform_set_drvdata(pdev, eth); 295923020f04SLorenzo Bianconi 296019e47fc2SLorenzo Bianconi err = airoha_hw_init(pdev, eth); 296123020f04SLorenzo Bianconi if (err) 29620c7469eeSLorenzo Bianconi goto error_hw_cleanup; 296323020f04SLorenzo Bianconi 2964160231e3SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) 2965160231e3SLorenzo Bianconi airoha_qdma_start_napi(ð->qdma[i]); 2966160231e3SLorenzo Bianconi 296780369686SLorenzo Bianconi i = 0; 296823020f04SLorenzo Bianconi for_each_child_of_node(pdev->dev.of_node, np) { 296923020f04SLorenzo Bianconi if (!of_device_is_compatible(np, "airoha,eth-mac")) 297023020f04SLorenzo Bianconi continue; 297123020f04SLorenzo Bianconi 297223020f04SLorenzo Bianconi if (!of_device_is_available(np)) 297323020f04SLorenzo Bianconi continue; 297423020f04SLorenzo Bianconi 297580369686SLorenzo Bianconi err = airoha_alloc_gdm_port(eth, np, i++); 297623020f04SLorenzo Bianconi if (err) { 297723020f04SLorenzo Bianconi of_node_put(np); 29780c7469eeSLorenzo Bianconi goto error_napi_stop; 297923020f04SLorenzo Bianconi } 298023020f04SLorenzo Bianconi } 298123020f04SLorenzo Bianconi 298223020f04SLorenzo Bianconi return 0; 298323020f04SLorenzo Bianconi 29840c7469eeSLorenzo Bianconi error_napi_stop: 29850c7469eeSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) 29860c7469eeSLorenzo Bianconi airoha_qdma_stop_napi(ð->qdma[i]); 2987*3ef07434SChristophe JAILLET airoha_ppe_deinit(eth); 29880c7469eeSLorenzo Bianconi error_hw_cleanup: 2989e618447cSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) 2990e618447cSLorenzo Bianconi airoha_hw_cleanup(ð->qdma[i]); 2991e618447cSLorenzo Bianconi 299223020f04SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { 299323020f04SLorenzo Bianconi struct airoha_gdm_port *port = eth->ports[i]; 299423020f04SLorenzo Bianconi 2995af3cf757SLorenzo Bianconi if (port && port->dev->reg_state == NETREG_REGISTERED) { 299623020f04SLorenzo Bianconi unregister_netdev(port->dev); 2997af3cf757SLorenzo Bianconi airoha_metadata_dst_free(port); 2998af3cf757SLorenzo Bianconi } 299923020f04SLorenzo Bianconi } 300023020f04SLorenzo Bianconi free_netdev(eth->napi_dev); 300123020f04SLorenzo Bianconi platform_set_drvdata(pdev, NULL); 300223020f04SLorenzo Bianconi 300323020f04SLorenzo Bianconi return err; 300423020f04SLorenzo Bianconi } 300523020f04SLorenzo Bianconi 300623020f04SLorenzo Bianconi static void airoha_remove(struct platform_device *pdev) 300723020f04SLorenzo Bianconi { 300823020f04SLorenzo Bianconi struct airoha_eth *eth = platform_get_drvdata(pdev); 300923020f04SLorenzo Bianconi int i; 301023020f04SLorenzo Bianconi 30110c7469eeSLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { 30120c7469eeSLorenzo Bianconi airoha_qdma_stop_napi(ð->qdma[i]); 3013e618447cSLorenzo Bianconi airoha_hw_cleanup(ð->qdma[i]); 30140c7469eeSLorenzo Bianconi } 3015e618447cSLorenzo Bianconi 301623020f04SLorenzo Bianconi for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { 301723020f04SLorenzo Bianconi struct airoha_gdm_port *port = eth->ports[i]; 301823020f04SLorenzo Bianconi 301923020f04SLorenzo Bianconi if (!port) 302023020f04SLorenzo Bianconi continue; 302123020f04SLorenzo Bianconi 302223020f04SLorenzo Bianconi airoha_dev_stop(port->dev); 302323020f04SLorenzo Bianconi unregister_netdev(port->dev); 3024af3cf757SLorenzo Bianconi airoha_metadata_dst_free(port); 302523020f04SLorenzo Bianconi } 302623020f04SLorenzo Bianconi free_netdev(eth->napi_dev); 302723020f04SLorenzo Bianconi 302800a76783SLorenzo Bianconi airoha_ppe_deinit(eth); 302923020f04SLorenzo Bianconi platform_set_drvdata(pdev, NULL); 303023020f04SLorenzo Bianconi } 303123020f04SLorenzo Bianconi 303223020f04SLorenzo Bianconi static const struct of_device_id of_airoha_match[] = { 303323020f04SLorenzo Bianconi { .compatible = "airoha,en7581-eth" }, 303423020f04SLorenzo Bianconi { /* sentinel */ } 303523020f04SLorenzo Bianconi }; 30367d2bd8acSLiao Chen MODULE_DEVICE_TABLE(of, of_airoha_match); 303723020f04SLorenzo Bianconi 303823020f04SLorenzo Bianconi static struct platform_driver airoha_driver = { 303923020f04SLorenzo Bianconi .probe = airoha_probe, 3040e96321faSUwe Kleine-König .remove = airoha_remove, 304123020f04SLorenzo Bianconi .driver = { 304223020f04SLorenzo Bianconi .name = KBUILD_MODNAME, 304323020f04SLorenzo Bianconi .of_match_table = of_airoha_match, 304423020f04SLorenzo Bianconi }, 304523020f04SLorenzo Bianconi }; 304623020f04SLorenzo Bianconi module_platform_driver(airoha_driver); 304723020f04SLorenzo Bianconi 304823020f04SLorenzo Bianconi MODULE_LICENSE("GPL"); 304923020f04SLorenzo Bianconi MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); 305023020f04SLorenzo Bianconi MODULE_DESCRIPTION("Ethernet driver for Airoha SoC"); 3051