18c2ecf20Sopenharmony_ci/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is dual-licensed; you may select either version 2 of 68c2ecf20Sopenharmony_ci * the GNU General Public License ("GPL") or BSD license ("BSD"). 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This Synopsys DWC XLGMAC software driver and associated documentation 98c2ecf20Sopenharmony_ci * (hereinafter the "Software") is an unsupported proprietary work of 108c2ecf20Sopenharmony_ci * Synopsys, Inc. unless otherwise expressly agreed to in writing between 118c2ecf20Sopenharmony_ci * Synopsys and you. The Software IS NOT an item of Licensed Software or a 128c2ecf20Sopenharmony_ci * Licensed Product under any End User Software License Agreement or 138c2ecf20Sopenharmony_ci * Agreement for Licensed Products with Synopsys or any supplement thereto. 148c2ecf20Sopenharmony_ci * Synopsys is a registered trademark of Synopsys, Inc. Other names included 158c2ecf20Sopenharmony_ci * in the SOFTWARE may be the trademarks of their respective owners. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/tcp.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "dwc-xlgmac.h" 238c2ecf20Sopenharmony_ci#include "dwc-xlgmac-reg.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int xlgmac_one_poll(struct napi_struct *, int); 268c2ecf20Sopenharmony_cistatic int xlgmac_all_poll(struct napi_struct *, int); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic inline unsigned int xlgmac_tx_avail_desc(struct xlgmac_ring *ring) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return (ring->dma_desc_count - (ring->cur - ring->dirty)); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic inline unsigned int xlgmac_rx_dirty_desc(struct xlgmac_ring *ring) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci return (ring->cur - ring->dirty); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int xlgmac_maybe_stop_tx_queue( 398c2ecf20Sopenharmony_ci struct xlgmac_channel *channel, 408c2ecf20Sopenharmony_ci struct xlgmac_ring *ring, 418c2ecf20Sopenharmony_ci unsigned int count) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = channel->pdata; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (count > xlgmac_tx_avail_desc(ring)) { 468c2ecf20Sopenharmony_ci netif_info(pdata, drv, pdata->netdev, 478c2ecf20Sopenharmony_ci "Tx queue stopped, not enough descriptors available\n"); 488c2ecf20Sopenharmony_ci netif_stop_subqueue(pdata->netdev, channel->queue_index); 498c2ecf20Sopenharmony_ci ring->tx.queue_stopped = 1; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* If we haven't notified the hardware because of xmit_more 528c2ecf20Sopenharmony_ci * support, tell it now 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci if (ring->tx.xmit_more) 558c2ecf20Sopenharmony_ci pdata->hw_ops.tx_start_xmit(channel, ring); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void xlgmac_prep_vlan(struct sk_buff *skb, 648c2ecf20Sopenharmony_ci struct xlgmac_pkt_info *pkt_info) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) 678c2ecf20Sopenharmony_ci pkt_info->vlan_ctag = skb_vlan_tag_get(skb); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int xlgmac_prep_tso(struct sk_buff *skb, 718c2ecf20Sopenharmony_ci struct xlgmac_pkt_info *pkt_info) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int ret; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!XLGMAC_GET_REG_BITS(pkt_info->attributes, 768c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, 778c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN)) 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ret = skb_cow_head(skb, 0); 818c2ecf20Sopenharmony_ci if (ret) 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci pkt_info->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 858c2ecf20Sopenharmony_ci pkt_info->tcp_header_len = tcp_hdrlen(skb); 868c2ecf20Sopenharmony_ci pkt_info->tcp_payload_len = skb->len - pkt_info->header_len; 878c2ecf20Sopenharmony_ci pkt_info->mss = skb_shinfo(skb)->gso_size; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci XLGMAC_PR("header_len=%u\n", pkt_info->header_len); 908c2ecf20Sopenharmony_ci XLGMAC_PR("tcp_header_len=%u, tcp_payload_len=%u\n", 918c2ecf20Sopenharmony_ci pkt_info->tcp_header_len, pkt_info->tcp_payload_len); 928c2ecf20Sopenharmony_ci XLGMAC_PR("mss=%u\n", pkt_info->mss); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Update the number of packets that will ultimately be transmitted 958c2ecf20Sopenharmony_ci * along with the extra bytes for each extra packet 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci pkt_info->tx_packets = skb_shinfo(skb)->gso_segs; 988c2ecf20Sopenharmony_ci pkt_info->tx_bytes += (pkt_info->tx_packets - 1) * pkt_info->header_len; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int xlgmac_is_tso(struct sk_buff *skb) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!skb_is_gso(skb)) 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 1; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void xlgmac_prep_tx_pkt(struct xlgmac_pdata *pdata, 1158c2ecf20Sopenharmony_ci struct xlgmac_ring *ring, 1168c2ecf20Sopenharmony_ci struct sk_buff *skb, 1178c2ecf20Sopenharmony_ci struct xlgmac_pkt_info *pkt_info) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci skb_frag_t *frag; 1208c2ecf20Sopenharmony_ci unsigned int context_desc; 1218c2ecf20Sopenharmony_ci unsigned int len; 1228c2ecf20Sopenharmony_ci unsigned int i; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci pkt_info->skb = skb; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci context_desc = 0; 1278c2ecf20Sopenharmony_ci pkt_info->desc_count = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci pkt_info->tx_packets = 1; 1308c2ecf20Sopenharmony_ci pkt_info->tx_bytes = skb->len; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (xlgmac_is_tso(skb)) { 1338c2ecf20Sopenharmony_ci /* TSO requires an extra descriptor if mss is different */ 1348c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) { 1358c2ecf20Sopenharmony_ci context_desc = 1; 1368c2ecf20Sopenharmony_ci pkt_info->desc_count++; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* TSO requires an extra descriptor for TSO header */ 1408c2ecf20Sopenharmony_ci pkt_info->desc_count++; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci pkt_info->attributes = XLGMAC_SET_REG_BITS( 1438c2ecf20Sopenharmony_ci pkt_info->attributes, 1448c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, 1458c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN, 1468c2ecf20Sopenharmony_ci 1); 1478c2ecf20Sopenharmony_ci pkt_info->attributes = XLGMAC_SET_REG_BITS( 1488c2ecf20Sopenharmony_ci pkt_info->attributes, 1498c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, 1508c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN, 1518c2ecf20Sopenharmony_ci 1); 1528c2ecf20Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_PARTIAL) 1538c2ecf20Sopenharmony_ci pkt_info->attributes = XLGMAC_SET_REG_BITS( 1548c2ecf20Sopenharmony_ci pkt_info->attributes, 1558c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, 1568c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN, 1578c2ecf20Sopenharmony_ci 1); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 1608c2ecf20Sopenharmony_ci /* VLAN requires an extra descriptor if tag is different */ 1618c2ecf20Sopenharmony_ci if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) 1628c2ecf20Sopenharmony_ci /* We can share with the TSO context descriptor */ 1638c2ecf20Sopenharmony_ci if (!context_desc) { 1648c2ecf20Sopenharmony_ci context_desc = 1; 1658c2ecf20Sopenharmony_ci pkt_info->desc_count++; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci pkt_info->attributes = XLGMAC_SET_REG_BITS( 1698c2ecf20Sopenharmony_ci pkt_info->attributes, 1708c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, 1718c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN, 1728c2ecf20Sopenharmony_ci 1); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci for (len = skb_headlen(skb); len;) { 1768c2ecf20Sopenharmony_ci pkt_info->desc_count++; 1778c2ecf20Sopenharmony_ci len -= min_t(unsigned int, len, XLGMAC_TX_MAX_BUF_SIZE); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 1818c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 1828c2ecf20Sopenharmony_ci for (len = skb_frag_size(frag); len; ) { 1838c2ecf20Sopenharmony_ci pkt_info->desc_count++; 1848c2ecf20Sopenharmony_ci len -= min_t(unsigned int, len, XLGMAC_TX_MAX_BUF_SIZE); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int xlgmac_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci unsigned int rx_buf_size; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (mtu > XLGMAC_JUMBO_PACKET_MTU) { 1948c2ecf20Sopenharmony_ci netdev_alert(netdev, "MTU exceeds maximum supported value\n"); 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 1998c2ecf20Sopenharmony_ci rx_buf_size = clamp_val(rx_buf_size, XLGMAC_RX_MIN_BUF_SIZE, PAGE_SIZE); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci rx_buf_size = (rx_buf_size + XLGMAC_RX_BUF_ALIGN - 1) & 2028c2ecf20Sopenharmony_ci ~(XLGMAC_RX_BUF_ALIGN - 1); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return rx_buf_size; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void xlgmac_enable_rx_tx_ints(struct xlgmac_pdata *pdata) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 2108c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 2118c2ecf20Sopenharmony_ci enum xlgmac_int int_id; 2128c2ecf20Sopenharmony_ci unsigned int i; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci channel = pdata->channel_head; 2158c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 2168c2ecf20Sopenharmony_ci if (channel->tx_ring && channel->rx_ring) 2178c2ecf20Sopenharmony_ci int_id = XLGMAC_INT_DMA_CH_SR_TI_RI; 2188c2ecf20Sopenharmony_ci else if (channel->tx_ring) 2198c2ecf20Sopenharmony_ci int_id = XLGMAC_INT_DMA_CH_SR_TI; 2208c2ecf20Sopenharmony_ci else if (channel->rx_ring) 2218c2ecf20Sopenharmony_ci int_id = XLGMAC_INT_DMA_CH_SR_RI; 2228c2ecf20Sopenharmony_ci else 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci hw_ops->enable_int(channel, int_id); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void xlgmac_disable_rx_tx_ints(struct xlgmac_pdata *pdata) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 2328c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 2338c2ecf20Sopenharmony_ci enum xlgmac_int int_id; 2348c2ecf20Sopenharmony_ci unsigned int i; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci channel = pdata->channel_head; 2378c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 2388c2ecf20Sopenharmony_ci if (channel->tx_ring && channel->rx_ring) 2398c2ecf20Sopenharmony_ci int_id = XLGMAC_INT_DMA_CH_SR_TI_RI; 2408c2ecf20Sopenharmony_ci else if (channel->tx_ring) 2418c2ecf20Sopenharmony_ci int_id = XLGMAC_INT_DMA_CH_SR_TI; 2428c2ecf20Sopenharmony_ci else if (channel->rx_ring) 2438c2ecf20Sopenharmony_ci int_id = XLGMAC_INT_DMA_CH_SR_RI; 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci continue; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci hw_ops->disable_int(channel, int_id); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic irqreturn_t xlgmac_isr(int irq, void *data) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci unsigned int dma_isr, dma_ch_isr, mac_isr; 2548c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = data; 2558c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 2568c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops; 2578c2ecf20Sopenharmony_ci unsigned int i, ti, ri; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci hw_ops = &pdata->hw_ops; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* The DMA interrupt status register also reports MAC and MTL 2628c2ecf20Sopenharmony_ci * interrupts. So for polling mode, we just need to check for 2638c2ecf20Sopenharmony_ci * this register to be non-zero 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci dma_isr = readl(pdata->mac_regs + DMA_ISR); 2668c2ecf20Sopenharmony_ci if (!dma_isr) 2678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci netif_dbg(pdata, intr, pdata->netdev, "DMA_ISR=%#010x\n", dma_isr); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++) { 2728c2ecf20Sopenharmony_ci if (!(dma_isr & (1 << i))) 2738c2ecf20Sopenharmony_ci continue; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci channel = pdata->channel_head + i; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dma_ch_isr = readl(XLGMAC_DMA_REG(channel, DMA_CH_SR)); 2788c2ecf20Sopenharmony_ci netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n", 2798c2ecf20Sopenharmony_ci i, dma_ch_isr); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* The TI or RI interrupt bits may still be set even if using 2828c2ecf20Sopenharmony_ci * per channel DMA interrupts. Check to be sure those are not 2838c2ecf20Sopenharmony_ci * enabled before using the private data napi structure. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci ti = XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TI_POS, 2868c2ecf20Sopenharmony_ci DMA_CH_SR_TI_LEN); 2878c2ecf20Sopenharmony_ci ri = XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RI_POS, 2888c2ecf20Sopenharmony_ci DMA_CH_SR_RI_LEN); 2898c2ecf20Sopenharmony_ci if (!pdata->per_channel_irq && (ti || ri)) { 2908c2ecf20Sopenharmony_ci if (napi_schedule_prep(&pdata->napi)) { 2918c2ecf20Sopenharmony_ci /* Disable Tx and Rx interrupts */ 2928c2ecf20Sopenharmony_ci xlgmac_disable_rx_tx_ints(pdata); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci pdata->stats.napi_poll_isr++; 2958c2ecf20Sopenharmony_ci /* Turn on polling */ 2968c2ecf20Sopenharmony_ci __napi_schedule_irqoff(&pdata->napi); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TPS_POS, 3018c2ecf20Sopenharmony_ci DMA_CH_SR_TPS_LEN)) 3028c2ecf20Sopenharmony_ci pdata->stats.tx_process_stopped++; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RPS_POS, 3058c2ecf20Sopenharmony_ci DMA_CH_SR_RPS_LEN)) 3068c2ecf20Sopenharmony_ci pdata->stats.rx_process_stopped++; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TBU_POS, 3098c2ecf20Sopenharmony_ci DMA_CH_SR_TBU_LEN)) 3108c2ecf20Sopenharmony_ci pdata->stats.tx_buffer_unavailable++; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RBU_POS, 3138c2ecf20Sopenharmony_ci DMA_CH_SR_RBU_LEN)) 3148c2ecf20Sopenharmony_ci pdata->stats.rx_buffer_unavailable++; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Restart the device on a Fatal Bus Error */ 3178c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_FBE_POS, 3188c2ecf20Sopenharmony_ci DMA_CH_SR_FBE_LEN)) { 3198c2ecf20Sopenharmony_ci pdata->stats.fatal_bus_error++; 3208c2ecf20Sopenharmony_ci schedule_work(&pdata->restart_work); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Clear all interrupt signals */ 3248c2ecf20Sopenharmony_ci writel(dma_ch_isr, XLGMAC_DMA_REG(channel, DMA_CH_SR)); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(dma_isr, DMA_ISR_MACIS_POS, 3288c2ecf20Sopenharmony_ci DMA_ISR_MACIS_LEN)) { 3298c2ecf20Sopenharmony_ci mac_isr = readl(pdata->mac_regs + MAC_ISR); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCTXIS_POS, 3328c2ecf20Sopenharmony_ci MAC_ISR_MMCTXIS_LEN)) 3338c2ecf20Sopenharmony_ci hw_ops->tx_mmc_int(pdata); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCRXIS_POS, 3368c2ecf20Sopenharmony_ci MAC_ISR_MMCRXIS_LEN)) 3378c2ecf20Sopenharmony_ci hw_ops->rx_mmc_int(pdata); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic irqreturn_t xlgmac_dma_isr(int irq, void *data) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct xlgmac_channel *channel = data; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Per channel DMA interrupts are enabled, so we use the per 3488c2ecf20Sopenharmony_ci * channel napi structure and not the private data napi structure 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci if (napi_schedule_prep(&channel->napi)) { 3518c2ecf20Sopenharmony_ci /* Disable Tx and Rx interrupts */ 3528c2ecf20Sopenharmony_ci disable_irq_nosync(channel->dma_irq); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Turn on polling */ 3558c2ecf20Sopenharmony_ci __napi_schedule_irqoff(&channel->napi); 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void xlgmac_tx_timer(struct timer_list *t) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct xlgmac_channel *channel = from_timer(channel, t, tx_timer); 3648c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = channel->pdata; 3658c2ecf20Sopenharmony_ci struct napi_struct *napi; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (napi_schedule_prep(napi)) { 3708c2ecf20Sopenharmony_ci /* Disable Tx and Rx interrupts */ 3718c2ecf20Sopenharmony_ci if (pdata->per_channel_irq) 3728c2ecf20Sopenharmony_ci disable_irq_nosync(channel->dma_irq); 3738c2ecf20Sopenharmony_ci else 3748c2ecf20Sopenharmony_ci xlgmac_disable_rx_tx_ints(pdata); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci pdata->stats.napi_poll_txtimer++; 3778c2ecf20Sopenharmony_ci /* Turn on polling */ 3788c2ecf20Sopenharmony_ci __napi_schedule(napi); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci channel->tx_timer_active = 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void xlgmac_init_timers(struct xlgmac_pdata *pdata) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 3878c2ecf20Sopenharmony_ci unsigned int i; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci channel = pdata->channel_head; 3908c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 3918c2ecf20Sopenharmony_ci if (!channel->tx_ring) 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci timer_setup(&channel->tx_timer, xlgmac_tx_timer, 0); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void xlgmac_stop_timers(struct xlgmac_pdata *pdata) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 4018c2ecf20Sopenharmony_ci unsigned int i; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci channel = pdata->channel_head; 4048c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 4058c2ecf20Sopenharmony_ci if (!channel->tx_ring) 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci del_timer_sync(&channel->tx_timer); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void xlgmac_napi_enable(struct xlgmac_pdata *pdata, unsigned int add) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 4158c2ecf20Sopenharmony_ci unsigned int i; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (pdata->per_channel_irq) { 4188c2ecf20Sopenharmony_ci channel = pdata->channel_head; 4198c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 4208c2ecf20Sopenharmony_ci if (add) 4218c2ecf20Sopenharmony_ci netif_napi_add(pdata->netdev, &channel->napi, 4228c2ecf20Sopenharmony_ci xlgmac_one_poll, 4238c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci napi_enable(&channel->napi); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } else { 4288c2ecf20Sopenharmony_ci if (add) 4298c2ecf20Sopenharmony_ci netif_napi_add(pdata->netdev, &pdata->napi, 4308c2ecf20Sopenharmony_ci xlgmac_all_poll, NAPI_POLL_WEIGHT); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci napi_enable(&pdata->napi); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void xlgmac_napi_disable(struct xlgmac_pdata *pdata, unsigned int del) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 4398c2ecf20Sopenharmony_ci unsigned int i; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (pdata->per_channel_irq) { 4428c2ecf20Sopenharmony_ci channel = pdata->channel_head; 4438c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 4448c2ecf20Sopenharmony_ci napi_disable(&channel->napi); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (del) 4478c2ecf20Sopenharmony_ci netif_napi_del(&channel->napi); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci napi_disable(&pdata->napi); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (del) 4538c2ecf20Sopenharmony_ci netif_napi_del(&pdata->napi); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int xlgmac_request_irqs(struct xlgmac_pdata *pdata) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct net_device *netdev = pdata->netdev; 4608c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 4618c2ecf20Sopenharmony_ci unsigned int i; 4628c2ecf20Sopenharmony_ci int ret; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = devm_request_irq(pdata->dev, pdata->dev_irq, xlgmac_isr, 4658c2ecf20Sopenharmony_ci IRQF_SHARED, netdev->name, pdata); 4668c2ecf20Sopenharmony_ci if (ret) { 4678c2ecf20Sopenharmony_ci netdev_alert(netdev, "error requesting irq %d\n", 4688c2ecf20Sopenharmony_ci pdata->dev_irq); 4698c2ecf20Sopenharmony_ci return ret; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (!pdata->per_channel_irq) 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci channel = pdata->channel_head; 4768c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 4778c2ecf20Sopenharmony_ci snprintf(channel->dma_irq_name, 4788c2ecf20Sopenharmony_ci sizeof(channel->dma_irq_name) - 1, 4798c2ecf20Sopenharmony_ci "%s-TxRx-%u", netdev_name(netdev), 4808c2ecf20Sopenharmony_ci channel->queue_index); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = devm_request_irq(pdata->dev, channel->dma_irq, 4838c2ecf20Sopenharmony_ci xlgmac_dma_isr, 0, 4848c2ecf20Sopenharmony_ci channel->dma_irq_name, channel); 4858c2ecf20Sopenharmony_ci if (ret) { 4868c2ecf20Sopenharmony_ci netdev_alert(netdev, "error requesting irq %d\n", 4878c2ecf20Sopenharmony_ci channel->dma_irq); 4888c2ecf20Sopenharmony_ci goto err_irq; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cierr_irq: 4958c2ecf20Sopenharmony_ci /* Using an unsigned int, 'i' will go to UINT_MAX and exit */ 4968c2ecf20Sopenharmony_ci for (i--, channel--; i < pdata->channel_count; i--, channel--) 4978c2ecf20Sopenharmony_ci devm_free_irq(pdata->dev, channel->dma_irq, channel); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci devm_free_irq(pdata->dev, pdata->dev_irq, pdata); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void xlgmac_free_irqs(struct xlgmac_pdata *pdata) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 5078c2ecf20Sopenharmony_ci unsigned int i; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci devm_free_irq(pdata->dev, pdata->dev_irq, pdata); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!pdata->per_channel_irq) 5128c2ecf20Sopenharmony_ci return; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci channel = pdata->channel_head; 5158c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) 5168c2ecf20Sopenharmony_ci devm_free_irq(pdata->dev, channel->dma_irq, channel); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void xlgmac_free_tx_data(struct xlgmac_pdata *pdata) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops; 5228c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 5238c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 5248c2ecf20Sopenharmony_ci struct xlgmac_ring *ring; 5258c2ecf20Sopenharmony_ci unsigned int i, j; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci channel = pdata->channel_head; 5288c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 5298c2ecf20Sopenharmony_ci ring = channel->tx_ring; 5308c2ecf20Sopenharmony_ci if (!ring) 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci for (j = 0; j < ring->dma_desc_count; j++) { 5348c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, j); 5358c2ecf20Sopenharmony_ci desc_ops->unmap_desc_data(pdata, desc_data); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic void xlgmac_free_rx_data(struct xlgmac_pdata *pdata) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops; 5438c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 5448c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 5458c2ecf20Sopenharmony_ci struct xlgmac_ring *ring; 5468c2ecf20Sopenharmony_ci unsigned int i, j; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci channel = pdata->channel_head; 5498c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 5508c2ecf20Sopenharmony_ci ring = channel->rx_ring; 5518c2ecf20Sopenharmony_ci if (!ring) 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (j = 0; j < ring->dma_desc_count; j++) { 5558c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, j); 5568c2ecf20Sopenharmony_ci desc_ops->unmap_desc_data(pdata, desc_data); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int xlgmac_start(struct xlgmac_pdata *pdata) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 5648c2ecf20Sopenharmony_ci struct net_device *netdev = pdata->netdev; 5658c2ecf20Sopenharmony_ci int ret; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci hw_ops->init(pdata); 5688c2ecf20Sopenharmony_ci xlgmac_napi_enable(pdata, 1); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ret = xlgmac_request_irqs(pdata); 5718c2ecf20Sopenharmony_ci if (ret) 5728c2ecf20Sopenharmony_ci goto err_napi; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci hw_ops->enable_tx(pdata); 5758c2ecf20Sopenharmony_ci hw_ops->enable_rx(pdata); 5768c2ecf20Sopenharmony_ci netif_tx_start_all_queues(netdev); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cierr_napi: 5818c2ecf20Sopenharmony_ci xlgmac_napi_disable(pdata, 1); 5828c2ecf20Sopenharmony_ci hw_ops->exit(pdata); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return ret; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic void xlgmac_stop(struct xlgmac_pdata *pdata) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 5908c2ecf20Sopenharmony_ci struct net_device *netdev = pdata->netdev; 5918c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 5928c2ecf20Sopenharmony_ci struct netdev_queue *txq; 5938c2ecf20Sopenharmony_ci unsigned int i; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(netdev); 5968c2ecf20Sopenharmony_ci xlgmac_stop_timers(pdata); 5978c2ecf20Sopenharmony_ci hw_ops->disable_tx(pdata); 5988c2ecf20Sopenharmony_ci hw_ops->disable_rx(pdata); 5998c2ecf20Sopenharmony_ci xlgmac_free_irqs(pdata); 6008c2ecf20Sopenharmony_ci xlgmac_napi_disable(pdata, 1); 6018c2ecf20Sopenharmony_ci hw_ops->exit(pdata); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci channel = pdata->channel_head; 6048c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 6058c2ecf20Sopenharmony_ci if (!channel->tx_ring) 6068c2ecf20Sopenharmony_ci continue; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci txq = netdev_get_tx_queue(netdev, channel->queue_index); 6098c2ecf20Sopenharmony_ci netdev_tx_reset_queue(txq); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic void xlgmac_restart_dev(struct xlgmac_pdata *pdata) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci /* If not running, "restart" will happen on open */ 6168c2ecf20Sopenharmony_ci if (!netif_running(pdata->netdev)) 6178c2ecf20Sopenharmony_ci return; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci xlgmac_stop(pdata); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci xlgmac_free_tx_data(pdata); 6228c2ecf20Sopenharmony_ci xlgmac_free_rx_data(pdata); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci xlgmac_start(pdata); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void xlgmac_restart(struct work_struct *work) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = container_of(work, 6308c2ecf20Sopenharmony_ci struct xlgmac_pdata, 6318c2ecf20Sopenharmony_ci restart_work); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci rtnl_lock(); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci xlgmac_restart_dev(pdata); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci rtnl_unlock(); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int xlgmac_open(struct net_device *netdev) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 6438c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops; 6448c2ecf20Sopenharmony_ci int ret; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci desc_ops = &pdata->desc_ops; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* TODO: Initialize the phy */ 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Calculate the Rx buffer size before allocating rings */ 6518c2ecf20Sopenharmony_ci ret = xlgmac_calc_rx_buf_size(netdev, netdev->mtu); 6528c2ecf20Sopenharmony_ci if (ret < 0) 6538c2ecf20Sopenharmony_ci return ret; 6548c2ecf20Sopenharmony_ci pdata->rx_buf_size = ret; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Allocate the channels and rings */ 6578c2ecf20Sopenharmony_ci ret = desc_ops->alloc_channles_and_rings(pdata); 6588c2ecf20Sopenharmony_ci if (ret) 6598c2ecf20Sopenharmony_ci return ret; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci INIT_WORK(&pdata->restart_work, xlgmac_restart); 6628c2ecf20Sopenharmony_ci xlgmac_init_timers(pdata); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret = xlgmac_start(pdata); 6658c2ecf20Sopenharmony_ci if (ret) 6668c2ecf20Sopenharmony_ci goto err_channels_and_rings; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cierr_channels_and_rings: 6718c2ecf20Sopenharmony_ci desc_ops->free_channels_and_rings(pdata); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int xlgmac_close(struct net_device *netdev) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 6798c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci desc_ops = &pdata->desc_ops; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* Stop the device */ 6848c2ecf20Sopenharmony_ci xlgmac_stop(pdata); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* Free the channels and rings */ 6878c2ecf20Sopenharmony_ci desc_ops->free_channels_and_rings(pdata); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void xlgmac_tx_timeout(struct net_device *netdev, unsigned int txqueue) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci netdev_warn(netdev, "tx timeout, device restarting\n"); 6978c2ecf20Sopenharmony_ci schedule_work(&pdata->restart_work); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic netdev_tx_t xlgmac_xmit(struct sk_buff *skb, struct net_device *netdev) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 7038c2ecf20Sopenharmony_ci struct xlgmac_pkt_info *tx_pkt_info; 7048c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops; 7058c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 7068c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops; 7078c2ecf20Sopenharmony_ci struct netdev_queue *txq; 7088c2ecf20Sopenharmony_ci struct xlgmac_ring *ring; 7098c2ecf20Sopenharmony_ci int ret; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci desc_ops = &pdata->desc_ops; 7128c2ecf20Sopenharmony_ci hw_ops = &pdata->hw_ops; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci XLGMAC_PR("skb->len = %d\n", skb->len); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci channel = pdata->channel_head + skb->queue_mapping; 7178c2ecf20Sopenharmony_ci txq = netdev_get_tx_queue(netdev, channel->queue_index); 7188c2ecf20Sopenharmony_ci ring = channel->tx_ring; 7198c2ecf20Sopenharmony_ci tx_pkt_info = &ring->pkt_info; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (skb->len == 0) { 7228c2ecf20Sopenharmony_ci netif_err(pdata, tx_err, netdev, 7238c2ecf20Sopenharmony_ci "empty skb received from stack\n"); 7248c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7258c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* Prepare preliminary packet info for TX */ 7298c2ecf20Sopenharmony_ci memset(tx_pkt_info, 0, sizeof(*tx_pkt_info)); 7308c2ecf20Sopenharmony_ci xlgmac_prep_tx_pkt(pdata, ring, skb, tx_pkt_info); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* Check that there are enough descriptors available */ 7338c2ecf20Sopenharmony_ci ret = xlgmac_maybe_stop_tx_queue(channel, ring, 7348c2ecf20Sopenharmony_ci tx_pkt_info->desc_count); 7358c2ecf20Sopenharmony_ci if (ret) 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci ret = xlgmac_prep_tso(skb, tx_pkt_info); 7398c2ecf20Sopenharmony_ci if (ret) { 7408c2ecf20Sopenharmony_ci netif_err(pdata, tx_err, netdev, 7418c2ecf20Sopenharmony_ci "error processing TSO packet\n"); 7428c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7438c2ecf20Sopenharmony_ci return ret; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci xlgmac_prep_vlan(skb, tx_pkt_info); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!desc_ops->map_tx_skb(channel, skb)) { 7488c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7498c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Report on the actual number of bytes (to be) sent */ 7538c2ecf20Sopenharmony_ci netdev_tx_sent_queue(txq, tx_pkt_info->tx_bytes); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* Configure required descriptor fields for transmission */ 7568c2ecf20Sopenharmony_ci hw_ops->dev_xmit(channel); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (netif_msg_pktdata(pdata)) 7598c2ecf20Sopenharmony_ci xlgmac_print_pkt(netdev, skb, true); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* Stop the queue in advance if there may not be enough descriptors */ 7628c2ecf20Sopenharmony_ci xlgmac_maybe_stop_tx_queue(channel, ring, XLGMAC_TX_MAX_DESC_NR); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic void xlgmac_get_stats64(struct net_device *netdev, 7688c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *s) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 7718c2ecf20Sopenharmony_ci struct xlgmac_stats *pstats = &pdata->stats; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci pdata->hw_ops.read_mmc_stats(pdata); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci s->rx_packets = pstats->rxframecount_gb; 7768c2ecf20Sopenharmony_ci s->rx_bytes = pstats->rxoctetcount_gb; 7778c2ecf20Sopenharmony_ci s->rx_errors = pstats->rxframecount_gb - 7788c2ecf20Sopenharmony_ci pstats->rxbroadcastframes_g - 7798c2ecf20Sopenharmony_ci pstats->rxmulticastframes_g - 7808c2ecf20Sopenharmony_ci pstats->rxunicastframes_g; 7818c2ecf20Sopenharmony_ci s->multicast = pstats->rxmulticastframes_g; 7828c2ecf20Sopenharmony_ci s->rx_length_errors = pstats->rxlengtherror; 7838c2ecf20Sopenharmony_ci s->rx_crc_errors = pstats->rxcrcerror; 7848c2ecf20Sopenharmony_ci s->rx_fifo_errors = pstats->rxfifooverflow; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci s->tx_packets = pstats->txframecount_gb; 7878c2ecf20Sopenharmony_ci s->tx_bytes = pstats->txoctetcount_gb; 7888c2ecf20Sopenharmony_ci s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g; 7898c2ecf20Sopenharmony_ci s->tx_dropped = netdev->stats.tx_dropped; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int xlgmac_set_mac_address(struct net_device *netdev, void *addr) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 7958c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 7968c2ecf20Sopenharmony_ci struct sockaddr *saddr = addr; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(saddr->sa_data)) 7998c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, saddr->sa_data, netdev->addr_len); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci hw_ops->set_mac_address(pdata, netdev->dev_addr); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return 0; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic int xlgmac_ioctl(struct net_device *netdev, 8098c2ecf20Sopenharmony_ci struct ifreq *ifreq, int cmd) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 8128c2ecf20Sopenharmony_ci return -ENODEV; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int xlgmac_change_mtu(struct net_device *netdev, int mtu) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 8208c2ecf20Sopenharmony_ci int ret; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci ret = xlgmac_calc_rx_buf_size(netdev, mtu); 8238c2ecf20Sopenharmony_ci if (ret < 0) 8248c2ecf20Sopenharmony_ci return ret; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci pdata->rx_buf_size = ret; 8278c2ecf20Sopenharmony_ci netdev->mtu = mtu; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci xlgmac_restart_dev(pdata); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic int xlgmac_vlan_rx_add_vid(struct net_device *netdev, 8358c2ecf20Sopenharmony_ci __be16 proto, 8368c2ecf20Sopenharmony_ci u16 vid) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 8398c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci set_bit(vid, pdata->active_vlans); 8428c2ecf20Sopenharmony_ci hw_ops->update_vlan_hash_table(pdata); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int xlgmac_vlan_rx_kill_vid(struct net_device *netdev, 8488c2ecf20Sopenharmony_ci __be16 proto, 8498c2ecf20Sopenharmony_ci u16 vid) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 8528c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci clear_bit(vid, pdata->active_vlans); 8558c2ecf20Sopenharmony_ci hw_ops->update_vlan_hash_table(pdata); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 8618c2ecf20Sopenharmony_cistatic void xlgmac_poll_controller(struct net_device *netdev) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 8648c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 8658c2ecf20Sopenharmony_ci unsigned int i; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (pdata->per_channel_irq) { 8688c2ecf20Sopenharmony_ci channel = pdata->channel_head; 8698c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) 8708c2ecf20Sopenharmony_ci xlgmac_dma_isr(channel->dma_irq, channel); 8718c2ecf20Sopenharmony_ci } else { 8728c2ecf20Sopenharmony_ci disable_irq(pdata->dev_irq); 8738c2ecf20Sopenharmony_ci xlgmac_isr(pdata->dev_irq, pdata); 8748c2ecf20Sopenharmony_ci enable_irq(pdata->dev_irq); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci#endif /* CONFIG_NET_POLL_CONTROLLER */ 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic int xlgmac_set_features(struct net_device *netdev, 8808c2ecf20Sopenharmony_ci netdev_features_t features) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter; 8838c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 8848c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 8858c2ecf20Sopenharmony_ci int ret = 0; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci rxhash = pdata->netdev_features & NETIF_F_RXHASH; 8888c2ecf20Sopenharmony_ci rxcsum = pdata->netdev_features & NETIF_F_RXCSUM; 8898c2ecf20Sopenharmony_ci rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX; 8908c2ecf20Sopenharmony_ci rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if ((features & NETIF_F_RXHASH) && !rxhash) 8938c2ecf20Sopenharmony_ci ret = hw_ops->enable_rss(pdata); 8948c2ecf20Sopenharmony_ci else if (!(features & NETIF_F_RXHASH) && rxhash) 8958c2ecf20Sopenharmony_ci ret = hw_ops->disable_rss(pdata); 8968c2ecf20Sopenharmony_ci if (ret) 8978c2ecf20Sopenharmony_ci return ret; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if ((features & NETIF_F_RXCSUM) && !rxcsum) 9008c2ecf20Sopenharmony_ci hw_ops->enable_rx_csum(pdata); 9018c2ecf20Sopenharmony_ci else if (!(features & NETIF_F_RXCSUM) && rxcsum) 9028c2ecf20Sopenharmony_ci hw_ops->disable_rx_csum(pdata); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan) 9058c2ecf20Sopenharmony_ci hw_ops->enable_rx_vlan_stripping(pdata); 9068c2ecf20Sopenharmony_ci else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan) 9078c2ecf20Sopenharmony_ci hw_ops->disable_rx_vlan_stripping(pdata); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter) 9108c2ecf20Sopenharmony_ci hw_ops->enable_rx_vlan_filtering(pdata); 9118c2ecf20Sopenharmony_ci else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter) 9128c2ecf20Sopenharmony_ci hw_ops->disable_rx_vlan_filtering(pdata); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci pdata->netdev_features = features; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic void xlgmac_set_rx_mode(struct net_device *netdev) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = netdev_priv(netdev); 9228c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci hw_ops->config_rx_mode(pdata); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic const struct net_device_ops xlgmac_netdev_ops = { 9288c2ecf20Sopenharmony_ci .ndo_open = xlgmac_open, 9298c2ecf20Sopenharmony_ci .ndo_stop = xlgmac_close, 9308c2ecf20Sopenharmony_ci .ndo_start_xmit = xlgmac_xmit, 9318c2ecf20Sopenharmony_ci .ndo_tx_timeout = xlgmac_tx_timeout, 9328c2ecf20Sopenharmony_ci .ndo_get_stats64 = xlgmac_get_stats64, 9338c2ecf20Sopenharmony_ci .ndo_change_mtu = xlgmac_change_mtu, 9348c2ecf20Sopenharmony_ci .ndo_set_mac_address = xlgmac_set_mac_address, 9358c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 9368c2ecf20Sopenharmony_ci .ndo_do_ioctl = xlgmac_ioctl, 9378c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = xlgmac_vlan_rx_add_vid, 9388c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = xlgmac_vlan_rx_kill_vid, 9398c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 9408c2ecf20Sopenharmony_ci .ndo_poll_controller = xlgmac_poll_controller, 9418c2ecf20Sopenharmony_ci#endif 9428c2ecf20Sopenharmony_ci .ndo_set_features = xlgmac_set_features, 9438c2ecf20Sopenharmony_ci .ndo_set_rx_mode = xlgmac_set_rx_mode, 9448c2ecf20Sopenharmony_ci}; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ciconst struct net_device_ops *xlgmac_get_netdev_ops(void) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci return &xlgmac_netdev_ops; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic void xlgmac_rx_refresh(struct xlgmac_channel *channel) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = channel->pdata; 9548c2ecf20Sopenharmony_ci struct xlgmac_ring *ring = channel->rx_ring; 9558c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 9568c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops; 9578c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci desc_ops = &pdata->desc_ops; 9608c2ecf20Sopenharmony_ci hw_ops = &pdata->hw_ops; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci while (ring->dirty != ring->cur) { 9638c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Reset desc_data values */ 9668c2ecf20Sopenharmony_ci desc_ops->unmap_desc_data(pdata, desc_data); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (desc_ops->map_rx_buffer(pdata, ring, desc_data)) 9698c2ecf20Sopenharmony_ci break; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci hw_ops->rx_desc_reset(pdata, desc_data, ring->dirty); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci ring->dirty++; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* Make sure everything is written before the register write */ 9778c2ecf20Sopenharmony_ci wmb(); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* Update the Rx Tail Pointer Register with address of 9808c2ecf20Sopenharmony_ci * the last cleaned entry 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty - 1); 9838c2ecf20Sopenharmony_ci writel(lower_32_bits(desc_data->dma_desc_addr), 9848c2ecf20Sopenharmony_ci XLGMAC_DMA_REG(channel, DMA_CH_RDTR_LO)); 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic struct sk_buff *xlgmac_create_skb(struct xlgmac_pdata *pdata, 9888c2ecf20Sopenharmony_ci struct napi_struct *napi, 9898c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data, 9908c2ecf20Sopenharmony_ci unsigned int len) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci unsigned int copy_len; 9938c2ecf20Sopenharmony_ci struct sk_buff *skb; 9948c2ecf20Sopenharmony_ci u8 *packet; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci skb = napi_alloc_skb(napi, desc_data->rx.hdr.dma_len); 9978c2ecf20Sopenharmony_ci if (!skb) 9988c2ecf20Sopenharmony_ci return NULL; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* Start with the header buffer which may contain just the header 10018c2ecf20Sopenharmony_ci * or the header plus data 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ci dma_sync_single_range_for_cpu(pdata->dev, desc_data->rx.hdr.dma_base, 10048c2ecf20Sopenharmony_ci desc_data->rx.hdr.dma_off, 10058c2ecf20Sopenharmony_ci desc_data->rx.hdr.dma_len, 10068c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci packet = page_address(desc_data->rx.hdr.pa.pages) + 10098c2ecf20Sopenharmony_ci desc_data->rx.hdr.pa.pages_offset; 10108c2ecf20Sopenharmony_ci copy_len = (desc_data->rx.hdr_len) ? desc_data->rx.hdr_len : len; 10118c2ecf20Sopenharmony_ci copy_len = min(desc_data->rx.hdr.dma_len, copy_len); 10128c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, packet, copy_len); 10138c2ecf20Sopenharmony_ci skb_put(skb, copy_len); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci len -= copy_len; 10168c2ecf20Sopenharmony_ci if (len) { 10178c2ecf20Sopenharmony_ci /* Add the remaining data as a frag */ 10188c2ecf20Sopenharmony_ci dma_sync_single_range_for_cpu(pdata->dev, 10198c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_base, 10208c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_off, 10218c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_len, 10228c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 10258c2ecf20Sopenharmony_ci desc_data->rx.buf.pa.pages, 10268c2ecf20Sopenharmony_ci desc_data->rx.buf.pa.pages_offset, 10278c2ecf20Sopenharmony_ci len, desc_data->rx.buf.dma_len); 10288c2ecf20Sopenharmony_ci desc_data->rx.buf.pa.pages = NULL; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci return skb; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_cistatic int xlgmac_tx_poll(struct xlgmac_channel *channel) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = channel->pdata; 10378c2ecf20Sopenharmony_ci struct xlgmac_ring *ring = channel->tx_ring; 10388c2ecf20Sopenharmony_ci struct net_device *netdev = pdata->netdev; 10398c2ecf20Sopenharmony_ci unsigned int tx_packets = 0, tx_bytes = 0; 10408c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 10418c2ecf20Sopenharmony_ci struct xlgmac_dma_desc *dma_desc; 10428c2ecf20Sopenharmony_ci struct xlgmac_desc_ops *desc_ops; 10438c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops; 10448c2ecf20Sopenharmony_ci struct netdev_queue *txq; 10458c2ecf20Sopenharmony_ci int processed = 0; 10468c2ecf20Sopenharmony_ci unsigned int cur; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci desc_ops = &pdata->desc_ops; 10498c2ecf20Sopenharmony_ci hw_ops = &pdata->hw_ops; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* Nothing to do if there isn't a Tx ring for this channel */ 10528c2ecf20Sopenharmony_ci if (!ring) 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci cur = ring->cur; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* Be sure we get ring->cur before accessing descriptor data */ 10588c2ecf20Sopenharmony_ci smp_rmb(); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci txq = netdev_get_tx_queue(netdev, channel->queue_index); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci while ((processed < XLGMAC_TX_DESC_MAX_PROC) && 10638c2ecf20Sopenharmony_ci (ring->dirty != cur)) { 10648c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty); 10658c2ecf20Sopenharmony_ci dma_desc = desc_data->dma_desc; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (!hw_ops->tx_complete(dma_desc)) 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* Make sure descriptor fields are read after reading 10718c2ecf20Sopenharmony_ci * the OWN bit 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci dma_rmb(); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (netif_msg_tx_done(pdata)) 10768c2ecf20Sopenharmony_ci xlgmac_dump_tx_desc(pdata, ring, ring->dirty, 1, 0); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (hw_ops->is_last_desc(dma_desc)) { 10798c2ecf20Sopenharmony_ci tx_packets += desc_data->tx.packets; 10808c2ecf20Sopenharmony_ci tx_bytes += desc_data->tx.bytes; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Free the SKB and reset the descriptor for re-use */ 10848c2ecf20Sopenharmony_ci desc_ops->unmap_desc_data(pdata, desc_data); 10858c2ecf20Sopenharmony_ci hw_ops->tx_desc_reset(desc_data); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci processed++; 10888c2ecf20Sopenharmony_ci ring->dirty++; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (!processed) 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci netdev_tx_completed_queue(txq, tx_packets, tx_bytes); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if ((ring->tx.queue_stopped == 1) && 10978c2ecf20Sopenharmony_ci (xlgmac_tx_avail_desc(ring) > XLGMAC_TX_DESC_MIN_FREE)) { 10988c2ecf20Sopenharmony_ci ring->tx.queue_stopped = 0; 10998c2ecf20Sopenharmony_ci netif_tx_wake_queue(txq); 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci XLGMAC_PR("processed=%d\n", processed); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci return processed; 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_cistatic int xlgmac_rx_poll(struct xlgmac_channel *channel, int budget) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = channel->pdata; 11108c2ecf20Sopenharmony_ci struct xlgmac_ring *ring = channel->rx_ring; 11118c2ecf20Sopenharmony_ci struct net_device *netdev = pdata->netdev; 11128c2ecf20Sopenharmony_ci unsigned int len, dma_desc_len, max_len; 11138c2ecf20Sopenharmony_ci unsigned int context_next, context; 11148c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 11158c2ecf20Sopenharmony_ci struct xlgmac_pkt_info *pkt_info; 11168c2ecf20Sopenharmony_ci unsigned int incomplete, error; 11178c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops; 11188c2ecf20Sopenharmony_ci unsigned int received = 0; 11198c2ecf20Sopenharmony_ci struct napi_struct *napi; 11208c2ecf20Sopenharmony_ci struct sk_buff *skb; 11218c2ecf20Sopenharmony_ci int packet_count = 0; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci hw_ops = &pdata->hw_ops; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Nothing to do if there isn't a Rx ring for this channel */ 11268c2ecf20Sopenharmony_ci if (!ring) 11278c2ecf20Sopenharmony_ci return 0; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci incomplete = 0; 11308c2ecf20Sopenharmony_ci context_next = 0; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); 11358c2ecf20Sopenharmony_ci pkt_info = &ring->pkt_info; 11368c2ecf20Sopenharmony_ci while (packet_count < budget) { 11378c2ecf20Sopenharmony_ci /* First time in loop see if we need to restore state */ 11388c2ecf20Sopenharmony_ci if (!received && desc_data->state_saved) { 11398c2ecf20Sopenharmony_ci skb = desc_data->state.skb; 11408c2ecf20Sopenharmony_ci error = desc_data->state.error; 11418c2ecf20Sopenharmony_ci len = desc_data->state.len; 11428c2ecf20Sopenharmony_ci } else { 11438c2ecf20Sopenharmony_ci memset(pkt_info, 0, sizeof(*pkt_info)); 11448c2ecf20Sopenharmony_ci skb = NULL; 11458c2ecf20Sopenharmony_ci error = 0; 11468c2ecf20Sopenharmony_ci len = 0; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ciread_again: 11508c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (xlgmac_rx_dirty_desc(ring) > XLGMAC_RX_DESC_MAX_DIRTY) 11538c2ecf20Sopenharmony_ci xlgmac_rx_refresh(channel); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (hw_ops->dev_read(channel)) 11568c2ecf20Sopenharmony_ci break; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci received++; 11598c2ecf20Sopenharmony_ci ring->cur++; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci incomplete = XLGMAC_GET_REG_BITS( 11628c2ecf20Sopenharmony_ci pkt_info->attributes, 11638c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, 11648c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN); 11658c2ecf20Sopenharmony_ci context_next = XLGMAC_GET_REG_BITS( 11668c2ecf20Sopenharmony_ci pkt_info->attributes, 11678c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS, 11688c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN); 11698c2ecf20Sopenharmony_ci context = XLGMAC_GET_REG_BITS( 11708c2ecf20Sopenharmony_ci pkt_info->attributes, 11718c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_CONTEXT_POS, 11728c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_CONTEXT_LEN); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* Earlier error, just drain the remaining data */ 11758c2ecf20Sopenharmony_ci if ((incomplete || context_next) && error) 11768c2ecf20Sopenharmony_ci goto read_again; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (error || pkt_info->errors) { 11798c2ecf20Sopenharmony_ci if (pkt_info->errors) 11808c2ecf20Sopenharmony_ci netif_err(pdata, rx_err, netdev, 11818c2ecf20Sopenharmony_ci "error in received packet\n"); 11828c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 11838c2ecf20Sopenharmony_ci goto next_packet; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (!context) { 11878c2ecf20Sopenharmony_ci /* Length is cumulative, get this descriptor's length */ 11888c2ecf20Sopenharmony_ci dma_desc_len = desc_data->rx.len - len; 11898c2ecf20Sopenharmony_ci len += dma_desc_len; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (dma_desc_len && !skb) { 11928c2ecf20Sopenharmony_ci skb = xlgmac_create_skb(pdata, napi, desc_data, 11938c2ecf20Sopenharmony_ci dma_desc_len); 11948c2ecf20Sopenharmony_ci if (!skb) 11958c2ecf20Sopenharmony_ci error = 1; 11968c2ecf20Sopenharmony_ci } else if (dma_desc_len) { 11978c2ecf20Sopenharmony_ci dma_sync_single_range_for_cpu( 11988c2ecf20Sopenharmony_ci pdata->dev, 11998c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_base, 12008c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_off, 12018c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_len, 12028c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci skb_add_rx_frag( 12058c2ecf20Sopenharmony_ci skb, skb_shinfo(skb)->nr_frags, 12068c2ecf20Sopenharmony_ci desc_data->rx.buf.pa.pages, 12078c2ecf20Sopenharmony_ci desc_data->rx.buf.pa.pages_offset, 12088c2ecf20Sopenharmony_ci dma_desc_len, 12098c2ecf20Sopenharmony_ci desc_data->rx.buf.dma_len); 12108c2ecf20Sopenharmony_ci desc_data->rx.buf.pa.pages = NULL; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (incomplete || context_next) 12158c2ecf20Sopenharmony_ci goto read_again; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (!skb) 12188c2ecf20Sopenharmony_ci goto next_packet; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Be sure we don't exceed the configured MTU */ 12218c2ecf20Sopenharmony_ci max_len = netdev->mtu + ETH_HLEN; 12228c2ecf20Sopenharmony_ci if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && 12238c2ecf20Sopenharmony_ci (skb->protocol == htons(ETH_P_8021Q))) 12248c2ecf20Sopenharmony_ci max_len += VLAN_HLEN; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (skb->len > max_len) { 12278c2ecf20Sopenharmony_ci netif_err(pdata, rx_err, netdev, 12288c2ecf20Sopenharmony_ci "packet length exceeds configured MTU\n"); 12298c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 12308c2ecf20Sopenharmony_ci goto next_packet; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (netif_msg_pktdata(pdata)) 12348c2ecf20Sopenharmony_ci xlgmac_print_pkt(netdev, skb, false); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 12378c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(pkt_info->attributes, 12388c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_CSUM_DONE_POS, 12398c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN)) 12408c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(pkt_info->attributes, 12438c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, 12448c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) { 12458c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 12468c2ecf20Sopenharmony_ci pkt_info->vlan_ctag); 12478c2ecf20Sopenharmony_ci pdata->stats.rx_vlan_packets++; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (XLGMAC_GET_REG_BITS(pkt_info->attributes, 12518c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_RSS_HASH_POS, 12528c2ecf20Sopenharmony_ci RX_PACKET_ATTRIBUTES_RSS_HASH_LEN)) 12538c2ecf20Sopenharmony_ci skb_set_hash(skb, pkt_info->rss_hash, 12548c2ecf20Sopenharmony_ci pkt_info->rss_hash_type); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci skb->dev = netdev; 12578c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 12588c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, channel->queue_index); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci napi_gro_receive(napi, skb); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cinext_packet: 12638c2ecf20Sopenharmony_ci packet_count++; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Check if we need to save state before leaving */ 12678c2ecf20Sopenharmony_ci if (received && (incomplete || context_next)) { 12688c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); 12698c2ecf20Sopenharmony_ci desc_data->state_saved = 1; 12708c2ecf20Sopenharmony_ci desc_data->state.skb = skb; 12718c2ecf20Sopenharmony_ci desc_data->state.len = len; 12728c2ecf20Sopenharmony_ci desc_data->state.error = error; 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci XLGMAC_PR("packet_count = %d\n", packet_count); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci return packet_count; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic int xlgmac_one_poll(struct napi_struct *napi, int budget) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci struct xlgmac_channel *channel = container_of(napi, 12838c2ecf20Sopenharmony_ci struct xlgmac_channel, 12848c2ecf20Sopenharmony_ci napi); 12858c2ecf20Sopenharmony_ci int processed = 0; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci XLGMAC_PR("budget=%d\n", budget); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci /* Cleanup Tx ring first */ 12908c2ecf20Sopenharmony_ci xlgmac_tx_poll(channel); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci /* Process Rx ring next */ 12938c2ecf20Sopenharmony_ci processed = xlgmac_rx_poll(channel, budget); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* If we processed everything, we are done */ 12968c2ecf20Sopenharmony_ci if (processed < budget) { 12978c2ecf20Sopenharmony_ci /* Turn off polling */ 12988c2ecf20Sopenharmony_ci napi_complete_done(napi, processed); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* Enable Tx and Rx interrupts */ 13018c2ecf20Sopenharmony_ci enable_irq(channel->dma_irq); 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci XLGMAC_PR("received = %d\n", processed); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci return processed; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int xlgmac_all_poll(struct napi_struct *napi, int budget) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = container_of(napi, 13128c2ecf20Sopenharmony_ci struct xlgmac_pdata, 13138c2ecf20Sopenharmony_ci napi); 13148c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 13158c2ecf20Sopenharmony_ci int processed, last_processed; 13168c2ecf20Sopenharmony_ci int ring_budget; 13178c2ecf20Sopenharmony_ci unsigned int i; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci XLGMAC_PR("budget=%d\n", budget); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci processed = 0; 13228c2ecf20Sopenharmony_ci ring_budget = budget / pdata->rx_ring_count; 13238c2ecf20Sopenharmony_ci do { 13248c2ecf20Sopenharmony_ci last_processed = processed; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci channel = pdata->channel_head; 13278c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 13288c2ecf20Sopenharmony_ci /* Cleanup Tx ring first */ 13298c2ecf20Sopenharmony_ci xlgmac_tx_poll(channel); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* Process Rx ring next */ 13328c2ecf20Sopenharmony_ci if (ring_budget > (budget - processed)) 13338c2ecf20Sopenharmony_ci ring_budget = budget - processed; 13348c2ecf20Sopenharmony_ci processed += xlgmac_rx_poll(channel, ring_budget); 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci } while ((processed < budget) && (processed != last_processed)); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci /* If we processed everything, we are done */ 13398c2ecf20Sopenharmony_ci if (processed < budget) { 13408c2ecf20Sopenharmony_ci /* Turn off polling */ 13418c2ecf20Sopenharmony_ci napi_complete_done(napi, processed); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* Enable Tx and Rx interrupts */ 13448c2ecf20Sopenharmony_ci xlgmac_enable_rx_tx_ints(pdata); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci XLGMAC_PR("received = %d\n", processed); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci return processed; 13508c2ecf20Sopenharmony_ci} 1351