18c2ecf20Sopenharmony_ci/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is dual-licensed; you may select either version 2 of 68c2ecf20Sopenharmony_ci * the GNU General Public License ("GPL") or BSD license ("BSD"). 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This Synopsys DWC XLGMAC software driver and associated documentation 98c2ecf20Sopenharmony_ci * (hereinafter the "Software") is an unsupported proprietary work of 108c2ecf20Sopenharmony_ci * Synopsys, Inc. unless otherwise expressly agreed to in writing between 118c2ecf20Sopenharmony_ci * Synopsys and you. The Software IS NOT an item of Licensed Software or a 128c2ecf20Sopenharmony_ci * Licensed Product under any End User Software License Agreement or 138c2ecf20Sopenharmony_ci * Agreement for Licensed Products with Synopsys or any supplement thereto. 148c2ecf20Sopenharmony_ci * Synopsys is a registered trademark of Synopsys, Inc. Other names included 158c2ecf20Sopenharmony_ci * in the SOFTWARE may be the trademarks of their respective owners. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "dwc-xlgmac.h" 198c2ecf20Sopenharmony_ci#include "dwc-xlgmac-reg.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void xlgmac_unmap_desc_data(struct xlgmac_pdata *pdata, 228c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci if (desc_data->skb_dma) { 258c2ecf20Sopenharmony_ci if (desc_data->mapped_as_page) { 268c2ecf20Sopenharmony_ci dma_unmap_page(pdata->dev, desc_data->skb_dma, 278c2ecf20Sopenharmony_ci desc_data->skb_dma_len, DMA_TO_DEVICE); 288c2ecf20Sopenharmony_ci } else { 298c2ecf20Sopenharmony_ci dma_unmap_single(pdata->dev, desc_data->skb_dma, 308c2ecf20Sopenharmony_ci desc_data->skb_dma_len, DMA_TO_DEVICE); 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci desc_data->skb_dma = 0; 338c2ecf20Sopenharmony_ci desc_data->skb_dma_len = 0; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (desc_data->skb) { 378c2ecf20Sopenharmony_ci dev_kfree_skb_any(desc_data->skb); 388c2ecf20Sopenharmony_ci desc_data->skb = NULL; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (desc_data->rx.hdr.pa.pages) 428c2ecf20Sopenharmony_ci put_page(desc_data->rx.hdr.pa.pages); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (desc_data->rx.hdr.pa_unmap.pages) { 458c2ecf20Sopenharmony_ci dma_unmap_page(pdata->dev, desc_data->rx.hdr.pa_unmap.pages_dma, 468c2ecf20Sopenharmony_ci desc_data->rx.hdr.pa_unmap.pages_len, 478c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 488c2ecf20Sopenharmony_ci put_page(desc_data->rx.hdr.pa_unmap.pages); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (desc_data->rx.buf.pa.pages) 528c2ecf20Sopenharmony_ci put_page(desc_data->rx.buf.pa.pages); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (desc_data->rx.buf.pa_unmap.pages) { 558c2ecf20Sopenharmony_ci dma_unmap_page(pdata->dev, desc_data->rx.buf.pa_unmap.pages_dma, 568c2ecf20Sopenharmony_ci desc_data->rx.buf.pa_unmap.pages_len, 578c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 588c2ecf20Sopenharmony_ci put_page(desc_data->rx.buf.pa_unmap.pages); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci memset(&desc_data->tx, 0, sizeof(desc_data->tx)); 628c2ecf20Sopenharmony_ci memset(&desc_data->rx, 0, sizeof(desc_data->rx)); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci desc_data->mapped_as_page = 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (desc_data->state_saved) { 678c2ecf20Sopenharmony_ci desc_data->state_saved = 0; 688c2ecf20Sopenharmony_ci desc_data->state.skb = NULL; 698c2ecf20Sopenharmony_ci desc_data->state.len = 0; 708c2ecf20Sopenharmony_ci desc_data->state.error = 0; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void xlgmac_free_ring(struct xlgmac_pdata *pdata, 758c2ecf20Sopenharmony_ci struct xlgmac_ring *ring) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 788c2ecf20Sopenharmony_ci unsigned int i; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (!ring) 818c2ecf20Sopenharmony_ci return; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ring->desc_data_head) { 848c2ecf20Sopenharmony_ci for (i = 0; i < ring->dma_desc_count; i++) { 858c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, i); 868c2ecf20Sopenharmony_ci xlgmac_unmap_desc_data(pdata, desc_data); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci kfree(ring->desc_data_head); 908c2ecf20Sopenharmony_ci ring->desc_data_head = NULL; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (ring->rx_hdr_pa.pages) { 948c2ecf20Sopenharmony_ci dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma, 958c2ecf20Sopenharmony_ci ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE); 968c2ecf20Sopenharmony_ci put_page(ring->rx_hdr_pa.pages); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ring->rx_hdr_pa.pages = NULL; 998c2ecf20Sopenharmony_ci ring->rx_hdr_pa.pages_len = 0; 1008c2ecf20Sopenharmony_ci ring->rx_hdr_pa.pages_offset = 0; 1018c2ecf20Sopenharmony_ci ring->rx_hdr_pa.pages_dma = 0; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (ring->rx_buf_pa.pages) { 1058c2ecf20Sopenharmony_ci dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma, 1068c2ecf20Sopenharmony_ci ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE); 1078c2ecf20Sopenharmony_ci put_page(ring->rx_buf_pa.pages); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ring->rx_buf_pa.pages = NULL; 1108c2ecf20Sopenharmony_ci ring->rx_buf_pa.pages_len = 0; 1118c2ecf20Sopenharmony_ci ring->rx_buf_pa.pages_offset = 0; 1128c2ecf20Sopenharmony_ci ring->rx_buf_pa.pages_dma = 0; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (ring->dma_desc_head) { 1168c2ecf20Sopenharmony_ci dma_free_coherent(pdata->dev, 1178c2ecf20Sopenharmony_ci (sizeof(struct xlgmac_dma_desc) * 1188c2ecf20Sopenharmony_ci ring->dma_desc_count), 1198c2ecf20Sopenharmony_ci ring->dma_desc_head, 1208c2ecf20Sopenharmony_ci ring->dma_desc_head_addr); 1218c2ecf20Sopenharmony_ci ring->dma_desc_head = NULL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int xlgmac_init_ring(struct xlgmac_pdata *pdata, 1268c2ecf20Sopenharmony_ci struct xlgmac_ring *ring, 1278c2ecf20Sopenharmony_ci unsigned int dma_desc_count) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (!ring) 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Descriptors */ 1338c2ecf20Sopenharmony_ci ring->dma_desc_count = dma_desc_count; 1348c2ecf20Sopenharmony_ci ring->dma_desc_head = dma_alloc_coherent(pdata->dev, 1358c2ecf20Sopenharmony_ci (sizeof(struct xlgmac_dma_desc) * 1368c2ecf20Sopenharmony_ci dma_desc_count), 1378c2ecf20Sopenharmony_ci &ring->dma_desc_head_addr, 1388c2ecf20Sopenharmony_ci GFP_KERNEL); 1398c2ecf20Sopenharmony_ci if (!ring->dma_desc_head) 1408c2ecf20Sopenharmony_ci return -ENOMEM; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Array of descriptor data */ 1438c2ecf20Sopenharmony_ci ring->desc_data_head = kcalloc(dma_desc_count, 1448c2ecf20Sopenharmony_ci sizeof(struct xlgmac_desc_data), 1458c2ecf20Sopenharmony_ci GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!ring->desc_data_head) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, 1508c2ecf20Sopenharmony_ci "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n", 1518c2ecf20Sopenharmony_ci ring->dma_desc_head, 1528c2ecf20Sopenharmony_ci &ring->dma_desc_head_addr, 1538c2ecf20Sopenharmony_ci ring->desc_data_head); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void xlgmac_free_rings(struct xlgmac_pdata *pdata) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 1618c2ecf20Sopenharmony_ci unsigned int i; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!pdata->channel_head) 1648c2ecf20Sopenharmony_ci return; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci channel = pdata->channel_head; 1678c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 1688c2ecf20Sopenharmony_ci xlgmac_free_ring(pdata, channel->tx_ring); 1698c2ecf20Sopenharmony_ci xlgmac_free_ring(pdata, channel->rx_ring); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int xlgmac_alloc_rings(struct xlgmac_pdata *pdata) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 1768c2ecf20Sopenharmony_ci unsigned int i; 1778c2ecf20Sopenharmony_ci int ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci channel = pdata->channel_head; 1808c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 1818c2ecf20Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n", 1828c2ecf20Sopenharmony_ci channel->name); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = xlgmac_init_ring(pdata, channel->tx_ring, 1858c2ecf20Sopenharmony_ci pdata->tx_desc_count); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (ret) { 1888c2ecf20Sopenharmony_ci netdev_alert(pdata->netdev, 1898c2ecf20Sopenharmony_ci "error initializing Tx ring"); 1908c2ecf20Sopenharmony_ci goto err_init_ring; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n", 1948c2ecf20Sopenharmony_ci channel->name); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = xlgmac_init_ring(pdata, channel->rx_ring, 1978c2ecf20Sopenharmony_ci pdata->rx_desc_count); 1988c2ecf20Sopenharmony_ci if (ret) { 1998c2ecf20Sopenharmony_ci netdev_alert(pdata->netdev, 2008c2ecf20Sopenharmony_ci "error initializing Rx ring\n"); 2018c2ecf20Sopenharmony_ci goto err_init_ring; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cierr_init_ring: 2088c2ecf20Sopenharmony_ci xlgmac_free_rings(pdata); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void xlgmac_free_channels(struct xlgmac_pdata *pdata) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci if (!pdata->channel_head) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci kfree(pdata->channel_head->tx_ring); 2198c2ecf20Sopenharmony_ci pdata->channel_head->tx_ring = NULL; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci kfree(pdata->channel_head->rx_ring); 2228c2ecf20Sopenharmony_ci pdata->channel_head->rx_ring = NULL; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci kfree(pdata->channel_head); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci pdata->channel_head = NULL; 2278c2ecf20Sopenharmony_ci pdata->channel_count = 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int xlgmac_alloc_channels(struct xlgmac_pdata *pdata) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct xlgmac_channel *channel_head, *channel; 2338c2ecf20Sopenharmony_ci struct xlgmac_ring *tx_ring, *rx_ring; 2348c2ecf20Sopenharmony_ci int ret = -ENOMEM; 2358c2ecf20Sopenharmony_ci unsigned int i; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci channel_head = kcalloc(pdata->channel_count, 2388c2ecf20Sopenharmony_ci sizeof(struct xlgmac_channel), GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!channel_head) 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, 2438c2ecf20Sopenharmony_ci "channel_head=%p\n", channel_head); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci tx_ring = kcalloc(pdata->tx_ring_count, sizeof(struct xlgmac_ring), 2468c2ecf20Sopenharmony_ci GFP_KERNEL); 2478c2ecf20Sopenharmony_ci if (!tx_ring) 2488c2ecf20Sopenharmony_ci goto err_tx_ring; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct xlgmac_ring), 2518c2ecf20Sopenharmony_ci GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (!rx_ring) 2538c2ecf20Sopenharmony_ci goto err_rx_ring; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci for (i = 0, channel = channel_head; i < pdata->channel_count; 2568c2ecf20Sopenharmony_ci i++, channel++) { 2578c2ecf20Sopenharmony_ci snprintf(channel->name, sizeof(channel->name), "channel-%u", i); 2588c2ecf20Sopenharmony_ci channel->pdata = pdata; 2598c2ecf20Sopenharmony_ci channel->queue_index = i; 2608c2ecf20Sopenharmony_ci channel->dma_regs = pdata->mac_regs + DMA_CH_BASE + 2618c2ecf20Sopenharmony_ci (DMA_CH_INC * i); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (pdata->per_channel_irq) { 2648c2ecf20Sopenharmony_ci /* Get the per DMA interrupt */ 2658c2ecf20Sopenharmony_ci ret = pdata->channel_irq[i]; 2668c2ecf20Sopenharmony_ci if (ret < 0) { 2678c2ecf20Sopenharmony_ci netdev_err(pdata->netdev, 2688c2ecf20Sopenharmony_ci "get_irq %u failed\n", 2698c2ecf20Sopenharmony_ci i + 1); 2708c2ecf20Sopenharmony_ci goto err_irq; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci channel->dma_irq = ret; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (i < pdata->tx_ring_count) 2768c2ecf20Sopenharmony_ci channel->tx_ring = tx_ring++; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (i < pdata->rx_ring_count) 2798c2ecf20Sopenharmony_ci channel->rx_ring = rx_ring++; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, 2828c2ecf20Sopenharmony_ci "%s: dma_regs=%p, tx_ring=%p, rx_ring=%p\n", 2838c2ecf20Sopenharmony_ci channel->name, channel->dma_regs, 2848c2ecf20Sopenharmony_ci channel->tx_ring, channel->rx_ring); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci pdata->channel_head = channel_head; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cierr_irq: 2928c2ecf20Sopenharmony_ci kfree(rx_ring); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cierr_rx_ring: 2958c2ecf20Sopenharmony_ci kfree(tx_ring); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cierr_tx_ring: 2988c2ecf20Sopenharmony_ci kfree(channel_head); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return ret; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void xlgmac_free_channels_and_rings(struct xlgmac_pdata *pdata) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci xlgmac_free_rings(pdata); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci xlgmac_free_channels(pdata); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int xlgmac_alloc_channels_and_rings(struct xlgmac_pdata *pdata) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci int ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci ret = xlgmac_alloc_channels(pdata); 3158c2ecf20Sopenharmony_ci if (ret) 3168c2ecf20Sopenharmony_ci goto err_alloc; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ret = xlgmac_alloc_rings(pdata); 3198c2ecf20Sopenharmony_ci if (ret) 3208c2ecf20Sopenharmony_ci goto err_alloc; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cierr_alloc: 3258c2ecf20Sopenharmony_ci xlgmac_free_channels_and_rings(pdata); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int xlgmac_alloc_pages(struct xlgmac_pdata *pdata, 3318c2ecf20Sopenharmony_ci struct xlgmac_page_alloc *pa, 3328c2ecf20Sopenharmony_ci gfp_t gfp, int order) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct page *pages = NULL; 3358c2ecf20Sopenharmony_ci dma_addr_t pages_dma; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Try to obtain pages, decreasing order if necessary */ 3388c2ecf20Sopenharmony_ci gfp |= __GFP_COMP | __GFP_NOWARN; 3398c2ecf20Sopenharmony_ci while (order >= 0) { 3408c2ecf20Sopenharmony_ci pages = alloc_pages(gfp, order); 3418c2ecf20Sopenharmony_ci if (pages) 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci order--; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci if (!pages) 3478c2ecf20Sopenharmony_ci return -ENOMEM; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Map the pages */ 3508c2ecf20Sopenharmony_ci pages_dma = dma_map_page(pdata->dev, pages, 0, 3518c2ecf20Sopenharmony_ci PAGE_SIZE << order, DMA_FROM_DEVICE); 3528c2ecf20Sopenharmony_ci if (dma_mapping_error(pdata->dev, pages_dma)) { 3538c2ecf20Sopenharmony_ci put_page(pages); 3548c2ecf20Sopenharmony_ci return -ENOMEM; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci pa->pages = pages; 3588c2ecf20Sopenharmony_ci pa->pages_len = PAGE_SIZE << order; 3598c2ecf20Sopenharmony_ci pa->pages_offset = 0; 3608c2ecf20Sopenharmony_ci pa->pages_dma = pages_dma; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void xlgmac_set_buffer_data(struct xlgmac_buffer_data *bd, 3668c2ecf20Sopenharmony_ci struct xlgmac_page_alloc *pa, 3678c2ecf20Sopenharmony_ci unsigned int len) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci get_page(pa->pages); 3708c2ecf20Sopenharmony_ci bd->pa = *pa; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci bd->dma_base = pa->pages_dma; 3738c2ecf20Sopenharmony_ci bd->dma_off = pa->pages_offset; 3748c2ecf20Sopenharmony_ci bd->dma_len = len; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci pa->pages_offset += len; 3778c2ecf20Sopenharmony_ci if ((pa->pages_offset + len) > pa->pages_len) { 3788c2ecf20Sopenharmony_ci /* This data descriptor is responsible for unmapping page(s) */ 3798c2ecf20Sopenharmony_ci bd->pa_unmap = *pa; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Get a new allocation next time */ 3828c2ecf20Sopenharmony_ci pa->pages = NULL; 3838c2ecf20Sopenharmony_ci pa->pages_len = 0; 3848c2ecf20Sopenharmony_ci pa->pages_offset = 0; 3858c2ecf20Sopenharmony_ci pa->pages_dma = 0; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int xlgmac_map_rx_buffer(struct xlgmac_pdata *pdata, 3908c2ecf20Sopenharmony_ci struct xlgmac_ring *ring, 3918c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci int order, ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!ring->rx_hdr_pa.pages) { 3968c2ecf20Sopenharmony_ci ret = xlgmac_alloc_pages(pdata, &ring->rx_hdr_pa, 3978c2ecf20Sopenharmony_ci GFP_ATOMIC, 0); 3988c2ecf20Sopenharmony_ci if (ret) 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!ring->rx_buf_pa.pages) { 4038c2ecf20Sopenharmony_ci order = max_t(int, PAGE_ALLOC_COSTLY_ORDER - 1, 0); 4048c2ecf20Sopenharmony_ci ret = xlgmac_alloc_pages(pdata, &ring->rx_buf_pa, 4058c2ecf20Sopenharmony_ci GFP_ATOMIC, order); 4068c2ecf20Sopenharmony_ci if (ret) 4078c2ecf20Sopenharmony_ci return ret; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Set up the header page info */ 4118c2ecf20Sopenharmony_ci xlgmac_set_buffer_data(&desc_data->rx.hdr, &ring->rx_hdr_pa, 4128c2ecf20Sopenharmony_ci XLGMAC_SKB_ALLOC_SIZE); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Set up the buffer page info */ 4158c2ecf20Sopenharmony_ci xlgmac_set_buffer_data(&desc_data->rx.buf, &ring->rx_buf_pa, 4168c2ecf20Sopenharmony_ci pdata->rx_buf_size); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic void xlgmac_tx_desc_init(struct xlgmac_pdata *pdata) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 4248c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 4258c2ecf20Sopenharmony_ci struct xlgmac_dma_desc *dma_desc; 4268c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 4278c2ecf20Sopenharmony_ci struct xlgmac_ring *ring; 4288c2ecf20Sopenharmony_ci dma_addr_t dma_desc_addr; 4298c2ecf20Sopenharmony_ci unsigned int i, j; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci channel = pdata->channel_head; 4328c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 4338c2ecf20Sopenharmony_ci ring = channel->tx_ring; 4348c2ecf20Sopenharmony_ci if (!ring) 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci dma_desc = ring->dma_desc_head; 4388c2ecf20Sopenharmony_ci dma_desc_addr = ring->dma_desc_head_addr; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for (j = 0; j < ring->dma_desc_count; j++) { 4418c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, j); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci desc_data->dma_desc = dma_desc; 4448c2ecf20Sopenharmony_ci desc_data->dma_desc_addr = dma_desc_addr; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dma_desc++; 4478c2ecf20Sopenharmony_ci dma_desc_addr += sizeof(struct xlgmac_dma_desc); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ring->cur = 0; 4518c2ecf20Sopenharmony_ci ring->dirty = 0; 4528c2ecf20Sopenharmony_ci memset(&ring->tx, 0, sizeof(ring->tx)); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci hw_ops->tx_desc_init(channel); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void xlgmac_rx_desc_init(struct xlgmac_pdata *pdata) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; 4618c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 4628c2ecf20Sopenharmony_ci struct xlgmac_dma_desc *dma_desc; 4638c2ecf20Sopenharmony_ci struct xlgmac_channel *channel; 4648c2ecf20Sopenharmony_ci struct xlgmac_ring *ring; 4658c2ecf20Sopenharmony_ci dma_addr_t dma_desc_addr; 4668c2ecf20Sopenharmony_ci unsigned int i, j; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci channel = pdata->channel_head; 4698c2ecf20Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++, channel++) { 4708c2ecf20Sopenharmony_ci ring = channel->rx_ring; 4718c2ecf20Sopenharmony_ci if (!ring) 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci dma_desc = ring->dma_desc_head; 4758c2ecf20Sopenharmony_ci dma_desc_addr = ring->dma_desc_head_addr; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci for (j = 0; j < ring->dma_desc_count; j++) { 4788c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, j); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci desc_data->dma_desc = dma_desc; 4818c2ecf20Sopenharmony_ci desc_data->dma_desc_addr = dma_desc_addr; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (xlgmac_map_rx_buffer(pdata, ring, desc_data)) 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci dma_desc++; 4878c2ecf20Sopenharmony_ci dma_desc_addr += sizeof(struct xlgmac_dma_desc); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ring->cur = 0; 4918c2ecf20Sopenharmony_ci ring->dirty = 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci hw_ops->rx_desc_init(channel); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int xlgmac_map_tx_skb(struct xlgmac_channel *channel, 4988c2ecf20Sopenharmony_ci struct sk_buff *skb) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct xlgmac_pdata *pdata = channel->pdata; 5018c2ecf20Sopenharmony_ci struct xlgmac_ring *ring = channel->tx_ring; 5028c2ecf20Sopenharmony_ci unsigned int start_index, cur_index; 5038c2ecf20Sopenharmony_ci struct xlgmac_desc_data *desc_data; 5048c2ecf20Sopenharmony_ci unsigned int offset, datalen, len; 5058c2ecf20Sopenharmony_ci struct xlgmac_pkt_info *pkt_info; 5068c2ecf20Sopenharmony_ci skb_frag_t *frag; 5078c2ecf20Sopenharmony_ci unsigned int tso, vlan; 5088c2ecf20Sopenharmony_ci dma_addr_t skb_dma; 5098c2ecf20Sopenharmony_ci unsigned int i; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci offset = 0; 5128c2ecf20Sopenharmony_ci start_index = ring->cur; 5138c2ecf20Sopenharmony_ci cur_index = ring->cur; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci pkt_info = &ring->pkt_info; 5168c2ecf20Sopenharmony_ci pkt_info->desc_count = 0; 5178c2ecf20Sopenharmony_ci pkt_info->length = 0; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci tso = XLGMAC_GET_REG_BITS(pkt_info->attributes, 5208c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, 5218c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN); 5228c2ecf20Sopenharmony_ci vlan = XLGMAC_GET_REG_BITS(pkt_info->attributes, 5238c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, 5248c2ecf20Sopenharmony_ci TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Save space for a context descriptor if needed */ 5278c2ecf20Sopenharmony_ci if ((tso && (pkt_info->mss != ring->tx.cur_mss)) || 5288c2ecf20Sopenharmony_ci (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag))) 5298c2ecf20Sopenharmony_ci cur_index++; 5308c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (tso) { 5338c2ecf20Sopenharmony_ci /* Map the TSO header */ 5348c2ecf20Sopenharmony_ci skb_dma = dma_map_single(pdata->dev, skb->data, 5358c2ecf20Sopenharmony_ci pkt_info->header_len, DMA_TO_DEVICE); 5368c2ecf20Sopenharmony_ci if (dma_mapping_error(pdata->dev, skb_dma)) { 5378c2ecf20Sopenharmony_ci netdev_alert(pdata->netdev, "dma_map_single failed\n"); 5388c2ecf20Sopenharmony_ci goto err_out; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci desc_data->skb_dma = skb_dma; 5418c2ecf20Sopenharmony_ci desc_data->skb_dma_len = pkt_info->header_len; 5428c2ecf20Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 5438c2ecf20Sopenharmony_ci "skb header: index=%u, dma=%pad, len=%u\n", 5448c2ecf20Sopenharmony_ci cur_index, &skb_dma, pkt_info->header_len); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci offset = pkt_info->header_len; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci pkt_info->length += pkt_info->header_len; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci cur_index++; 5518c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Map the (remainder of the) packet */ 5558c2ecf20Sopenharmony_ci for (datalen = skb_headlen(skb) - offset; datalen; ) { 5568c2ecf20Sopenharmony_ci len = min_t(unsigned int, datalen, XLGMAC_TX_MAX_BUF_SIZE); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci skb_dma = dma_map_single(pdata->dev, skb->data + offset, len, 5598c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 5608c2ecf20Sopenharmony_ci if (dma_mapping_error(pdata->dev, skb_dma)) { 5618c2ecf20Sopenharmony_ci netdev_alert(pdata->netdev, "dma_map_single failed\n"); 5628c2ecf20Sopenharmony_ci goto err_out; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci desc_data->skb_dma = skb_dma; 5658c2ecf20Sopenharmony_ci desc_data->skb_dma_len = len; 5668c2ecf20Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 5678c2ecf20Sopenharmony_ci "skb data: index=%u, dma=%pad, len=%u\n", 5688c2ecf20Sopenharmony_ci cur_index, &skb_dma, len); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci datalen -= len; 5718c2ecf20Sopenharmony_ci offset += len; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci pkt_info->length += len; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci cur_index++; 5768c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 5808c2ecf20Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 5818c2ecf20Sopenharmony_ci "mapping frag %u\n", i); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 5848c2ecf20Sopenharmony_ci offset = 0; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci for (datalen = skb_frag_size(frag); datalen; ) { 5878c2ecf20Sopenharmony_ci len = min_t(unsigned int, datalen, 5888c2ecf20Sopenharmony_ci XLGMAC_TX_MAX_BUF_SIZE); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci skb_dma = skb_frag_dma_map(pdata->dev, frag, offset, 5918c2ecf20Sopenharmony_ci len, DMA_TO_DEVICE); 5928c2ecf20Sopenharmony_ci if (dma_mapping_error(pdata->dev, skb_dma)) { 5938c2ecf20Sopenharmony_ci netdev_alert(pdata->netdev, 5948c2ecf20Sopenharmony_ci "skb_frag_dma_map failed\n"); 5958c2ecf20Sopenharmony_ci goto err_out; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci desc_data->skb_dma = skb_dma; 5988c2ecf20Sopenharmony_ci desc_data->skb_dma_len = len; 5998c2ecf20Sopenharmony_ci desc_data->mapped_as_page = 1; 6008c2ecf20Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 6018c2ecf20Sopenharmony_ci "skb frag: index=%u, dma=%pad, len=%u\n", 6028c2ecf20Sopenharmony_ci cur_index, &skb_dma, len); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci datalen -= len; 6058c2ecf20Sopenharmony_ci offset += len; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci pkt_info->length += len; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci cur_index++; 6108c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* Save the skb address in the last entry. We always have some data 6158c2ecf20Sopenharmony_ci * that has been mapped so desc_data is always advanced past the last 6168c2ecf20Sopenharmony_ci * piece of mapped data - use the entry pointed to by cur_index - 1. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index - 1); 6198c2ecf20Sopenharmony_ci desc_data->skb = skb; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* Save the number of descriptor entries used */ 6228c2ecf20Sopenharmony_ci pkt_info->desc_count = cur_index - start_index; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return pkt_info->desc_count; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cierr_out: 6278c2ecf20Sopenharmony_ci while (start_index < cur_index) { 6288c2ecf20Sopenharmony_ci desc_data = XLGMAC_GET_DESC_DATA(ring, start_index++); 6298c2ecf20Sopenharmony_ci xlgmac_unmap_desc_data(pdata, desc_data); 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_civoid xlgmac_init_desc_ops(struct xlgmac_desc_ops *desc_ops) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci desc_ops->alloc_channles_and_rings = xlgmac_alloc_channels_and_rings; 6388c2ecf20Sopenharmony_ci desc_ops->free_channels_and_rings = xlgmac_free_channels_and_rings; 6398c2ecf20Sopenharmony_ci desc_ops->map_tx_skb = xlgmac_map_tx_skb; 6408c2ecf20Sopenharmony_ci desc_ops->map_rx_buffer = xlgmac_map_rx_buffer; 6418c2ecf20Sopenharmony_ci desc_ops->unmap_desc_data = xlgmac_unmap_desc_data; 6428c2ecf20Sopenharmony_ci desc_ops->tx_desc_init = xlgmac_tx_desc_init; 6438c2ecf20Sopenharmony_ci desc_ops->rx_desc_init = xlgmac_rx_desc_init; 6448c2ecf20Sopenharmony_ci} 645