162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright Sunplus Technology Co., Ltd. 362306a36Sopenharmony_ci * All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/platform_device.h> 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/of_mdio.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "spl2sw_define.h" 1162306a36Sopenharmony_ci#include "spl2sw_desc.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_civoid spl2sw_rx_descs_flush(struct spl2sw_common *comm) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct spl2sw_skb_info *rx_skbinfo; 1662306a36Sopenharmony_ci struct spl2sw_mac_desc *rx_desc; 1762306a36Sopenharmony_ci u32 i, j; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) { 2062306a36Sopenharmony_ci rx_desc = comm->rx_desc[i]; 2162306a36Sopenharmony_ci rx_skbinfo = comm->rx_skb_info[i]; 2262306a36Sopenharmony_ci for (j = 0; j < comm->rx_desc_num[i]; j++) { 2362306a36Sopenharmony_ci rx_desc[j].addr1 = rx_skbinfo[j].mapping; 2462306a36Sopenharmony_ci rx_desc[j].cmd2 = (j == comm->rx_desc_num[i] - 1) ? 2562306a36Sopenharmony_ci RXD_EOR | comm->rx_desc_buff_size : 2662306a36Sopenharmony_ci comm->rx_desc_buff_size; 2762306a36Sopenharmony_ci wmb(); /* Set RXD_OWN after other fields are ready. */ 2862306a36Sopenharmony_ci rx_desc[j].cmd1 = RXD_OWN; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid spl2sw_tx_descs_clean(struct spl2sw_common *comm) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci u32 i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (!comm->tx_desc) 3862306a36Sopenharmony_ci return; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < TX_DESC_NUM; i++) { 4162306a36Sopenharmony_ci comm->tx_desc[i].cmd1 = 0; 4262306a36Sopenharmony_ci wmb(); /* Clear TXD_OWN and then set other fields. */ 4362306a36Sopenharmony_ci comm->tx_desc[i].cmd2 = 0; 4462306a36Sopenharmony_ci comm->tx_desc[i].addr1 = 0; 4562306a36Sopenharmony_ci comm->tx_desc[i].addr2 = 0; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (comm->tx_temp_skb_info[i].mapping) { 4862306a36Sopenharmony_ci dma_unmap_single(&comm->pdev->dev, comm->tx_temp_skb_info[i].mapping, 4962306a36Sopenharmony_ci comm->tx_temp_skb_info[i].skb->len, DMA_TO_DEVICE); 5062306a36Sopenharmony_ci comm->tx_temp_skb_info[i].mapping = 0; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (comm->tx_temp_skb_info[i].skb) { 5462306a36Sopenharmony_ci dev_kfree_skb_any(comm->tx_temp_skb_info[i].skb); 5562306a36Sopenharmony_ci comm->tx_temp_skb_info[i].skb = NULL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_civoid spl2sw_rx_descs_clean(struct spl2sw_common *comm) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct spl2sw_skb_info *rx_skbinfo; 6362306a36Sopenharmony_ci struct spl2sw_mac_desc *rx_desc; 6462306a36Sopenharmony_ci u32 i, j; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) { 6762306a36Sopenharmony_ci if (!comm->rx_skb_info[i]) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci rx_desc = comm->rx_desc[i]; 7162306a36Sopenharmony_ci rx_skbinfo = comm->rx_skb_info[i]; 7262306a36Sopenharmony_ci for (j = 0; j < comm->rx_desc_num[i]; j++) { 7362306a36Sopenharmony_ci rx_desc[j].cmd1 = 0; 7462306a36Sopenharmony_ci wmb(); /* Clear RXD_OWN and then set other fields. */ 7562306a36Sopenharmony_ci rx_desc[j].cmd2 = 0; 7662306a36Sopenharmony_ci rx_desc[j].addr1 = 0; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (rx_skbinfo[j].skb) { 7962306a36Sopenharmony_ci dma_unmap_single(&comm->pdev->dev, rx_skbinfo[j].mapping, 8062306a36Sopenharmony_ci comm->rx_desc_buff_size, DMA_FROM_DEVICE); 8162306a36Sopenharmony_ci dev_kfree_skb_any(rx_skbinfo[j].skb); 8262306a36Sopenharmony_ci rx_skbinfo[j].skb = NULL; 8362306a36Sopenharmony_ci rx_skbinfo[j].mapping = 0; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci kfree(rx_skbinfo); 8862306a36Sopenharmony_ci comm->rx_skb_info[i] = NULL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid spl2sw_descs_clean(struct spl2sw_common *comm) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci spl2sw_rx_descs_clean(comm); 9562306a36Sopenharmony_ci spl2sw_tx_descs_clean(comm); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_civoid spl2sw_descs_free(struct spl2sw_common *comm) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u32 i; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci spl2sw_descs_clean(comm); 10362306a36Sopenharmony_ci comm->tx_desc = NULL; 10462306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) 10562306a36Sopenharmony_ci comm->rx_desc[i] = NULL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Free descriptor area */ 10862306a36Sopenharmony_ci if (comm->desc_base) { 10962306a36Sopenharmony_ci dma_free_coherent(&comm->pdev->dev, comm->desc_size, comm->desc_base, 11062306a36Sopenharmony_ci comm->desc_dma); 11162306a36Sopenharmony_ci comm->desc_base = NULL; 11262306a36Sopenharmony_ci comm->desc_dma = 0; 11362306a36Sopenharmony_ci comm->desc_size = 0; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_civoid spl2sw_tx_descs_init(struct spl2sw_common *comm) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci memset(comm->tx_desc, '\0', sizeof(struct spl2sw_mac_desc) * 12062306a36Sopenharmony_ci (TX_DESC_NUM + MAC_GUARD_DESC_NUM)); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciint spl2sw_rx_descs_init(struct spl2sw_common *comm) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct spl2sw_skb_info *rx_skbinfo; 12662306a36Sopenharmony_ci struct spl2sw_mac_desc *rx_desc; 12762306a36Sopenharmony_ci struct sk_buff *skb; 12862306a36Sopenharmony_ci u32 mapping; 12962306a36Sopenharmony_ci u32 i, j; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) { 13262306a36Sopenharmony_ci comm->rx_skb_info[i] = kcalloc(comm->rx_desc_num[i], sizeof(*rx_skbinfo), 13362306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 13462306a36Sopenharmony_ci if (!comm->rx_skb_info[i]) 13562306a36Sopenharmony_ci goto mem_alloc_fail; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rx_skbinfo = comm->rx_skb_info[i]; 13862306a36Sopenharmony_ci rx_desc = comm->rx_desc[i]; 13962306a36Sopenharmony_ci for (j = 0; j < comm->rx_desc_num[i]; j++) { 14062306a36Sopenharmony_ci skb = netdev_alloc_skb(NULL, comm->rx_desc_buff_size); 14162306a36Sopenharmony_ci if (!skb) 14262306a36Sopenharmony_ci goto mem_alloc_fail; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci rx_skbinfo[j].skb = skb; 14562306a36Sopenharmony_ci mapping = dma_map_single(&comm->pdev->dev, skb->data, 14662306a36Sopenharmony_ci comm->rx_desc_buff_size, 14762306a36Sopenharmony_ci DMA_FROM_DEVICE); 14862306a36Sopenharmony_ci if (dma_mapping_error(&comm->pdev->dev, mapping)) 14962306a36Sopenharmony_ci goto mem_alloc_fail; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci rx_skbinfo[j].mapping = mapping; 15262306a36Sopenharmony_ci rx_desc[j].addr1 = mapping; 15362306a36Sopenharmony_ci rx_desc[j].addr2 = 0; 15462306a36Sopenharmony_ci rx_desc[j].cmd2 = (j == comm->rx_desc_num[i] - 1) ? 15562306a36Sopenharmony_ci RXD_EOR | comm->rx_desc_buff_size : 15662306a36Sopenharmony_ci comm->rx_desc_buff_size; 15762306a36Sopenharmony_ci wmb(); /* Set RXD_OWN after other fields are effective. */ 15862306a36Sopenharmony_ci rx_desc[j].cmd1 = RXD_OWN; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cimem_alloc_fail: 16562306a36Sopenharmony_ci spl2sw_rx_descs_clean(comm); 16662306a36Sopenharmony_ci return -ENOMEM; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciint spl2sw_descs_alloc(struct spl2sw_common *comm) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci s32 desc_size; 17262306a36Sopenharmony_ci u32 i; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Alloc descriptor area */ 17562306a36Sopenharmony_ci desc_size = (TX_DESC_NUM + MAC_GUARD_DESC_NUM) * sizeof(struct spl2sw_mac_desc); 17662306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) 17762306a36Sopenharmony_ci desc_size += comm->rx_desc_num[i] * sizeof(struct spl2sw_mac_desc); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci comm->desc_base = dma_alloc_coherent(&comm->pdev->dev, desc_size, &comm->desc_dma, 18062306a36Sopenharmony_ci GFP_KERNEL); 18162306a36Sopenharmony_ci if (!comm->desc_base) 18262306a36Sopenharmony_ci return -ENOMEM; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci comm->desc_size = desc_size; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* Setup Tx descriptor */ 18762306a36Sopenharmony_ci comm->tx_desc = comm->desc_base; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Setup Rx descriptor */ 19062306a36Sopenharmony_ci comm->rx_desc[0] = &comm->tx_desc[TX_DESC_NUM + MAC_GUARD_DESC_NUM]; 19162306a36Sopenharmony_ci for (i = 1; i < RX_DESC_QUEUE_NUM; i++) 19262306a36Sopenharmony_ci comm->rx_desc[i] = comm->rx_desc[i - 1] + comm->rx_desc_num[i - 1]; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciint spl2sw_descs_init(struct spl2sw_common *comm) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u32 i, ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Initialize rx descriptor's data */ 20262306a36Sopenharmony_ci comm->rx_desc_num[0] = RX_QUEUE0_DESC_NUM; 20362306a36Sopenharmony_ci comm->rx_desc_num[1] = RX_QUEUE1_DESC_NUM; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) { 20662306a36Sopenharmony_ci comm->rx_desc[i] = NULL; 20762306a36Sopenharmony_ci comm->rx_skb_info[i] = NULL; 20862306a36Sopenharmony_ci comm->rx_pos[i] = 0; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci comm->rx_desc_buff_size = MAC_RX_LEN_MAX; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Initialize tx descriptor's data */ 21362306a36Sopenharmony_ci comm->tx_done_pos = 0; 21462306a36Sopenharmony_ci comm->tx_desc = NULL; 21562306a36Sopenharmony_ci comm->tx_pos = 0; 21662306a36Sopenharmony_ci comm->tx_desc_full = 0; 21762306a36Sopenharmony_ci for (i = 0; i < TX_DESC_NUM; i++) 21862306a36Sopenharmony_ci comm->tx_temp_skb_info[i].skb = NULL; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Allocate tx & rx descriptors. */ 22162306a36Sopenharmony_ci ret = spl2sw_descs_alloc(comm); 22262306a36Sopenharmony_ci if (ret) 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci spl2sw_tx_descs_init(comm); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return spl2sw_rx_descs_init(comm); 22862306a36Sopenharmony_ci} 229