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