162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bitfield.h> 562306a36Sopenharmony_ci#include <linux/dmapool.h> 662306a36Sopenharmony_ci#include <linux/etherdevice.h> 762306a36Sopenharmony_ci#include <linux/if_vlan.h> 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "prestera_dsa.h" 1162306a36Sopenharmony_ci#include "prestera.h" 1262306a36Sopenharmony_ci#include "prestera_hw.h" 1362306a36Sopenharmony_ci#include "prestera_rxtx.h" 1462306a36Sopenharmony_ci#include "prestera_devlink.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define PRESTERA_SDMA_WAIT_MUL 10 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct prestera_sdma_desc { 1962306a36Sopenharmony_ci __le32 word1; 2062306a36Sopenharmony_ci __le32 word2; 2162306a36Sopenharmony_ci __le32 buff; 2262306a36Sopenharmony_ci __le32 next; 2362306a36Sopenharmony_ci} __packed __aligned(16); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define PRESTERA_SDMA_BUFF_SIZE_MAX 1544 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_PKT_LEN(desc) \ 2862306a36Sopenharmony_ci ((le32_to_cpu((desc)->word2) >> 16) & GENMASK(13, 0)) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_OWNER(desc) \ 3162306a36Sopenharmony_ci ((le32_to_cpu((desc)->word1) & BIT(31)) >> 31) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_IS_RCVD(desc) \ 3462306a36Sopenharmony_ci (PRESTERA_SDMA_RX_DESC_OWNER(desc) == PRESTERA_SDMA_RX_DESC_CPU_OWN) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_CPU_OWN 0 3762306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_DMA_OWN 1 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_NUM 8 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_PER_Q 1000 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_PER_Q 1000 4462306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_MAX_BURST 64 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_OWNER(desc) \ 4762306a36Sopenharmony_ci ((le32_to_cpu((desc)->word1) & BIT(31)) >> 31) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_CPU_OWN 0 5062306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_DMA_OWN 1U 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_IS_SENT(desc) \ 5362306a36Sopenharmony_ci (PRESTERA_SDMA_TX_DESC_OWNER(desc) == PRESTERA_SDMA_TX_DESC_CPU_OWN) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_LAST BIT(20) 5662306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_FIRST BIT(21) 5762306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_CALC_CRC BIT(12) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_SINGLE \ 6062306a36Sopenharmony_ci (PRESTERA_SDMA_TX_DESC_FIRST | PRESTERA_SDMA_TX_DESC_LAST) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_INIT \ 6362306a36Sopenharmony_ci (PRESTERA_SDMA_TX_DESC_SINGLE | PRESTERA_SDMA_TX_DESC_CALC_CRC) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_INTR_MASK_REG 0x2814 6662306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_STATUS_REG 0x2680 6762306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_DESC_REG(n) (0x260C + (n) * 16) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_QUEUE_DESC_REG 0x26C0 7062306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_QUEUE_START_REG 0x2868 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct prestera_sdma_buf { 7362306a36Sopenharmony_ci struct prestera_sdma_desc *desc; 7462306a36Sopenharmony_ci dma_addr_t desc_dma; 7562306a36Sopenharmony_ci struct sk_buff *skb; 7662306a36Sopenharmony_ci dma_addr_t buf_dma; 7762306a36Sopenharmony_ci bool is_used; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct prestera_rx_ring { 8162306a36Sopenharmony_ci struct prestera_sdma_buf *bufs; 8262306a36Sopenharmony_ci int next_rx; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct prestera_tx_ring { 8662306a36Sopenharmony_ci struct prestera_sdma_buf *bufs; 8762306a36Sopenharmony_ci int next_tx; 8862306a36Sopenharmony_ci int max_burst; 8962306a36Sopenharmony_ci int burst; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct prestera_sdma { 9362306a36Sopenharmony_ci struct prestera_rx_ring rx_ring[PRESTERA_SDMA_RX_QUEUE_NUM]; 9462306a36Sopenharmony_ci struct prestera_tx_ring tx_ring; 9562306a36Sopenharmony_ci struct prestera_switch *sw; 9662306a36Sopenharmony_ci struct dma_pool *desc_pool; 9762306a36Sopenharmony_ci struct work_struct tx_work; 9862306a36Sopenharmony_ci struct napi_struct rx_napi; 9962306a36Sopenharmony_ci struct net_device napi_dev; 10062306a36Sopenharmony_ci u32 map_addr; 10162306a36Sopenharmony_ci u64 dma_mask; 10262306a36Sopenharmony_ci /* protect SDMA with concurrent access from multiple CPUs */ 10362306a36Sopenharmony_ci spinlock_t tx_lock; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct prestera_rxtx { 10762306a36Sopenharmony_ci struct prestera_sdma sdma; 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int prestera_sdma_buf_init(struct prestera_sdma *sdma, 11162306a36Sopenharmony_ci struct prestera_sdma_buf *buf) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct prestera_sdma_desc *desc; 11462306a36Sopenharmony_ci dma_addr_t dma; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci desc = dma_pool_alloc(sdma->desc_pool, GFP_DMA | GFP_KERNEL, &dma); 11762306a36Sopenharmony_ci if (!desc) 11862306a36Sopenharmony_ci return -ENOMEM; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci buf->buf_dma = DMA_MAPPING_ERROR; 12162306a36Sopenharmony_ci buf->desc_dma = dma; 12262306a36Sopenharmony_ci buf->desc = desc; 12362306a36Sopenharmony_ci buf->skb = NULL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic u32 prestera_sdma_map(struct prestera_sdma *sdma, dma_addr_t pa) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return sdma->map_addr + pa; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void prestera_sdma_rx_desc_init(struct prestera_sdma *sdma, 13462306a36Sopenharmony_ci struct prestera_sdma_desc *desc, 13562306a36Sopenharmony_ci dma_addr_t buf) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u32 word = le32_to_cpu(desc->word2); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci u32p_replace_bits(&word, PRESTERA_SDMA_BUFF_SIZE_MAX, GENMASK(15, 0)); 14062306a36Sopenharmony_ci desc->word2 = cpu_to_le32(word); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* make sure buffer is set before reset the descriptor */ 14562306a36Sopenharmony_ci wmb(); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci desc->word1 = cpu_to_le32(0xA0000000); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void prestera_sdma_rx_desc_set_next(struct prestera_sdma *sdma, 15162306a36Sopenharmony_ci struct prestera_sdma_desc *desc, 15262306a36Sopenharmony_ci dma_addr_t next) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci desc->next = cpu_to_le32(prestera_sdma_map(sdma, next)); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int prestera_sdma_rx_skb_alloc(struct prestera_sdma *sdma, 15862306a36Sopenharmony_ci struct prestera_sdma_buf *buf) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct device *dev = sdma->sw->dev->dev; 16162306a36Sopenharmony_ci struct sk_buff *skb; 16262306a36Sopenharmony_ci dma_addr_t dma; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci skb = alloc_skb(PRESTERA_SDMA_BUFF_SIZE_MAX, GFP_DMA | GFP_ATOMIC); 16562306a36Sopenharmony_ci if (!skb) 16662306a36Sopenharmony_ci return -ENOMEM; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci dma = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); 16962306a36Sopenharmony_ci if (dma_mapping_error(dev, dma)) 17062306a36Sopenharmony_ci goto err_dma_map; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (buf->skb) 17362306a36Sopenharmony_ci dma_unmap_single(dev, buf->buf_dma, buf->skb->len, 17462306a36Sopenharmony_ci DMA_FROM_DEVICE); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci buf->buf_dma = dma; 17762306a36Sopenharmony_ci buf->skb = skb; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cierr_dma_map: 18262306a36Sopenharmony_ci kfree_skb(skb); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return -ENOMEM; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct sk_buff *prestera_sdma_rx_skb_get(struct prestera_sdma *sdma, 18862306a36Sopenharmony_ci struct prestera_sdma_buf *buf) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci dma_addr_t buf_dma = buf->buf_dma; 19162306a36Sopenharmony_ci struct sk_buff *skb = buf->skb; 19262306a36Sopenharmony_ci u32 len = skb->len; 19362306a36Sopenharmony_ci int err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci err = prestera_sdma_rx_skb_alloc(sdma, buf); 19662306a36Sopenharmony_ci if (err) { 19762306a36Sopenharmony_ci buf->buf_dma = buf_dma; 19862306a36Sopenharmony_ci buf->skb = skb; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci skb = alloc_skb(skb->len, GFP_ATOMIC); 20162306a36Sopenharmony_ci if (skb) { 20262306a36Sopenharmony_ci skb_put(skb, len); 20362306a36Sopenharmony_ci skb_copy_from_linear_data(buf->skb, skb->data, len); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci prestera_sdma_rx_desc_init(sdma, buf->desc, buf->buf_dma); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return skb; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int prestera_rxtx_process_skb(struct prestera_sdma *sdma, 21362306a36Sopenharmony_ci struct sk_buff *skb) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct prestera_port *port; 21662306a36Sopenharmony_ci struct prestera_dsa dsa; 21762306a36Sopenharmony_ci u32 hw_port, dev_id; 21862306a36Sopenharmony_ci u8 cpu_code; 21962306a36Sopenharmony_ci int err; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci skb_pull(skb, ETH_HLEN); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* ethertype field is part of the dsa header */ 22462306a36Sopenharmony_ci err = prestera_dsa_parse(&dsa, skb->data - ETH_TLEN); 22562306a36Sopenharmony_ci if (err) 22662306a36Sopenharmony_ci return err; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dev_id = dsa.hw_dev_num; 22962306a36Sopenharmony_ci hw_port = dsa.port_num; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci port = prestera_port_find_by_hwid(sdma->sw, dev_id, hw_port); 23262306a36Sopenharmony_ci if (unlikely(!port)) { 23362306a36Sopenharmony_ci dev_warn_ratelimited(prestera_dev(sdma->sw), "received pkt for non-existent port(%u, %u)\n", 23462306a36Sopenharmony_ci dev_id, hw_port); 23562306a36Sopenharmony_ci return -ENOENT; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, PRESTERA_DSA_HLEN))) 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* remove DSA tag and update checksum */ 24262306a36Sopenharmony_ci skb_pull_rcsum(skb, PRESTERA_DSA_HLEN); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - PRESTERA_DSA_HLEN, 24562306a36Sopenharmony_ci ETH_ALEN * 2); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci skb_push(skb, ETH_HLEN); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, port->dev); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (dsa.vlan.is_tagged) { 25262306a36Sopenharmony_ci u16 tci = dsa.vlan.vid & VLAN_VID_MASK; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci tci |= dsa.vlan.vpt << VLAN_PRIO_SHIFT; 25562306a36Sopenharmony_ci if (dsa.vlan.cfi_bit) 25662306a36Sopenharmony_ci tci |= VLAN_CFI_MASK; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tci); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci cpu_code = dsa.cpu_code; 26262306a36Sopenharmony_ci prestera_devlink_trap_report(port, skb, cpu_code); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int prestera_sdma_next_rx_buf_idx(int buf_idx) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci return (buf_idx + 1) % PRESTERA_SDMA_RX_DESC_PER_Q; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int prestera_sdma_rx_poll(struct napi_struct *napi, int budget) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci int qnum = PRESTERA_SDMA_RX_QUEUE_NUM; 27562306a36Sopenharmony_ci unsigned int rxq_done_map = 0; 27662306a36Sopenharmony_ci struct prestera_sdma *sdma; 27762306a36Sopenharmony_ci struct list_head rx_list; 27862306a36Sopenharmony_ci unsigned int qmask; 27962306a36Sopenharmony_ci int pkts_done = 0; 28062306a36Sopenharmony_ci int q; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci qnum = PRESTERA_SDMA_RX_QUEUE_NUM; 28362306a36Sopenharmony_ci qmask = GENMASK(qnum - 1, 0); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci INIT_LIST_HEAD(&rx_list); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci sdma = container_of(napi, struct prestera_sdma, rx_napi); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci while (pkts_done < budget && rxq_done_map != qmask) { 29062306a36Sopenharmony_ci for (q = 0; q < qnum && pkts_done < budget; q++) { 29162306a36Sopenharmony_ci struct prestera_rx_ring *ring = &sdma->rx_ring[q]; 29262306a36Sopenharmony_ci struct prestera_sdma_desc *desc; 29362306a36Sopenharmony_ci struct prestera_sdma_buf *buf; 29462306a36Sopenharmony_ci int buf_idx = ring->next_rx; 29562306a36Sopenharmony_ci struct sk_buff *skb; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci buf = &ring->bufs[buf_idx]; 29862306a36Sopenharmony_ci desc = buf->desc; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (PRESTERA_SDMA_RX_DESC_IS_RCVD(desc)) { 30162306a36Sopenharmony_ci rxq_done_map &= ~BIT(q); 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci rxq_done_map |= BIT(q); 30462306a36Sopenharmony_ci continue; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci pkts_done++; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci __skb_trim(buf->skb, PRESTERA_SDMA_RX_DESC_PKT_LEN(desc)); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci skb = prestera_sdma_rx_skb_get(sdma, buf); 31262306a36Sopenharmony_ci if (!skb) 31362306a36Sopenharmony_ci goto rx_next_buf; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (unlikely(prestera_rxtx_process_skb(sdma, skb))) 31662306a36Sopenharmony_ci goto rx_next_buf; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci list_add_tail(&skb->list, &rx_list); 31962306a36Sopenharmony_cirx_next_buf: 32062306a36Sopenharmony_ci ring->next_rx = prestera_sdma_next_rx_buf_idx(buf_idx); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (pkts_done < budget && napi_complete_done(napi, pkts_done)) 32562306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_RX_INTR_MASK_REG, 32662306a36Sopenharmony_ci GENMASK(9, 2)); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci netif_receive_skb_list(&rx_list); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return pkts_done; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void prestera_sdma_rx_fini(struct prestera_sdma *sdma) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int qnum = PRESTERA_SDMA_RX_QUEUE_NUM; 33662306a36Sopenharmony_ci int q, b; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* disable all rx queues */ 33962306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG, 34062306a36Sopenharmony_ci GENMASK(15, 8)); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (q = 0; q < qnum; q++) { 34362306a36Sopenharmony_ci struct prestera_rx_ring *ring = &sdma->rx_ring[q]; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!ring->bufs) 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci for (b = 0; b < PRESTERA_SDMA_RX_DESC_PER_Q; b++) { 34962306a36Sopenharmony_ci struct prestera_sdma_buf *buf = &ring->bufs[b]; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (buf->desc_dma) 35262306a36Sopenharmony_ci dma_pool_free(sdma->desc_pool, buf->desc, 35362306a36Sopenharmony_ci buf->desc_dma); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!buf->skb) 35662306a36Sopenharmony_ci continue; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (buf->buf_dma != DMA_MAPPING_ERROR) 35962306a36Sopenharmony_ci dma_unmap_single(sdma->sw->dev->dev, 36062306a36Sopenharmony_ci buf->buf_dma, buf->skb->len, 36162306a36Sopenharmony_ci DMA_FROM_DEVICE); 36262306a36Sopenharmony_ci kfree_skb(buf->skb); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int prestera_sdma_rx_init(struct prestera_sdma *sdma) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci int bnum = PRESTERA_SDMA_RX_DESC_PER_Q; 37062306a36Sopenharmony_ci int qnum = PRESTERA_SDMA_RX_QUEUE_NUM; 37162306a36Sopenharmony_ci int err; 37262306a36Sopenharmony_ci int q; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* disable all rx queues */ 37562306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG, 37662306a36Sopenharmony_ci GENMASK(15, 8)); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (q = 0; q < qnum; q++) { 37962306a36Sopenharmony_ci struct prestera_sdma_buf *head, *tail, *next, *prev; 38062306a36Sopenharmony_ci struct prestera_rx_ring *ring = &sdma->rx_ring[q]; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL); 38362306a36Sopenharmony_ci if (!ring->bufs) 38462306a36Sopenharmony_ci return -ENOMEM; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ring->next_rx = 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci tail = &ring->bufs[bnum - 1]; 38962306a36Sopenharmony_ci head = &ring->bufs[0]; 39062306a36Sopenharmony_ci next = head; 39162306a36Sopenharmony_ci prev = next; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci do { 39462306a36Sopenharmony_ci err = prestera_sdma_buf_init(sdma, next); 39562306a36Sopenharmony_ci if (err) 39662306a36Sopenharmony_ci return err; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci err = prestera_sdma_rx_skb_alloc(sdma, next); 39962306a36Sopenharmony_ci if (err) 40062306a36Sopenharmony_ci return err; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci prestera_sdma_rx_desc_init(sdma, next->desc, 40362306a36Sopenharmony_ci next->buf_dma); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci prestera_sdma_rx_desc_set_next(sdma, prev->desc, 40662306a36Sopenharmony_ci next->desc_dma); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci prev = next; 40962306a36Sopenharmony_ci next++; 41062306a36Sopenharmony_ci } while (prev != tail); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* join tail with head to make a circular list */ 41362306a36Sopenharmony_ci prestera_sdma_rx_desc_set_next(sdma, tail->desc, head->desc_dma); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_DESC_REG(q), 41662306a36Sopenharmony_ci prestera_sdma_map(sdma, head->desc_dma)); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* make sure all rx descs are filled before enabling all rx queues */ 42062306a36Sopenharmony_ci wmb(); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG, 42362306a36Sopenharmony_ci GENMASK(7, 0)); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_init(struct prestera_sdma *sdma, 42962306a36Sopenharmony_ci struct prestera_sdma_desc *desc) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci desc->word1 = cpu_to_le32(PRESTERA_SDMA_TX_DESC_INIT); 43262306a36Sopenharmony_ci desc->word2 = 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_set_next(struct prestera_sdma *sdma, 43662306a36Sopenharmony_ci struct prestera_sdma_desc *desc, 43762306a36Sopenharmony_ci dma_addr_t next) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci desc->next = cpu_to_le32(prestera_sdma_map(sdma, next)); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_set_buf(struct prestera_sdma *sdma, 44362306a36Sopenharmony_ci struct prestera_sdma_desc *desc, 44462306a36Sopenharmony_ci dma_addr_t buf, size_t len) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci u32 word = le32_to_cpu(desc->word2); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci u32p_replace_bits(&word, len + ETH_FCS_LEN, GENMASK(30, 16)); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf)); 45162306a36Sopenharmony_ci desc->word2 = cpu_to_le32(word); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_xmit(struct prestera_sdma_desc *desc) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci u32 word = le32_to_cpu(desc->word1); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci word |= PRESTERA_SDMA_TX_DESC_DMA_OWN << 31; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* make sure everything is written before enable xmit */ 46162306a36Sopenharmony_ci wmb(); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci desc->word1 = cpu_to_le32(word); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int prestera_sdma_tx_buf_map(struct prestera_sdma *sdma, 46762306a36Sopenharmony_ci struct prestera_sdma_buf *buf, 46862306a36Sopenharmony_ci struct sk_buff *skb) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct device *dma_dev = sdma->sw->dev->dev; 47162306a36Sopenharmony_ci dma_addr_t dma; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci dma = dma_map_single(dma_dev, skb->data, skb->len, DMA_TO_DEVICE); 47462306a36Sopenharmony_ci if (dma_mapping_error(dma_dev, dma)) 47562306a36Sopenharmony_ci return -ENOMEM; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci buf->buf_dma = dma; 47862306a36Sopenharmony_ci buf->skb = skb; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void prestera_sdma_tx_buf_unmap(struct prestera_sdma *sdma, 48462306a36Sopenharmony_ci struct prestera_sdma_buf *buf) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct device *dma_dev = sdma->sw->dev->dev; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci dma_unmap_single(dma_dev, buf->buf_dma, buf->skb->len, DMA_TO_DEVICE); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void prestera_sdma_tx_recycle_work_fn(struct work_struct *work) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int bnum = PRESTERA_SDMA_TX_DESC_PER_Q; 49462306a36Sopenharmony_ci struct prestera_tx_ring *tx_ring; 49562306a36Sopenharmony_ci struct prestera_sdma *sdma; 49662306a36Sopenharmony_ci int b; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci sdma = container_of(work, struct prestera_sdma, tx_work); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci tx_ring = &sdma->tx_ring; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci for (b = 0; b < bnum; b++) { 50362306a36Sopenharmony_ci struct prestera_sdma_buf *buf = &tx_ring->bufs[b]; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!buf->is_used) 50662306a36Sopenharmony_ci continue; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!PRESTERA_SDMA_TX_DESC_IS_SENT(buf->desc)) 50962306a36Sopenharmony_ci continue; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci prestera_sdma_tx_buf_unmap(sdma, buf); 51262306a36Sopenharmony_ci dev_consume_skb_any(buf->skb); 51362306a36Sopenharmony_ci buf->skb = NULL; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* make sure everything is cleaned up */ 51662306a36Sopenharmony_ci wmb(); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci buf->is_used = false; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int prestera_sdma_tx_init(struct prestera_sdma *sdma) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct prestera_sdma_buf *head, *tail, *next, *prev; 52562306a36Sopenharmony_ci struct prestera_tx_ring *tx_ring = &sdma->tx_ring; 52662306a36Sopenharmony_ci int bnum = PRESTERA_SDMA_TX_DESC_PER_Q; 52762306a36Sopenharmony_ci int err; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci INIT_WORK(&sdma->tx_work, prestera_sdma_tx_recycle_work_fn); 53062306a36Sopenharmony_ci spin_lock_init(&sdma->tx_lock); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci tx_ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL); 53362306a36Sopenharmony_ci if (!tx_ring->bufs) 53462306a36Sopenharmony_ci return -ENOMEM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci tail = &tx_ring->bufs[bnum - 1]; 53762306a36Sopenharmony_ci head = &tx_ring->bufs[0]; 53862306a36Sopenharmony_ci next = head; 53962306a36Sopenharmony_ci prev = next; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci tx_ring->max_burst = PRESTERA_SDMA_TX_MAX_BURST; 54262306a36Sopenharmony_ci tx_ring->burst = tx_ring->max_burst; 54362306a36Sopenharmony_ci tx_ring->next_tx = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci do { 54662306a36Sopenharmony_ci err = prestera_sdma_buf_init(sdma, next); 54762306a36Sopenharmony_ci if (err) 54862306a36Sopenharmony_ci return err; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci next->is_used = false; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci prestera_sdma_tx_desc_init(sdma, next->desc); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci prestera_sdma_tx_desc_set_next(sdma, prev->desc, 55562306a36Sopenharmony_ci next->desc_dma); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci prev = next; 55862306a36Sopenharmony_ci next++; 55962306a36Sopenharmony_ci } while (prev != tail); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* join tail with head to make a circular list */ 56262306a36Sopenharmony_ci prestera_sdma_tx_desc_set_next(sdma, tail->desc, head->desc_dma); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* make sure descriptors are written */ 56562306a36Sopenharmony_ci wmb(); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_DESC_REG, 56862306a36Sopenharmony_ci prestera_sdma_map(sdma, head->desc_dma)); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic void prestera_sdma_tx_fini(struct prestera_sdma *sdma) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct prestera_tx_ring *ring = &sdma->tx_ring; 57662306a36Sopenharmony_ci int bnum = PRESTERA_SDMA_TX_DESC_PER_Q; 57762306a36Sopenharmony_ci int b; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci cancel_work_sync(&sdma->tx_work); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!ring->bufs) 58262306a36Sopenharmony_ci return; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (b = 0; b < bnum; b++) { 58562306a36Sopenharmony_ci struct prestera_sdma_buf *buf = &ring->bufs[b]; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (buf->desc) 58862306a36Sopenharmony_ci dma_pool_free(sdma->desc_pool, buf->desc, 58962306a36Sopenharmony_ci buf->desc_dma); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (!buf->skb) 59262306a36Sopenharmony_ci continue; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci dma_unmap_single(sdma->sw->dev->dev, buf->buf_dma, 59562306a36Sopenharmony_ci buf->skb->len, DMA_TO_DEVICE); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci dev_consume_skb_any(buf->skb); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void prestera_rxtx_handle_event(struct prestera_switch *sw, 60262306a36Sopenharmony_ci struct prestera_event *evt, 60362306a36Sopenharmony_ci void *arg) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct prestera_sdma *sdma = arg; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (evt->id != PRESTERA_RXTX_EVENT_RCV_PKT) 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_RX_INTR_MASK_REG, 0); 61162306a36Sopenharmony_ci napi_schedule(&sdma->rx_napi); 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int prestera_sdma_switch_init(struct prestera_switch *sw) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct prestera_sdma *sdma = &sw->rxtx->sdma; 61762306a36Sopenharmony_ci struct device *dev = sw->dev->dev; 61862306a36Sopenharmony_ci struct prestera_rxtx_params p; 61962306a36Sopenharmony_ci int err; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci p.use_sdma = true; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = prestera_hw_rxtx_init(sw, &p); 62462306a36Sopenharmony_ci if (err) { 62562306a36Sopenharmony_ci dev_err(dev, "failed to init rxtx by hw\n"); 62662306a36Sopenharmony_ci return err; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci sdma->dma_mask = dma_get_mask(dev); 63062306a36Sopenharmony_ci sdma->map_addr = p.map_addr; 63162306a36Sopenharmony_ci sdma->sw = sw; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci sdma->desc_pool = dma_pool_create("desc_pool", dev, 63462306a36Sopenharmony_ci sizeof(struct prestera_sdma_desc), 63562306a36Sopenharmony_ci 16, 0); 63662306a36Sopenharmony_ci if (!sdma->desc_pool) 63762306a36Sopenharmony_ci return -ENOMEM; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci err = prestera_sdma_rx_init(sdma); 64062306a36Sopenharmony_ci if (err) { 64162306a36Sopenharmony_ci dev_err(dev, "failed to init rx ring\n"); 64262306a36Sopenharmony_ci goto err_rx_init; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci err = prestera_sdma_tx_init(sdma); 64662306a36Sopenharmony_ci if (err) { 64762306a36Sopenharmony_ci dev_err(dev, "failed to init tx ring\n"); 64862306a36Sopenharmony_ci goto err_tx_init; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_RXTX, 65262306a36Sopenharmony_ci prestera_rxtx_handle_event, 65362306a36Sopenharmony_ci sdma); 65462306a36Sopenharmony_ci if (err) 65562306a36Sopenharmony_ci goto err_evt_register; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci init_dummy_netdev(&sdma->napi_dev); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll); 66062306a36Sopenharmony_ci napi_enable(&sdma->rx_napi); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cierr_evt_register: 66562306a36Sopenharmony_cierr_tx_init: 66662306a36Sopenharmony_ci prestera_sdma_tx_fini(sdma); 66762306a36Sopenharmony_cierr_rx_init: 66862306a36Sopenharmony_ci prestera_sdma_rx_fini(sdma); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci dma_pool_destroy(sdma->desc_pool); 67162306a36Sopenharmony_ci return err; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic void prestera_sdma_switch_fini(struct prestera_switch *sw) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct prestera_sdma *sdma = &sw->rxtx->sdma; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci napi_disable(&sdma->rx_napi); 67962306a36Sopenharmony_ci netif_napi_del(&sdma->rx_napi); 68062306a36Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX, 68162306a36Sopenharmony_ci prestera_rxtx_handle_event); 68262306a36Sopenharmony_ci prestera_sdma_tx_fini(sdma); 68362306a36Sopenharmony_ci prestera_sdma_rx_fini(sdma); 68462306a36Sopenharmony_ci dma_pool_destroy(sdma->desc_pool); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic bool prestera_sdma_is_ready(struct prestera_sdma *sdma) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci return !(prestera_read(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG) & 1); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int prestera_sdma_tx_wait(struct prestera_sdma *sdma, 69362306a36Sopenharmony_ci struct prestera_tx_ring *tx_ring) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci int tx_wait_num = PRESTERA_SDMA_WAIT_MUL * tx_ring->max_burst; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci do { 69862306a36Sopenharmony_ci if (prestera_sdma_is_ready(sdma)) 69962306a36Sopenharmony_ci return 0; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci udelay(1); 70262306a36Sopenharmony_ci } while (--tx_wait_num); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return -EBUSY; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic void prestera_sdma_tx_start(struct prestera_sdma *sdma) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG, 1); 71062306a36Sopenharmony_ci schedule_work(&sdma->tx_work); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic netdev_tx_t prestera_sdma_xmit(struct prestera_sdma *sdma, 71462306a36Sopenharmony_ci struct sk_buff *skb) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct device *dma_dev = sdma->sw->dev->dev; 71762306a36Sopenharmony_ci struct net_device *dev = skb->dev; 71862306a36Sopenharmony_ci struct prestera_tx_ring *tx_ring; 71962306a36Sopenharmony_ci struct prestera_sdma_buf *buf; 72062306a36Sopenharmony_ci int err; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci spin_lock(&sdma->tx_lock); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci tx_ring = &sdma->tx_ring; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci buf = &tx_ring->bufs[tx_ring->next_tx]; 72762306a36Sopenharmony_ci if (buf->is_used) { 72862306a36Sopenharmony_ci schedule_work(&sdma->tx_work); 72962306a36Sopenharmony_ci goto drop_skb; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (unlikely(eth_skb_pad(skb))) 73362306a36Sopenharmony_ci goto drop_skb_nofree; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci err = prestera_sdma_tx_buf_map(sdma, buf, skb); 73662306a36Sopenharmony_ci if (err) 73762306a36Sopenharmony_ci goto drop_skb; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci prestera_sdma_tx_desc_set_buf(sdma, buf->desc, buf->buf_dma, skb->len); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci dma_sync_single_for_device(dma_dev, buf->buf_dma, skb->len, 74262306a36Sopenharmony_ci DMA_TO_DEVICE); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (tx_ring->burst) { 74562306a36Sopenharmony_ci tx_ring->burst--; 74662306a36Sopenharmony_ci } else { 74762306a36Sopenharmony_ci tx_ring->burst = tx_ring->max_burst; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci err = prestera_sdma_tx_wait(sdma, tx_ring); 75062306a36Sopenharmony_ci if (err) 75162306a36Sopenharmony_ci goto drop_skb_unmap; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci tx_ring->next_tx = (tx_ring->next_tx + 1) % PRESTERA_SDMA_TX_DESC_PER_Q; 75562306a36Sopenharmony_ci prestera_sdma_tx_desc_xmit(buf->desc); 75662306a36Sopenharmony_ci buf->is_used = true; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci prestera_sdma_tx_start(sdma); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci goto tx_done; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cidrop_skb_unmap: 76362306a36Sopenharmony_ci prestera_sdma_tx_buf_unmap(sdma, buf); 76462306a36Sopenharmony_cidrop_skb: 76562306a36Sopenharmony_ci dev_consume_skb_any(skb); 76662306a36Sopenharmony_cidrop_skb_nofree: 76762306a36Sopenharmony_ci dev->stats.tx_dropped++; 76862306a36Sopenharmony_citx_done: 76962306a36Sopenharmony_ci spin_unlock(&sdma->tx_lock); 77062306a36Sopenharmony_ci return NETDEV_TX_OK; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ciint prestera_rxtx_switch_init(struct prestera_switch *sw) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct prestera_rxtx *rxtx; 77662306a36Sopenharmony_ci int err; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci rxtx = kzalloc(sizeof(*rxtx), GFP_KERNEL); 77962306a36Sopenharmony_ci if (!rxtx) 78062306a36Sopenharmony_ci return -ENOMEM; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci sw->rxtx = rxtx; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci err = prestera_sdma_switch_init(sw); 78562306a36Sopenharmony_ci if (err) 78662306a36Sopenharmony_ci kfree(rxtx); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return err; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_civoid prestera_rxtx_switch_fini(struct prestera_switch *sw) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci prestera_sdma_switch_fini(sw); 79462306a36Sopenharmony_ci kfree(sw->rxtx); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ciint prestera_rxtx_port_init(struct prestera_port *port) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci port->dev->needed_headroom = PRESTERA_DSA_HLEN; 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cinetdev_tx_t prestera_rxtx_xmit(struct prestera_port *port, struct sk_buff *skb) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct prestera_dsa dsa; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci dsa.hw_dev_num = port->dev_id; 80862306a36Sopenharmony_ci dsa.port_num = port->hw_id; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (skb_cow_head(skb, PRESTERA_DSA_HLEN) < 0) 81162306a36Sopenharmony_ci return NET_XMIT_DROP; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci skb_push(skb, PRESTERA_DSA_HLEN); 81462306a36Sopenharmony_ci memmove(skb->data, skb->data + PRESTERA_DSA_HLEN, 2 * ETH_ALEN); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (prestera_dsa_build(&dsa, skb->data + 2 * ETH_ALEN) != 0) 81762306a36Sopenharmony_ci return NET_XMIT_DROP; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return prestera_sdma_xmit(&port->sw->rxtx->sdma, skb); 82062306a36Sopenharmony_ci} 821