162306a36Sopenharmony_ci/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This program is dual-licensed; you may select either version 2 of
662306a36Sopenharmony_ci * the GNU General Public License ("GPL") or BSD license ("BSD").
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This Synopsys DWC XLGMAC software driver and associated documentation
962306a36Sopenharmony_ci * (hereinafter the "Software") is an unsupported proprietary work of
1062306a36Sopenharmony_ci * Synopsys, Inc. unless otherwise expressly agreed to in writing between
1162306a36Sopenharmony_ci * Synopsys and you. The Software IS NOT an item of Licensed Software or a
1262306a36Sopenharmony_ci * Licensed Product under any End User Software License Agreement or
1362306a36Sopenharmony_ci * Agreement for Licensed Products with Synopsys or any supplement thereto.
1462306a36Sopenharmony_ci * Synopsys is a registered trademark of Synopsys, Inc. Other names included
1562306a36Sopenharmony_ci * in the SOFTWARE may be the trademarks of their respective owners.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "dwc-xlgmac.h"
1962306a36Sopenharmony_ci#include "dwc-xlgmac-reg.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void xlgmac_unmap_desc_data(struct xlgmac_pdata *pdata,
2262306a36Sopenharmony_ci				   struct xlgmac_desc_data *desc_data)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	if (desc_data->skb_dma) {
2562306a36Sopenharmony_ci		if (desc_data->mapped_as_page) {
2662306a36Sopenharmony_ci			dma_unmap_page(pdata->dev, desc_data->skb_dma,
2762306a36Sopenharmony_ci				       desc_data->skb_dma_len, DMA_TO_DEVICE);
2862306a36Sopenharmony_ci		} else {
2962306a36Sopenharmony_ci			dma_unmap_single(pdata->dev, desc_data->skb_dma,
3062306a36Sopenharmony_ci					 desc_data->skb_dma_len, DMA_TO_DEVICE);
3162306a36Sopenharmony_ci		}
3262306a36Sopenharmony_ci		desc_data->skb_dma = 0;
3362306a36Sopenharmony_ci		desc_data->skb_dma_len = 0;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (desc_data->skb) {
3762306a36Sopenharmony_ci		dev_kfree_skb_any(desc_data->skb);
3862306a36Sopenharmony_ci		desc_data->skb = NULL;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (desc_data->rx.hdr.pa.pages)
4262306a36Sopenharmony_ci		put_page(desc_data->rx.hdr.pa.pages);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (desc_data->rx.hdr.pa_unmap.pages) {
4562306a36Sopenharmony_ci		dma_unmap_page(pdata->dev, desc_data->rx.hdr.pa_unmap.pages_dma,
4662306a36Sopenharmony_ci			       desc_data->rx.hdr.pa_unmap.pages_len,
4762306a36Sopenharmony_ci			       DMA_FROM_DEVICE);
4862306a36Sopenharmony_ci		put_page(desc_data->rx.hdr.pa_unmap.pages);
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (desc_data->rx.buf.pa.pages)
5262306a36Sopenharmony_ci		put_page(desc_data->rx.buf.pa.pages);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (desc_data->rx.buf.pa_unmap.pages) {
5562306a36Sopenharmony_ci		dma_unmap_page(pdata->dev, desc_data->rx.buf.pa_unmap.pages_dma,
5662306a36Sopenharmony_ci			       desc_data->rx.buf.pa_unmap.pages_len,
5762306a36Sopenharmony_ci			       DMA_FROM_DEVICE);
5862306a36Sopenharmony_ci		put_page(desc_data->rx.buf.pa_unmap.pages);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	memset(&desc_data->tx, 0, sizeof(desc_data->tx));
6262306a36Sopenharmony_ci	memset(&desc_data->rx, 0, sizeof(desc_data->rx));
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	desc_data->mapped_as_page = 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (desc_data->state_saved) {
6762306a36Sopenharmony_ci		desc_data->state_saved = 0;
6862306a36Sopenharmony_ci		desc_data->state.skb = NULL;
6962306a36Sopenharmony_ci		desc_data->state.len = 0;
7062306a36Sopenharmony_ci		desc_data->state.error = 0;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void xlgmac_free_ring(struct xlgmac_pdata *pdata,
7562306a36Sopenharmony_ci			     struct xlgmac_ring *ring)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct xlgmac_desc_data *desc_data;
7862306a36Sopenharmony_ci	unsigned int i;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!ring)
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (ring->desc_data_head) {
8462306a36Sopenharmony_ci		for (i = 0; i < ring->dma_desc_count; i++) {
8562306a36Sopenharmony_ci			desc_data = XLGMAC_GET_DESC_DATA(ring, i);
8662306a36Sopenharmony_ci			xlgmac_unmap_desc_data(pdata, desc_data);
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		kfree(ring->desc_data_head);
9062306a36Sopenharmony_ci		ring->desc_data_head = NULL;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (ring->rx_hdr_pa.pages) {
9462306a36Sopenharmony_ci		dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma,
9562306a36Sopenharmony_ci			       ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE);
9662306a36Sopenharmony_ci		put_page(ring->rx_hdr_pa.pages);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		ring->rx_hdr_pa.pages = NULL;
9962306a36Sopenharmony_ci		ring->rx_hdr_pa.pages_len = 0;
10062306a36Sopenharmony_ci		ring->rx_hdr_pa.pages_offset = 0;
10162306a36Sopenharmony_ci		ring->rx_hdr_pa.pages_dma = 0;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (ring->rx_buf_pa.pages) {
10562306a36Sopenharmony_ci		dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma,
10662306a36Sopenharmony_ci			       ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE);
10762306a36Sopenharmony_ci		put_page(ring->rx_buf_pa.pages);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		ring->rx_buf_pa.pages = NULL;
11062306a36Sopenharmony_ci		ring->rx_buf_pa.pages_len = 0;
11162306a36Sopenharmony_ci		ring->rx_buf_pa.pages_offset = 0;
11262306a36Sopenharmony_ci		ring->rx_buf_pa.pages_dma = 0;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (ring->dma_desc_head) {
11662306a36Sopenharmony_ci		dma_free_coherent(pdata->dev,
11762306a36Sopenharmony_ci				  (sizeof(struct xlgmac_dma_desc) *
11862306a36Sopenharmony_ci				  ring->dma_desc_count),
11962306a36Sopenharmony_ci				  ring->dma_desc_head,
12062306a36Sopenharmony_ci				  ring->dma_desc_head_addr);
12162306a36Sopenharmony_ci		ring->dma_desc_head = NULL;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int xlgmac_init_ring(struct xlgmac_pdata *pdata,
12662306a36Sopenharmony_ci			    struct xlgmac_ring *ring,
12762306a36Sopenharmony_ci			    unsigned int dma_desc_count)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (!ring)
13062306a36Sopenharmony_ci		return 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Descriptors */
13362306a36Sopenharmony_ci	ring->dma_desc_count = dma_desc_count;
13462306a36Sopenharmony_ci	ring->dma_desc_head = dma_alloc_coherent(pdata->dev,
13562306a36Sopenharmony_ci					(sizeof(struct xlgmac_dma_desc) *
13662306a36Sopenharmony_ci					 dma_desc_count),
13762306a36Sopenharmony_ci					&ring->dma_desc_head_addr,
13862306a36Sopenharmony_ci					GFP_KERNEL);
13962306a36Sopenharmony_ci	if (!ring->dma_desc_head)
14062306a36Sopenharmony_ci		return -ENOMEM;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Array of descriptor data */
14362306a36Sopenharmony_ci	ring->desc_data_head = kcalloc(dma_desc_count,
14462306a36Sopenharmony_ci					sizeof(struct xlgmac_desc_data),
14562306a36Sopenharmony_ci					GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!ring->desc_data_head)
14762306a36Sopenharmony_ci		return -ENOMEM;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	netif_dbg(pdata, drv, pdata->netdev,
15062306a36Sopenharmony_ci		  "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n",
15162306a36Sopenharmony_ci		ring->dma_desc_head,
15262306a36Sopenharmony_ci		&ring->dma_desc_head_addr,
15362306a36Sopenharmony_ci		ring->desc_data_head);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void xlgmac_free_rings(struct xlgmac_pdata *pdata)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct xlgmac_channel *channel;
16162306a36Sopenharmony_ci	unsigned int i;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (!pdata->channel_head)
16462306a36Sopenharmony_ci		return;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	channel = pdata->channel_head;
16762306a36Sopenharmony_ci	for (i = 0; i < pdata->channel_count; i++, channel++) {
16862306a36Sopenharmony_ci		xlgmac_free_ring(pdata, channel->tx_ring);
16962306a36Sopenharmony_ci		xlgmac_free_ring(pdata, channel->rx_ring);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int xlgmac_alloc_rings(struct xlgmac_pdata *pdata)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct xlgmac_channel *channel;
17662306a36Sopenharmony_ci	unsigned int i;
17762306a36Sopenharmony_ci	int ret;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	channel = pdata->channel_head;
18062306a36Sopenharmony_ci	for (i = 0; i < pdata->channel_count; i++, channel++) {
18162306a36Sopenharmony_ci		netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n",
18262306a36Sopenharmony_ci			  channel->name);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		ret = xlgmac_init_ring(pdata, channel->tx_ring,
18562306a36Sopenharmony_ci				       pdata->tx_desc_count);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		if (ret) {
18862306a36Sopenharmony_ci			netdev_alert(pdata->netdev,
18962306a36Sopenharmony_ci				     "error initializing Tx ring");
19062306a36Sopenharmony_ci			goto err_init_ring;
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n",
19462306a36Sopenharmony_ci			  channel->name);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		ret = xlgmac_init_ring(pdata, channel->rx_ring,
19762306a36Sopenharmony_ci				       pdata->rx_desc_count);
19862306a36Sopenharmony_ci		if (ret) {
19962306a36Sopenharmony_ci			netdev_alert(pdata->netdev,
20062306a36Sopenharmony_ci				     "error initializing Rx ring\n");
20162306a36Sopenharmony_ci			goto err_init_ring;
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cierr_init_ring:
20862306a36Sopenharmony_ci	xlgmac_free_rings(pdata);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return ret;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void xlgmac_free_channels(struct xlgmac_pdata *pdata)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (!pdata->channel_head)
21662306a36Sopenharmony_ci		return;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	kfree(pdata->channel_head->tx_ring);
21962306a36Sopenharmony_ci	pdata->channel_head->tx_ring = NULL;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	kfree(pdata->channel_head->rx_ring);
22262306a36Sopenharmony_ci	pdata->channel_head->rx_ring = NULL;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	kfree(pdata->channel_head);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	pdata->channel_head = NULL;
22762306a36Sopenharmony_ci	pdata->channel_count = 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int xlgmac_alloc_channels(struct xlgmac_pdata *pdata)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct xlgmac_channel *channel_head, *channel;
23362306a36Sopenharmony_ci	struct xlgmac_ring *tx_ring, *rx_ring;
23462306a36Sopenharmony_ci	int ret = -ENOMEM;
23562306a36Sopenharmony_ci	unsigned int i;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	channel_head = kcalloc(pdata->channel_count,
23862306a36Sopenharmony_ci			       sizeof(struct xlgmac_channel), GFP_KERNEL);
23962306a36Sopenharmony_ci	if (!channel_head)
24062306a36Sopenharmony_ci		return ret;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	netif_dbg(pdata, drv, pdata->netdev,
24362306a36Sopenharmony_ci		  "channel_head=%p\n", channel_head);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	tx_ring = kcalloc(pdata->tx_ring_count, sizeof(struct xlgmac_ring),
24662306a36Sopenharmony_ci			  GFP_KERNEL);
24762306a36Sopenharmony_ci	if (!tx_ring)
24862306a36Sopenharmony_ci		goto err_tx_ring;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct xlgmac_ring),
25162306a36Sopenharmony_ci			  GFP_KERNEL);
25262306a36Sopenharmony_ci	if (!rx_ring)
25362306a36Sopenharmony_ci		goto err_rx_ring;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	for (i = 0, channel = channel_head; i < pdata->channel_count;
25662306a36Sopenharmony_ci		i++, channel++) {
25762306a36Sopenharmony_ci		snprintf(channel->name, sizeof(channel->name), "channel-%u", i);
25862306a36Sopenharmony_ci		channel->pdata = pdata;
25962306a36Sopenharmony_ci		channel->queue_index = i;
26062306a36Sopenharmony_ci		channel->dma_regs = pdata->mac_regs + DMA_CH_BASE +
26162306a36Sopenharmony_ci				    (DMA_CH_INC * i);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (pdata->per_channel_irq) {
26462306a36Sopenharmony_ci			/* Get the per DMA interrupt */
26562306a36Sopenharmony_ci			ret = pdata->channel_irq[i];
26662306a36Sopenharmony_ci			if (ret < 0) {
26762306a36Sopenharmony_ci				netdev_err(pdata->netdev,
26862306a36Sopenharmony_ci					   "get_irq %u failed\n",
26962306a36Sopenharmony_ci					   i + 1);
27062306a36Sopenharmony_ci				goto err_irq;
27162306a36Sopenharmony_ci			}
27262306a36Sopenharmony_ci			channel->dma_irq = ret;
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		if (i < pdata->tx_ring_count)
27662306a36Sopenharmony_ci			channel->tx_ring = tx_ring++;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		if (i < pdata->rx_ring_count)
27962306a36Sopenharmony_ci			channel->rx_ring = rx_ring++;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		netif_dbg(pdata, drv, pdata->netdev,
28262306a36Sopenharmony_ci			  "%s: dma_regs=%p, tx_ring=%p, rx_ring=%p\n",
28362306a36Sopenharmony_ci			  channel->name, channel->dma_regs,
28462306a36Sopenharmony_ci			  channel->tx_ring, channel->rx_ring);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	pdata->channel_head = channel_head;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cierr_irq:
29262306a36Sopenharmony_ci	kfree(rx_ring);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cierr_rx_ring:
29562306a36Sopenharmony_ci	kfree(tx_ring);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cierr_tx_ring:
29862306a36Sopenharmony_ci	kfree(channel_head);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return ret;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void xlgmac_free_channels_and_rings(struct xlgmac_pdata *pdata)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	xlgmac_free_rings(pdata);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	xlgmac_free_channels(pdata);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int xlgmac_alloc_channels_and_rings(struct xlgmac_pdata *pdata)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	int ret;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = xlgmac_alloc_channels(pdata);
31562306a36Sopenharmony_ci	if (ret)
31662306a36Sopenharmony_ci		goto err_alloc;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	ret = xlgmac_alloc_rings(pdata);
31962306a36Sopenharmony_ci	if (ret)
32062306a36Sopenharmony_ci		goto err_alloc;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cierr_alloc:
32562306a36Sopenharmony_ci	xlgmac_free_channels_and_rings(pdata);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return ret;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int xlgmac_alloc_pages(struct xlgmac_pdata *pdata,
33162306a36Sopenharmony_ci			      struct xlgmac_page_alloc *pa,
33262306a36Sopenharmony_ci			      gfp_t gfp, int order)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct page *pages = NULL;
33562306a36Sopenharmony_ci	dma_addr_t pages_dma;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Try to obtain pages, decreasing order if necessary */
33862306a36Sopenharmony_ci	gfp |= __GFP_COMP | __GFP_NOWARN;
33962306a36Sopenharmony_ci	while (order >= 0) {
34062306a36Sopenharmony_ci		pages = alloc_pages(gfp, order);
34162306a36Sopenharmony_ci		if (pages)
34262306a36Sopenharmony_ci			break;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		order--;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	if (!pages)
34762306a36Sopenharmony_ci		return -ENOMEM;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Map the pages */
35062306a36Sopenharmony_ci	pages_dma = dma_map_page(pdata->dev, pages, 0,
35162306a36Sopenharmony_ci				 PAGE_SIZE << order, DMA_FROM_DEVICE);
35262306a36Sopenharmony_ci	if (dma_mapping_error(pdata->dev, pages_dma)) {
35362306a36Sopenharmony_ci		put_page(pages);
35462306a36Sopenharmony_ci		return -ENOMEM;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	pa->pages = pages;
35862306a36Sopenharmony_ci	pa->pages_len = PAGE_SIZE << order;
35962306a36Sopenharmony_ci	pa->pages_offset = 0;
36062306a36Sopenharmony_ci	pa->pages_dma = pages_dma;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return 0;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void xlgmac_set_buffer_data(struct xlgmac_buffer_data *bd,
36662306a36Sopenharmony_ci				   struct xlgmac_page_alloc *pa,
36762306a36Sopenharmony_ci				   unsigned int len)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	get_page(pa->pages);
37062306a36Sopenharmony_ci	bd->pa = *pa;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	bd->dma_base = pa->pages_dma;
37362306a36Sopenharmony_ci	bd->dma_off = pa->pages_offset;
37462306a36Sopenharmony_ci	bd->dma_len = len;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	pa->pages_offset += len;
37762306a36Sopenharmony_ci	if ((pa->pages_offset + len) > pa->pages_len) {
37862306a36Sopenharmony_ci		/* This data descriptor is responsible for unmapping page(s) */
37962306a36Sopenharmony_ci		bd->pa_unmap = *pa;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		/* Get a new allocation next time */
38262306a36Sopenharmony_ci		pa->pages = NULL;
38362306a36Sopenharmony_ci		pa->pages_len = 0;
38462306a36Sopenharmony_ci		pa->pages_offset = 0;
38562306a36Sopenharmony_ci		pa->pages_dma = 0;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int xlgmac_map_rx_buffer(struct xlgmac_pdata *pdata,
39062306a36Sopenharmony_ci				struct xlgmac_ring *ring,
39162306a36Sopenharmony_ci				struct xlgmac_desc_data *desc_data)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	int order, ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (!ring->rx_hdr_pa.pages) {
39662306a36Sopenharmony_ci		ret = xlgmac_alloc_pages(pdata, &ring->rx_hdr_pa,
39762306a36Sopenharmony_ci					 GFP_ATOMIC, 0);
39862306a36Sopenharmony_ci		if (ret)
39962306a36Sopenharmony_ci			return ret;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (!ring->rx_buf_pa.pages) {
40362306a36Sopenharmony_ci		order = max_t(int, PAGE_ALLOC_COSTLY_ORDER - 1, 0);
40462306a36Sopenharmony_ci		ret = xlgmac_alloc_pages(pdata, &ring->rx_buf_pa,
40562306a36Sopenharmony_ci					 GFP_ATOMIC, order);
40662306a36Sopenharmony_ci		if (ret)
40762306a36Sopenharmony_ci			return ret;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Set up the header page info */
41162306a36Sopenharmony_ci	xlgmac_set_buffer_data(&desc_data->rx.hdr, &ring->rx_hdr_pa,
41262306a36Sopenharmony_ci			       XLGMAC_SKB_ALLOC_SIZE);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* Set up the buffer page info */
41562306a36Sopenharmony_ci	xlgmac_set_buffer_data(&desc_data->rx.buf, &ring->rx_buf_pa,
41662306a36Sopenharmony_ci			       pdata->rx_buf_size);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void xlgmac_tx_desc_init(struct xlgmac_pdata *pdata)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
42462306a36Sopenharmony_ci	struct xlgmac_desc_data *desc_data;
42562306a36Sopenharmony_ci	struct xlgmac_dma_desc *dma_desc;
42662306a36Sopenharmony_ci	struct xlgmac_channel *channel;
42762306a36Sopenharmony_ci	struct xlgmac_ring *ring;
42862306a36Sopenharmony_ci	dma_addr_t dma_desc_addr;
42962306a36Sopenharmony_ci	unsigned int i, j;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	channel = pdata->channel_head;
43262306a36Sopenharmony_ci	for (i = 0; i < pdata->channel_count; i++, channel++) {
43362306a36Sopenharmony_ci		ring = channel->tx_ring;
43462306a36Sopenharmony_ci		if (!ring)
43562306a36Sopenharmony_ci			break;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		dma_desc = ring->dma_desc_head;
43862306a36Sopenharmony_ci		dma_desc_addr = ring->dma_desc_head_addr;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		for (j = 0; j < ring->dma_desc_count; j++) {
44162306a36Sopenharmony_ci			desc_data = XLGMAC_GET_DESC_DATA(ring, j);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci			desc_data->dma_desc = dma_desc;
44462306a36Sopenharmony_ci			desc_data->dma_desc_addr = dma_desc_addr;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci			dma_desc++;
44762306a36Sopenharmony_ci			dma_desc_addr += sizeof(struct xlgmac_dma_desc);
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		ring->cur = 0;
45162306a36Sopenharmony_ci		ring->dirty = 0;
45262306a36Sopenharmony_ci		memset(&ring->tx, 0, sizeof(ring->tx));
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		hw_ops->tx_desc_init(channel);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void xlgmac_rx_desc_init(struct xlgmac_pdata *pdata)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
46162306a36Sopenharmony_ci	struct xlgmac_desc_data *desc_data;
46262306a36Sopenharmony_ci	struct xlgmac_dma_desc *dma_desc;
46362306a36Sopenharmony_ci	struct xlgmac_channel *channel;
46462306a36Sopenharmony_ci	struct xlgmac_ring *ring;
46562306a36Sopenharmony_ci	dma_addr_t dma_desc_addr;
46662306a36Sopenharmony_ci	unsigned int i, j;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	channel = pdata->channel_head;
46962306a36Sopenharmony_ci	for (i = 0; i < pdata->channel_count; i++, channel++) {
47062306a36Sopenharmony_ci		ring = channel->rx_ring;
47162306a36Sopenharmony_ci		if (!ring)
47262306a36Sopenharmony_ci			break;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		dma_desc = ring->dma_desc_head;
47562306a36Sopenharmony_ci		dma_desc_addr = ring->dma_desc_head_addr;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		for (j = 0; j < ring->dma_desc_count; j++) {
47862306a36Sopenharmony_ci			desc_data = XLGMAC_GET_DESC_DATA(ring, j);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci			desc_data->dma_desc = dma_desc;
48162306a36Sopenharmony_ci			desc_data->dma_desc_addr = dma_desc_addr;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci			if (xlgmac_map_rx_buffer(pdata, ring, desc_data))
48462306a36Sopenharmony_ci				break;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci			dma_desc++;
48762306a36Sopenharmony_ci			dma_desc_addr += sizeof(struct xlgmac_dma_desc);
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		ring->cur = 0;
49162306a36Sopenharmony_ci		ring->dirty = 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		hw_ops->rx_desc_init(channel);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int xlgmac_map_tx_skb(struct xlgmac_channel *channel,
49862306a36Sopenharmony_ci			     struct sk_buff *skb)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct xlgmac_pdata *pdata = channel->pdata;
50162306a36Sopenharmony_ci	struct xlgmac_ring *ring = channel->tx_ring;
50262306a36Sopenharmony_ci	unsigned int start_index, cur_index;
50362306a36Sopenharmony_ci	struct xlgmac_desc_data *desc_data;
50462306a36Sopenharmony_ci	unsigned int offset, datalen, len;
50562306a36Sopenharmony_ci	struct xlgmac_pkt_info *pkt_info;
50662306a36Sopenharmony_ci	skb_frag_t *frag;
50762306a36Sopenharmony_ci	unsigned int tso, vlan;
50862306a36Sopenharmony_ci	dma_addr_t skb_dma;
50962306a36Sopenharmony_ci	unsigned int i;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	offset = 0;
51262306a36Sopenharmony_ci	start_index = ring->cur;
51362306a36Sopenharmony_ci	cur_index = ring->cur;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	pkt_info = &ring->pkt_info;
51662306a36Sopenharmony_ci	pkt_info->desc_count = 0;
51762306a36Sopenharmony_ci	pkt_info->length = 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	tso = XLGMAC_GET_REG_BITS(pkt_info->attributes,
52062306a36Sopenharmony_ci				  TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
52162306a36Sopenharmony_ci				  TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN);
52262306a36Sopenharmony_ci	vlan = XLGMAC_GET_REG_BITS(pkt_info->attributes,
52362306a36Sopenharmony_ci				   TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
52462306a36Sopenharmony_ci				   TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* Save space for a context descriptor if needed */
52762306a36Sopenharmony_ci	if ((tso && (pkt_info->mss != ring->tx.cur_mss)) ||
52862306a36Sopenharmony_ci	    (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag)))
52962306a36Sopenharmony_ci		cur_index++;
53062306a36Sopenharmony_ci	desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (tso) {
53362306a36Sopenharmony_ci		/* Map the TSO header */
53462306a36Sopenharmony_ci		skb_dma = dma_map_single(pdata->dev, skb->data,
53562306a36Sopenharmony_ci					 pkt_info->header_len, DMA_TO_DEVICE);
53662306a36Sopenharmony_ci		if (dma_mapping_error(pdata->dev, skb_dma)) {
53762306a36Sopenharmony_ci			netdev_alert(pdata->netdev, "dma_map_single failed\n");
53862306a36Sopenharmony_ci			goto err_out;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci		desc_data->skb_dma = skb_dma;
54162306a36Sopenharmony_ci		desc_data->skb_dma_len = pkt_info->header_len;
54262306a36Sopenharmony_ci		netif_dbg(pdata, tx_queued, pdata->netdev,
54362306a36Sopenharmony_ci			  "skb header: index=%u, dma=%pad, len=%u\n",
54462306a36Sopenharmony_ci			  cur_index, &skb_dma, pkt_info->header_len);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		offset = pkt_info->header_len;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		pkt_info->length += pkt_info->header_len;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		cur_index++;
55162306a36Sopenharmony_ci		desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Map the (remainder of the) packet */
55562306a36Sopenharmony_ci	for (datalen = skb_headlen(skb) - offset; datalen; ) {
55662306a36Sopenharmony_ci		len = min_t(unsigned int, datalen, XLGMAC_TX_MAX_BUF_SIZE);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		skb_dma = dma_map_single(pdata->dev, skb->data + offset, len,
55962306a36Sopenharmony_ci					 DMA_TO_DEVICE);
56062306a36Sopenharmony_ci		if (dma_mapping_error(pdata->dev, skb_dma)) {
56162306a36Sopenharmony_ci			netdev_alert(pdata->netdev, "dma_map_single failed\n");
56262306a36Sopenharmony_ci			goto err_out;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci		desc_data->skb_dma = skb_dma;
56562306a36Sopenharmony_ci		desc_data->skb_dma_len = len;
56662306a36Sopenharmony_ci		netif_dbg(pdata, tx_queued, pdata->netdev,
56762306a36Sopenharmony_ci			  "skb data: index=%u, dma=%pad, len=%u\n",
56862306a36Sopenharmony_ci			  cur_index, &skb_dma, len);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		datalen -= len;
57162306a36Sopenharmony_ci		offset += len;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		pkt_info->length += len;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		cur_index++;
57662306a36Sopenharmony_ci		desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
58062306a36Sopenharmony_ci		netif_dbg(pdata, tx_queued, pdata->netdev,
58162306a36Sopenharmony_ci			  "mapping frag %u\n", i);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		frag = &skb_shinfo(skb)->frags[i];
58462306a36Sopenharmony_ci		offset = 0;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		for (datalen = skb_frag_size(frag); datalen; ) {
58762306a36Sopenharmony_ci			len = min_t(unsigned int, datalen,
58862306a36Sopenharmony_ci				    XLGMAC_TX_MAX_BUF_SIZE);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci			skb_dma = skb_frag_dma_map(pdata->dev, frag, offset,
59162306a36Sopenharmony_ci						   len, DMA_TO_DEVICE);
59262306a36Sopenharmony_ci			if (dma_mapping_error(pdata->dev, skb_dma)) {
59362306a36Sopenharmony_ci				netdev_alert(pdata->netdev,
59462306a36Sopenharmony_ci					     "skb_frag_dma_map failed\n");
59562306a36Sopenharmony_ci				goto err_out;
59662306a36Sopenharmony_ci			}
59762306a36Sopenharmony_ci			desc_data->skb_dma = skb_dma;
59862306a36Sopenharmony_ci			desc_data->skb_dma_len = len;
59962306a36Sopenharmony_ci			desc_data->mapped_as_page = 1;
60062306a36Sopenharmony_ci			netif_dbg(pdata, tx_queued, pdata->netdev,
60162306a36Sopenharmony_ci				  "skb frag: index=%u, dma=%pad, len=%u\n",
60262306a36Sopenharmony_ci				  cur_index, &skb_dma, len);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci			datalen -= len;
60562306a36Sopenharmony_ci			offset += len;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci			pkt_info->length += len;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci			cur_index++;
61062306a36Sopenharmony_ci			desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Save the skb address in the last entry. We always have some data
61562306a36Sopenharmony_ci	 * that has been mapped so desc_data is always advanced past the last
61662306a36Sopenharmony_ci	 * piece of mapped data - use the entry pointed to by cur_index - 1.
61762306a36Sopenharmony_ci	 */
61862306a36Sopenharmony_ci	desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index - 1);
61962306a36Sopenharmony_ci	desc_data->skb = skb;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* Save the number of descriptor entries used */
62262306a36Sopenharmony_ci	pkt_info->desc_count = cur_index - start_index;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return pkt_info->desc_count;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cierr_out:
62762306a36Sopenharmony_ci	while (start_index < cur_index) {
62862306a36Sopenharmony_ci		desc_data = XLGMAC_GET_DESC_DATA(ring, start_index++);
62962306a36Sopenharmony_ci		xlgmac_unmap_desc_data(pdata, desc_data);
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_civoid xlgmac_init_desc_ops(struct xlgmac_desc_ops *desc_ops)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	desc_ops->alloc_channels_and_rings = xlgmac_alloc_channels_and_rings;
63862306a36Sopenharmony_ci	desc_ops->free_channels_and_rings = xlgmac_free_channels_and_rings;
63962306a36Sopenharmony_ci	desc_ops->map_tx_skb = xlgmac_map_tx_skb;
64062306a36Sopenharmony_ci	desc_ops->map_rx_buffer = xlgmac_map_rx_buffer;
64162306a36Sopenharmony_ci	desc_ops->unmap_desc_data = xlgmac_unmap_desc_data;
64262306a36Sopenharmony_ci	desc_ops->tx_desc_init = xlgmac_tx_desc_init;
64362306a36Sopenharmony_ci	desc_ops->rx_desc_init = xlgmac_rx_desc_init;
64462306a36Sopenharmony_ci}
645