162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * AMD 10Gb Ethernet driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is available to you under your choice of the following two 562306a36Sopenharmony_ci * licenses: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * License 1: GPLv2 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2014 Advanced Micro Devices, Inc. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This file is free software; you may copy, redistribute and/or modify 1262306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1362306a36Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or (at 1462306a36Sopenharmony_ci * your option) any later version. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1762306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1862306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1962306a36Sopenharmony_ci * General Public License for more details. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 2262306a36Sopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * This file incorporates work covered by the following copyright and 2562306a36Sopenharmony_ci * permission notice: 2662306a36Sopenharmony_ci * The Synopsys DWC ETHER XGMAC Software Driver and documentation 2762306a36Sopenharmony_ci * (hereinafter "Software") is an unsupported proprietary work of Synopsys, 2862306a36Sopenharmony_ci * Inc. unless otherwise expressly agreed to in writing between Synopsys 2962306a36Sopenharmony_ci * and you. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * The Software IS NOT an item of Licensed Software or Licensed Product 3262306a36Sopenharmony_ci * under any End User Software License Agreement or Agreement for Licensed 3362306a36Sopenharmony_ci * Product with Synopsys or any supplement thereto. Permission is hereby 3462306a36Sopenharmony_ci * granted, free of charge, to any person obtaining a copy of this software 3562306a36Sopenharmony_ci * annotated with this license and the Software, to deal in the Software 3662306a36Sopenharmony_ci * without restriction, including without limitation the rights to use, 3762306a36Sopenharmony_ci * copy, modify, merge, publish, distribute, sublicense, and/or sell copies 3862306a36Sopenharmony_ci * of the Software, and to permit persons to whom the Software is furnished 3962306a36Sopenharmony_ci * to do so, subject to the following conditions: 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included 4262306a36Sopenharmony_ci * in all copies or substantial portions of the Software. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" 4562306a36Sopenharmony_ci * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 4662306a36Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 4762306a36Sopenharmony_ci * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 4862306a36Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 5062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 5162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 5262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 5362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 5462306a36Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGE. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * License 2: Modified BSD 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Copyright (c) 2014 Advanced Micro Devices, Inc. 6062306a36Sopenharmony_ci * All rights reserved. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 6362306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 6462306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 6562306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 6662306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 6762306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 6862306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 6962306a36Sopenharmony_ci * * Neither the name of Advanced Micro Devices, Inc. nor the 7062306a36Sopenharmony_ci * names of its contributors may be used to endorse or promote products 7162306a36Sopenharmony_ci * derived from this software without specific prior written permission. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7462306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 7562306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 7662306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY 7762306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7862306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 7962306a36Sopenharmony_ci * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 8062306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 8162306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 8262306a36Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * This file incorporates work covered by the following copyright and 8562306a36Sopenharmony_ci * permission notice: 8662306a36Sopenharmony_ci * The Synopsys DWC ETHER XGMAC Software Driver and documentation 8762306a36Sopenharmony_ci * (hereinafter "Software") is an unsupported proprietary work of Synopsys, 8862306a36Sopenharmony_ci * Inc. unless otherwise expressly agreed to in writing between Synopsys 8962306a36Sopenharmony_ci * and you. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * The Software IS NOT an item of Licensed Software or Licensed Product 9262306a36Sopenharmony_ci * under any End User Software License Agreement or Agreement for Licensed 9362306a36Sopenharmony_ci * Product with Synopsys or any supplement thereto. Permission is hereby 9462306a36Sopenharmony_ci * granted, free of charge, to any person obtaining a copy of this software 9562306a36Sopenharmony_ci * annotated with this license and the Software, to deal in the Software 9662306a36Sopenharmony_ci * without restriction, including without limitation the rights to use, 9762306a36Sopenharmony_ci * copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9862306a36Sopenharmony_ci * of the Software, and to permit persons to whom the Software is furnished 9962306a36Sopenharmony_ci * to do so, subject to the following conditions: 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included 10262306a36Sopenharmony_ci * in all copies or substantial portions of the Software. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" 10562306a36Sopenharmony_ci * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 10662306a36Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 10762306a36Sopenharmony_ci * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 10862306a36Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 10962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 11062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 11162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 11262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 11362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 11462306a36Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGE. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#include "xgbe.h" 11862306a36Sopenharmony_ci#include "xgbe-common.h" 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void xgbe_unmap_rdata(struct xgbe_prv_data *, struct xgbe_ring_data *); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void xgbe_free_ring(struct xgbe_prv_data *pdata, 12362306a36Sopenharmony_ci struct xgbe_ring *ring) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct xgbe_ring_data *rdata; 12662306a36Sopenharmony_ci unsigned int i; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!ring) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (ring->rdata) { 13262306a36Sopenharmony_ci for (i = 0; i < ring->rdesc_count; i++) { 13362306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, i); 13462306a36Sopenharmony_ci xgbe_unmap_rdata(pdata, rdata); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci kfree(ring->rdata); 13862306a36Sopenharmony_ci ring->rdata = NULL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ring->rx_hdr_pa.pages) { 14262306a36Sopenharmony_ci dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma, 14362306a36Sopenharmony_ci ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE); 14462306a36Sopenharmony_ci put_page(ring->rx_hdr_pa.pages); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ring->rx_hdr_pa.pages = NULL; 14762306a36Sopenharmony_ci ring->rx_hdr_pa.pages_len = 0; 14862306a36Sopenharmony_ci ring->rx_hdr_pa.pages_offset = 0; 14962306a36Sopenharmony_ci ring->rx_hdr_pa.pages_dma = 0; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (ring->rx_buf_pa.pages) { 15362306a36Sopenharmony_ci dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma, 15462306a36Sopenharmony_ci ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE); 15562306a36Sopenharmony_ci put_page(ring->rx_buf_pa.pages); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ring->rx_buf_pa.pages = NULL; 15862306a36Sopenharmony_ci ring->rx_buf_pa.pages_len = 0; 15962306a36Sopenharmony_ci ring->rx_buf_pa.pages_offset = 0; 16062306a36Sopenharmony_ci ring->rx_buf_pa.pages_dma = 0; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (ring->rdesc) { 16462306a36Sopenharmony_ci dma_free_coherent(pdata->dev, 16562306a36Sopenharmony_ci (sizeof(struct xgbe_ring_desc) * 16662306a36Sopenharmony_ci ring->rdesc_count), 16762306a36Sopenharmony_ci ring->rdesc, ring->rdesc_dma); 16862306a36Sopenharmony_ci ring->rdesc = NULL; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void xgbe_free_ring_resources(struct xgbe_prv_data *pdata) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct xgbe_channel *channel; 17562306a36Sopenharmony_ci unsigned int i; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci DBGPR("-->xgbe_free_ring_resources\n"); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++) { 18062306a36Sopenharmony_ci channel = pdata->channel[i]; 18162306a36Sopenharmony_ci xgbe_free_ring(pdata, channel->tx_ring); 18262306a36Sopenharmony_ci xgbe_free_ring(pdata, channel->rx_ring); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci DBGPR("<--xgbe_free_ring_resources\n"); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void *xgbe_alloc_node(size_t size, int node) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci void *mem; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci mem = kzalloc_node(size, GFP_KERNEL, node); 19362306a36Sopenharmony_ci if (!mem) 19462306a36Sopenharmony_ci mem = kzalloc(size, GFP_KERNEL); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return mem; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void *xgbe_dma_alloc_node(struct device *dev, size_t size, 20062306a36Sopenharmony_ci dma_addr_t *dma, int node) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci void *mem; 20362306a36Sopenharmony_ci int cur_node = dev_to_node(dev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci set_dev_node(dev, node); 20662306a36Sopenharmony_ci mem = dma_alloc_coherent(dev, size, dma, GFP_KERNEL); 20762306a36Sopenharmony_ci set_dev_node(dev, cur_node); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!mem) 21062306a36Sopenharmony_ci mem = dma_alloc_coherent(dev, size, dma, GFP_KERNEL); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return mem; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int xgbe_init_ring(struct xgbe_prv_data *pdata, 21662306a36Sopenharmony_ci struct xgbe_ring *ring, unsigned int rdesc_count) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci size_t size; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!ring) 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Descriptors */ 22462306a36Sopenharmony_ci size = rdesc_count * sizeof(struct xgbe_ring_desc); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ring->rdesc_count = rdesc_count; 22762306a36Sopenharmony_ci ring->rdesc = xgbe_dma_alloc_node(pdata->dev, size, &ring->rdesc_dma, 22862306a36Sopenharmony_ci ring->node); 22962306a36Sopenharmony_ci if (!ring->rdesc) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Descriptor information */ 23362306a36Sopenharmony_ci size = rdesc_count * sizeof(struct xgbe_ring_data); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci ring->rdata = xgbe_alloc_node(size, ring->node); 23662306a36Sopenharmony_ci if (!ring->rdata) 23762306a36Sopenharmony_ci return -ENOMEM; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, 24062306a36Sopenharmony_ci "rdesc=%p, rdesc_dma=%pad, rdata=%p, node=%d\n", 24162306a36Sopenharmony_ci ring->rdesc, &ring->rdesc_dma, ring->rdata, ring->node); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int xgbe_alloc_ring_resources(struct xgbe_prv_data *pdata) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct xgbe_channel *channel; 24962306a36Sopenharmony_ci unsigned int i; 25062306a36Sopenharmony_ci int ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++) { 25362306a36Sopenharmony_ci channel = pdata->channel[i]; 25462306a36Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n", 25562306a36Sopenharmony_ci channel->name); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ret = xgbe_init_ring(pdata, channel->tx_ring, 25862306a36Sopenharmony_ci pdata->tx_desc_count); 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci netdev_alert(pdata->netdev, 26162306a36Sopenharmony_ci "error initializing Tx ring\n"); 26262306a36Sopenharmony_ci goto err_ring; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n", 26662306a36Sopenharmony_ci channel->name); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ret = xgbe_init_ring(pdata, channel->rx_ring, 26962306a36Sopenharmony_ci pdata->rx_desc_count); 27062306a36Sopenharmony_ci if (ret) { 27162306a36Sopenharmony_ci netdev_alert(pdata->netdev, 27262306a36Sopenharmony_ci "error initializing Rx ring\n"); 27362306a36Sopenharmony_ci goto err_ring; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cierr_ring: 28062306a36Sopenharmony_ci xgbe_free_ring_resources(pdata); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int xgbe_alloc_pages(struct xgbe_prv_data *pdata, 28662306a36Sopenharmony_ci struct xgbe_page_alloc *pa, int alloc_order, 28762306a36Sopenharmony_ci int node) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct page *pages = NULL; 29062306a36Sopenharmony_ci dma_addr_t pages_dma; 29162306a36Sopenharmony_ci gfp_t gfp; 29262306a36Sopenharmony_ci int order; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciagain: 29562306a36Sopenharmony_ci order = alloc_order; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Try to obtain pages, decreasing order if necessary */ 29862306a36Sopenharmony_ci gfp = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN; 29962306a36Sopenharmony_ci while (order >= 0) { 30062306a36Sopenharmony_ci pages = alloc_pages_node(node, gfp, order); 30162306a36Sopenharmony_ci if (pages) 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci order--; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* If we couldn't get local pages, try getting from anywhere */ 30862306a36Sopenharmony_ci if (!pages && (node != NUMA_NO_NODE)) { 30962306a36Sopenharmony_ci node = NUMA_NO_NODE; 31062306a36Sopenharmony_ci goto again; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!pages) 31462306a36Sopenharmony_ci return -ENOMEM; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Map the pages */ 31762306a36Sopenharmony_ci pages_dma = dma_map_page(pdata->dev, pages, 0, 31862306a36Sopenharmony_ci PAGE_SIZE << order, DMA_FROM_DEVICE); 31962306a36Sopenharmony_ci if (dma_mapping_error(pdata->dev, pages_dma)) { 32062306a36Sopenharmony_ci put_page(pages); 32162306a36Sopenharmony_ci return -ENOMEM; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci pa->pages = pages; 32562306a36Sopenharmony_ci pa->pages_len = PAGE_SIZE << order; 32662306a36Sopenharmony_ci pa->pages_offset = 0; 32762306a36Sopenharmony_ci pa->pages_dma = pages_dma; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void xgbe_set_buffer_data(struct xgbe_buffer_data *bd, 33362306a36Sopenharmony_ci struct xgbe_page_alloc *pa, 33462306a36Sopenharmony_ci unsigned int len) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci get_page(pa->pages); 33762306a36Sopenharmony_ci bd->pa = *pa; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci bd->dma_base = pa->pages_dma; 34062306a36Sopenharmony_ci bd->dma_off = pa->pages_offset; 34162306a36Sopenharmony_ci bd->dma_len = len; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci pa->pages_offset += len; 34462306a36Sopenharmony_ci if ((pa->pages_offset + len) > pa->pages_len) { 34562306a36Sopenharmony_ci /* This data descriptor is responsible for unmapping page(s) */ 34662306a36Sopenharmony_ci bd->pa_unmap = *pa; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Get a new allocation next time */ 34962306a36Sopenharmony_ci pa->pages = NULL; 35062306a36Sopenharmony_ci pa->pages_len = 0; 35162306a36Sopenharmony_ci pa->pages_offset = 0; 35262306a36Sopenharmony_ci pa->pages_dma = 0; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int xgbe_map_rx_buffer(struct xgbe_prv_data *pdata, 35762306a36Sopenharmony_ci struct xgbe_ring *ring, 35862306a36Sopenharmony_ci struct xgbe_ring_data *rdata) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci int ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!ring->rx_hdr_pa.pages) { 36362306a36Sopenharmony_ci ret = xgbe_alloc_pages(pdata, &ring->rx_hdr_pa, 0, ring->node); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci return ret; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!ring->rx_buf_pa.pages) { 36962306a36Sopenharmony_ci ret = xgbe_alloc_pages(pdata, &ring->rx_buf_pa, 37062306a36Sopenharmony_ci PAGE_ALLOC_COSTLY_ORDER, ring->node); 37162306a36Sopenharmony_ci if (ret) 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Set up the header page info */ 37662306a36Sopenharmony_ci xgbe_set_buffer_data(&rdata->rx.hdr, &ring->rx_hdr_pa, 37762306a36Sopenharmony_ci XGBE_SKB_ALLOC_SIZE); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* Set up the buffer page info */ 38062306a36Sopenharmony_ci xgbe_set_buffer_data(&rdata->rx.buf, &ring->rx_buf_pa, 38162306a36Sopenharmony_ci pdata->rx_buf_size); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void xgbe_wrapper_tx_descriptor_init(struct xgbe_prv_data *pdata) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct xgbe_hw_if *hw_if = &pdata->hw_if; 38962306a36Sopenharmony_ci struct xgbe_channel *channel; 39062306a36Sopenharmony_ci struct xgbe_ring *ring; 39162306a36Sopenharmony_ci struct xgbe_ring_data *rdata; 39262306a36Sopenharmony_ci struct xgbe_ring_desc *rdesc; 39362306a36Sopenharmony_ci dma_addr_t rdesc_dma; 39462306a36Sopenharmony_ci unsigned int i, j; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci DBGPR("-->xgbe_wrapper_tx_descriptor_init\n"); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++) { 39962306a36Sopenharmony_ci channel = pdata->channel[i]; 40062306a36Sopenharmony_ci ring = channel->tx_ring; 40162306a36Sopenharmony_ci if (!ring) 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci rdesc = ring->rdesc; 40562306a36Sopenharmony_ci rdesc_dma = ring->rdesc_dma; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (j = 0; j < ring->rdesc_count; j++) { 40862306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, j); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci rdata->rdesc = rdesc; 41162306a36Sopenharmony_ci rdata->rdesc_dma = rdesc_dma; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci rdesc++; 41462306a36Sopenharmony_ci rdesc_dma += sizeof(struct xgbe_ring_desc); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ring->cur = 0; 41862306a36Sopenharmony_ci ring->dirty = 0; 41962306a36Sopenharmony_ci memset(&ring->tx, 0, sizeof(ring->tx)); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci hw_if->tx_desc_init(channel); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci DBGPR("<--xgbe_wrapper_tx_descriptor_init\n"); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct xgbe_hw_if *hw_if = &pdata->hw_if; 43062306a36Sopenharmony_ci struct xgbe_channel *channel; 43162306a36Sopenharmony_ci struct xgbe_ring *ring; 43262306a36Sopenharmony_ci struct xgbe_ring_desc *rdesc; 43362306a36Sopenharmony_ci struct xgbe_ring_data *rdata; 43462306a36Sopenharmony_ci dma_addr_t rdesc_dma; 43562306a36Sopenharmony_ci unsigned int i, j; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci DBGPR("-->xgbe_wrapper_rx_descriptor_init\n"); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci for (i = 0; i < pdata->channel_count; i++) { 44062306a36Sopenharmony_ci channel = pdata->channel[i]; 44162306a36Sopenharmony_ci ring = channel->rx_ring; 44262306a36Sopenharmony_ci if (!ring) 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci rdesc = ring->rdesc; 44662306a36Sopenharmony_ci rdesc_dma = ring->rdesc_dma; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci for (j = 0; j < ring->rdesc_count; j++) { 44962306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, j); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci rdata->rdesc = rdesc; 45262306a36Sopenharmony_ci rdata->rdesc_dma = rdesc_dma; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (xgbe_map_rx_buffer(pdata, ring, rdata)) 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci rdesc++; 45862306a36Sopenharmony_ci rdesc_dma += sizeof(struct xgbe_ring_desc); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ring->cur = 0; 46262306a36Sopenharmony_ci ring->dirty = 0; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci hw_if->rx_desc_init(channel); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci DBGPR("<--xgbe_wrapper_rx_descriptor_init\n"); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void xgbe_unmap_rdata(struct xgbe_prv_data *pdata, 47162306a36Sopenharmony_ci struct xgbe_ring_data *rdata) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci if (rdata->skb_dma) { 47462306a36Sopenharmony_ci if (rdata->mapped_as_page) { 47562306a36Sopenharmony_ci dma_unmap_page(pdata->dev, rdata->skb_dma, 47662306a36Sopenharmony_ci rdata->skb_dma_len, DMA_TO_DEVICE); 47762306a36Sopenharmony_ci } else { 47862306a36Sopenharmony_ci dma_unmap_single(pdata->dev, rdata->skb_dma, 47962306a36Sopenharmony_ci rdata->skb_dma_len, DMA_TO_DEVICE); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci rdata->skb_dma = 0; 48262306a36Sopenharmony_ci rdata->skb_dma_len = 0; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (rdata->skb) { 48662306a36Sopenharmony_ci dev_kfree_skb_any(rdata->skb); 48762306a36Sopenharmony_ci rdata->skb = NULL; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (rdata->rx.hdr.pa.pages) 49162306a36Sopenharmony_ci put_page(rdata->rx.hdr.pa.pages); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (rdata->rx.hdr.pa_unmap.pages) { 49462306a36Sopenharmony_ci dma_unmap_page(pdata->dev, rdata->rx.hdr.pa_unmap.pages_dma, 49562306a36Sopenharmony_ci rdata->rx.hdr.pa_unmap.pages_len, 49662306a36Sopenharmony_ci DMA_FROM_DEVICE); 49762306a36Sopenharmony_ci put_page(rdata->rx.hdr.pa_unmap.pages); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (rdata->rx.buf.pa.pages) 50162306a36Sopenharmony_ci put_page(rdata->rx.buf.pa.pages); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (rdata->rx.buf.pa_unmap.pages) { 50462306a36Sopenharmony_ci dma_unmap_page(pdata->dev, rdata->rx.buf.pa_unmap.pages_dma, 50562306a36Sopenharmony_ci rdata->rx.buf.pa_unmap.pages_len, 50662306a36Sopenharmony_ci DMA_FROM_DEVICE); 50762306a36Sopenharmony_ci put_page(rdata->rx.buf.pa_unmap.pages); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci memset(&rdata->tx, 0, sizeof(rdata->tx)); 51162306a36Sopenharmony_ci memset(&rdata->rx, 0, sizeof(rdata->rx)); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci rdata->mapped_as_page = 0; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (rdata->state_saved) { 51662306a36Sopenharmony_ci rdata->state_saved = 0; 51762306a36Sopenharmony_ci rdata->state.skb = NULL; 51862306a36Sopenharmony_ci rdata->state.len = 0; 51962306a36Sopenharmony_ci rdata->state.error = 0; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct xgbe_prv_data *pdata = channel->pdata; 52662306a36Sopenharmony_ci struct xgbe_ring *ring = channel->tx_ring; 52762306a36Sopenharmony_ci struct xgbe_ring_data *rdata; 52862306a36Sopenharmony_ci struct xgbe_packet_data *packet; 52962306a36Sopenharmony_ci skb_frag_t *frag; 53062306a36Sopenharmony_ci dma_addr_t skb_dma; 53162306a36Sopenharmony_ci unsigned int start_index, cur_index; 53262306a36Sopenharmony_ci unsigned int offset, tso, vlan, datalen, len; 53362306a36Sopenharmony_ci unsigned int i; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci DBGPR("-->xgbe_map_tx_skb: cur = %d\n", ring->cur); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci offset = 0; 53862306a36Sopenharmony_ci start_index = ring->cur; 53962306a36Sopenharmony_ci cur_index = ring->cur; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci packet = &ring->packet_data; 54262306a36Sopenharmony_ci packet->rdesc_count = 0; 54362306a36Sopenharmony_ci packet->length = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, 54662306a36Sopenharmony_ci TSO_ENABLE); 54762306a36Sopenharmony_ci vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, 54862306a36Sopenharmony_ci VLAN_CTAG); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Save space for a context descriptor if needed */ 55162306a36Sopenharmony_ci if ((tso && (packet->mss != ring->tx.cur_mss)) || 55262306a36Sopenharmony_ci (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag))) 55362306a36Sopenharmony_ci cur_index++; 55462306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, cur_index); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (tso) { 55762306a36Sopenharmony_ci /* Map the TSO header */ 55862306a36Sopenharmony_ci skb_dma = dma_map_single(pdata->dev, skb->data, 55962306a36Sopenharmony_ci packet->header_len, 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 rdata->skb_dma = skb_dma; 56562306a36Sopenharmony_ci rdata->skb_dma_len = packet->header_len; 56662306a36Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 56762306a36Sopenharmony_ci "skb header: index=%u, dma=%pad, len=%u\n", 56862306a36Sopenharmony_ci cur_index, &skb_dma, packet->header_len); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci offset = packet->header_len; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci packet->length += packet->header_len; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci cur_index++; 57562306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, cur_index); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* Map the (remainder of the) packet */ 57962306a36Sopenharmony_ci for (datalen = skb_headlen(skb) - offset; datalen; ) { 58062306a36Sopenharmony_ci len = min_t(unsigned int, datalen, XGBE_TX_MAX_BUF_SIZE); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci skb_dma = dma_map_single(pdata->dev, skb->data + offset, len, 58362306a36Sopenharmony_ci DMA_TO_DEVICE); 58462306a36Sopenharmony_ci if (dma_mapping_error(pdata->dev, skb_dma)) { 58562306a36Sopenharmony_ci netdev_alert(pdata->netdev, "dma_map_single failed\n"); 58662306a36Sopenharmony_ci goto err_out; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci rdata->skb_dma = skb_dma; 58962306a36Sopenharmony_ci rdata->skb_dma_len = len; 59062306a36Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 59162306a36Sopenharmony_ci "skb data: index=%u, dma=%pad, len=%u\n", 59262306a36Sopenharmony_ci cur_index, &skb_dma, len); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci datalen -= len; 59562306a36Sopenharmony_ci offset += len; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci packet->length += len; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci cur_index++; 60062306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, cur_index); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 60462306a36Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 60562306a36Sopenharmony_ci "mapping frag %u\n", i); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 60862306a36Sopenharmony_ci offset = 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci for (datalen = skb_frag_size(frag); datalen; ) { 61162306a36Sopenharmony_ci len = min_t(unsigned int, datalen, 61262306a36Sopenharmony_ci XGBE_TX_MAX_BUF_SIZE); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci skb_dma = skb_frag_dma_map(pdata->dev, frag, offset, 61562306a36Sopenharmony_ci len, DMA_TO_DEVICE); 61662306a36Sopenharmony_ci if (dma_mapping_error(pdata->dev, skb_dma)) { 61762306a36Sopenharmony_ci netdev_alert(pdata->netdev, 61862306a36Sopenharmony_ci "skb_frag_dma_map failed\n"); 61962306a36Sopenharmony_ci goto err_out; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci rdata->skb_dma = skb_dma; 62262306a36Sopenharmony_ci rdata->skb_dma_len = len; 62362306a36Sopenharmony_ci rdata->mapped_as_page = 1; 62462306a36Sopenharmony_ci netif_dbg(pdata, tx_queued, pdata->netdev, 62562306a36Sopenharmony_ci "skb frag: index=%u, dma=%pad, len=%u\n", 62662306a36Sopenharmony_ci cur_index, &skb_dma, len); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci datalen -= len; 62962306a36Sopenharmony_ci offset += len; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci packet->length += len; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci cur_index++; 63462306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, cur_index); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Save the skb address in the last entry. We always have some data 63962306a36Sopenharmony_ci * that has been mapped so rdata is always advanced past the last 64062306a36Sopenharmony_ci * piece of mapped data - use the entry pointed to by cur_index - 1. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, cur_index - 1); 64362306a36Sopenharmony_ci rdata->skb = skb; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Save the number of descriptor entries used */ 64662306a36Sopenharmony_ci packet->rdesc_count = cur_index - start_index; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci DBGPR("<--xgbe_map_tx_skb: count=%u\n", packet->rdesc_count); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return packet->rdesc_count; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cierr_out: 65362306a36Sopenharmony_ci while (start_index < cur_index) { 65462306a36Sopenharmony_ci rdata = XGBE_GET_DESC_DATA(ring, start_index++); 65562306a36Sopenharmony_ci xgbe_unmap_rdata(pdata, rdata); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci DBGPR("<--xgbe_map_tx_skb: count=0\n"); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_civoid xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci DBGPR("-->xgbe_init_function_ptrs_desc\n"); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci desc_if->alloc_ring_resources = xgbe_alloc_ring_resources; 66862306a36Sopenharmony_ci desc_if->free_ring_resources = xgbe_free_ring_resources; 66962306a36Sopenharmony_ci desc_if->map_tx_skb = xgbe_map_tx_skb; 67062306a36Sopenharmony_ci desc_if->map_rx_buffer = xgbe_map_rx_buffer; 67162306a36Sopenharmony_ci desc_if->unmap_rdata = xgbe_unmap_rdata; 67262306a36Sopenharmony_ci desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init; 67362306a36Sopenharmony_ci desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci DBGPR("<--xgbe_init_function_ptrs_desc\n"); 67662306a36Sopenharmony_ci} 677