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