| /* |
| * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. |
| * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <nss_dp_dev.h> |
| #include "syn_dma_reg.h" |
| |
| /* |
| * syn_dp_cfg_tx_setup_desc_queue |
| * This sets up the transmit Descriptor queue in ring mode. |
| */ |
| static int syn_dp_cfg_tx_setup_desc_queue(struct syn_dp_info *dev_info) |
| { |
| struct syn_dp_info_tx *tx_info = &dev_info->dp_info_tx; |
| struct net_device *netdev = tx_info->netdev; |
| struct dma_desc_tx *first_desc = NULL; |
| dma_addr_t dma_addr; |
| |
| netdev_dbg(netdev, "Total size of memory required for Tx Descriptors in Ring Mode = %u\n", (uint32_t)((sizeof(struct dma_desc_tx) * SYN_DP_TX_DESC_SIZE))); |
| |
| first_desc = dma_alloc_coherent(tx_info->dev, sizeof(struct dma_desc_tx) * SYN_DP_TX_DESC_SIZE, &dma_addr, GFP_KERNEL); |
| if (!first_desc) { |
| netdev_dbg(netdev, "Error in Tx Descriptors memory allocation\n"); |
| return -ENOMEM; |
| } |
| |
| tx_info->tx_desc = first_desc; |
| dev_info->tx_desc_dma_addr = dma_addr; |
| netdev_dbg(netdev, "Tx Descriptors in Ring Mode: No. of descriptors = %d base = 0x%px dma = 0x%px\n" |
| , SYN_DP_TX_DESC_SIZE, first_desc, (void *)dma_addr); |
| |
| syn_dp_gmac_tx_desc_init_ring(tx_info->tx_desc, SYN_DP_TX_DESC_SIZE); |
| |
| tx_info->tx_comp_idx = 0; |
| tx_info->tx_idx = 0; |
| tx_info->busy_tx_desc_cnt = 0; |
| |
| tx_info->tx_free_batch_size = SYN_DP_NAPI_BUDGET_TX; |
| tx_info->csd.func = syn_dp_free_tx_done_skb; |
| tx_info->csd.info = &tx_info->tx_free_batch_size; |
| tx_info->csd.flags = 0; |
| |
| return NSS_DP_SUCCESS; |
| } |
| |
| /* |
| * syn_dp_cfg_tx_setup_rings |
| * Perform initial setup of Tx rings |
| */ |
| int syn_dp_cfg_tx_setup_rings(struct syn_dp_info *dev_info) |
| { |
| int err; |
| |
| err = syn_dp_cfg_tx_setup_desc_queue(dev_info); |
| if (err) { |
| netdev_dbg(dev_info->dp_info_tx.netdev, "nss_dp_gmac: tx descriptor setup unsuccessfull, err code: %d", err); |
| return NSS_DP_FAILURE; |
| } |
| |
| syn_init_tx_desc_base(dev_info->mac_base, dev_info->tx_desc_dma_addr); |
| |
| return NSS_DP_SUCCESS; |
| } |
| |
| /* |
| * syn_dp_cfg_tx_cleanup_rings |
| * Cleanup Synopsys GMAC Tx rings |
| */ |
| void syn_dp_cfg_tx_cleanup_rings(struct syn_dp_info *dev_info) |
| { |
| struct syn_dp_info_tx *tx_info = &dev_info->dp_info_tx; |
| uint32_t tx_skb_index; |
| struct dma_desc_tx *txdesc; |
| int i; |
| struct sk_buff *skb; |
| uint32_t busy_tx_desc_cnt = atomic_read((atomic_t *)&tx_info->busy_tx_desc_cnt); |
| int budget = SYN_DP_TX_DESC_SIZE; |
| |
| syn_dp_free_tx_done_skb(&budget); |
| /* |
| * Tx Ring cleaning |
| */ |
| tx_skb_index = syn_dp_tx_comp_index_get(tx_info); |
| for (i = 0; i < busy_tx_desc_cnt; i++) { |
| tx_skb_index = syn_dp_tx_inc_index(tx_skb_index, i); |
| txdesc = tx_info->tx_desc; |
| |
| skb = tx_info->tx_buf_pool[tx_skb_index].skb; |
| if (unlikely(skb != NULL)) { |
| dev_kfree_skb_any(skb); |
| tx_info->tx_buf_pool[tx_skb_index].skb = NULL; |
| } |
| } |
| |
| dma_free_coherent(tx_info->dev, (sizeof(struct dma_desc_tx) * SYN_DP_TX_DESC_SIZE), |
| tx_info->tx_desc, dev_info->tx_desc_dma_addr); |
| } |