123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- /* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver
- *
- * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com)
- *
- * This program is dual-licensed; you may select either version 2 of
- * the GNU General Public License ("GPL") or BSD license ("BSD").
- *
- * This Synopsys DWC XLGMAC software driver and associated documentation
- * (hereinafter the "Software") is an unsupported proprietary work of
- * Synopsys, Inc. unless otherwise expressly agreed to in writing between
- * Synopsys and you. The Software IS NOT an item of Licensed Software or a
- * Licensed Product under any End User Software License Agreement or
- * Agreement for Licensed Products with Synopsys or any supplement thereto.
- * Synopsys is a registered trademark of Synopsys, Inc. Other names included
- * in the SOFTWARE may be the trademarks of their respective owners.
- */
- #include "dwc-xlgmac.h"
- #include "dwc-xlgmac-reg.h"
- static void xlgmac_unmap_desc_data(struct xlgmac_pdata *pdata,
- struct xlgmac_desc_data *desc_data)
- {
- if (desc_data->skb_dma) {
- if (desc_data->mapped_as_page) {
- dma_unmap_page(pdata->dev, desc_data->skb_dma,
- desc_data->skb_dma_len, DMA_TO_DEVICE);
- } else {
- dma_unmap_single(pdata->dev, desc_data->skb_dma,
- desc_data->skb_dma_len, DMA_TO_DEVICE);
- }
- desc_data->skb_dma = 0;
- desc_data->skb_dma_len = 0;
- }
- if (desc_data->skb) {
- dev_kfree_skb_any(desc_data->skb);
- desc_data->skb = NULL;
- }
- if (desc_data->rx.hdr.pa.pages)
- put_page(desc_data->rx.hdr.pa.pages);
- if (desc_data->rx.hdr.pa_unmap.pages) {
- dma_unmap_page(pdata->dev, desc_data->rx.hdr.pa_unmap.pages_dma,
- desc_data->rx.hdr.pa_unmap.pages_len,
- DMA_FROM_DEVICE);
- put_page(desc_data->rx.hdr.pa_unmap.pages);
- }
- if (desc_data->rx.buf.pa.pages)
- put_page(desc_data->rx.buf.pa.pages);
- if (desc_data->rx.buf.pa_unmap.pages) {
- dma_unmap_page(pdata->dev, desc_data->rx.buf.pa_unmap.pages_dma,
- desc_data->rx.buf.pa_unmap.pages_len,
- DMA_FROM_DEVICE);
- put_page(desc_data->rx.buf.pa_unmap.pages);
- }
- memset(&desc_data->tx, 0, sizeof(desc_data->tx));
- memset(&desc_data->rx, 0, sizeof(desc_data->rx));
- desc_data->mapped_as_page = 0;
- if (desc_data->state_saved) {
- desc_data->state_saved = 0;
- desc_data->state.skb = NULL;
- desc_data->state.len = 0;
- desc_data->state.error = 0;
- }
- }
- static void xlgmac_free_ring(struct xlgmac_pdata *pdata,
- struct xlgmac_ring *ring)
- {
- struct xlgmac_desc_data *desc_data;
- unsigned int i;
- if (!ring)
- return;
- if (ring->desc_data_head) {
- for (i = 0; i < ring->dma_desc_count; i++) {
- desc_data = XLGMAC_GET_DESC_DATA(ring, i);
- xlgmac_unmap_desc_data(pdata, desc_data);
- }
- kfree(ring->desc_data_head);
- ring->desc_data_head = NULL;
- }
- if (ring->rx_hdr_pa.pages) {
- dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma,
- ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE);
- put_page(ring->rx_hdr_pa.pages);
- ring->rx_hdr_pa.pages = NULL;
- ring->rx_hdr_pa.pages_len = 0;
- ring->rx_hdr_pa.pages_offset = 0;
- ring->rx_hdr_pa.pages_dma = 0;
- }
- if (ring->rx_buf_pa.pages) {
- dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma,
- ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE);
- put_page(ring->rx_buf_pa.pages);
- ring->rx_buf_pa.pages = NULL;
- ring->rx_buf_pa.pages_len = 0;
- ring->rx_buf_pa.pages_offset = 0;
- ring->rx_buf_pa.pages_dma = 0;
- }
- if (ring->dma_desc_head) {
- dma_free_coherent(pdata->dev,
- (sizeof(struct xlgmac_dma_desc) *
- ring->dma_desc_count),
- ring->dma_desc_head,
- ring->dma_desc_head_addr);
- ring->dma_desc_head = NULL;
- }
- }
- static int xlgmac_init_ring(struct xlgmac_pdata *pdata,
- struct xlgmac_ring *ring,
- unsigned int dma_desc_count)
- {
- if (!ring)
- return 0;
- /* Descriptors */
- ring->dma_desc_count = dma_desc_count;
- ring->dma_desc_head = dma_alloc_coherent(pdata->dev,
- (sizeof(struct xlgmac_dma_desc) *
- dma_desc_count),
- &ring->dma_desc_head_addr,
- GFP_KERNEL);
- if (!ring->dma_desc_head)
- return -ENOMEM;
- /* Array of descriptor data */
- ring->desc_data_head = kcalloc(dma_desc_count,
- sizeof(struct xlgmac_desc_data),
- GFP_KERNEL);
- if (!ring->desc_data_head)
- return -ENOMEM;
- netif_dbg(pdata, drv, pdata->netdev,
- "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n",
- ring->dma_desc_head,
- &ring->dma_desc_head_addr,
- ring->desc_data_head);
- return 0;
- }
- static void xlgmac_free_rings(struct xlgmac_pdata *pdata)
- {
- struct xlgmac_channel *channel;
- unsigned int i;
- if (!pdata->channel_head)
- return;
- channel = pdata->channel_head;
- for (i = 0; i < pdata->channel_count; i++, channel++) {
- xlgmac_free_ring(pdata, channel->tx_ring);
- xlgmac_free_ring(pdata, channel->rx_ring);
- }
- }
- static int xlgmac_alloc_rings(struct xlgmac_pdata *pdata)
- {
- struct xlgmac_channel *channel;
- unsigned int i;
- int ret;
- channel = pdata->channel_head;
- for (i = 0; i < pdata->channel_count; i++, channel++) {
- netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n",
- channel->name);
- ret = xlgmac_init_ring(pdata, channel->tx_ring,
- pdata->tx_desc_count);
- if (ret) {
- netdev_alert(pdata->netdev,
- "error initializing Tx ring");
- goto err_init_ring;
- }
- netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n",
- channel->name);
- ret = xlgmac_init_ring(pdata, channel->rx_ring,
- pdata->rx_desc_count);
- if (ret) {
- netdev_alert(pdata->netdev,
- "error initializing Rx ring\n");
- goto err_init_ring;
- }
- }
- return 0;
- err_init_ring:
- xlgmac_free_rings(pdata);
- return ret;
- }
- static void xlgmac_free_channels(struct xlgmac_pdata *pdata)
- {
- if (!pdata->channel_head)
- return;
- kfree(pdata->channel_head->tx_ring);
- pdata->channel_head->tx_ring = NULL;
- kfree(pdata->channel_head->rx_ring);
- pdata->channel_head->rx_ring = NULL;
- kfree(pdata->channel_head);
- pdata->channel_head = NULL;
- pdata->channel_count = 0;
- }
- static int xlgmac_alloc_channels(struct xlgmac_pdata *pdata)
- {
- struct xlgmac_channel *channel_head, *channel;
- struct xlgmac_ring *tx_ring, *rx_ring;
- int ret = -ENOMEM;
- unsigned int i;
- channel_head = kcalloc(pdata->channel_count,
- sizeof(struct xlgmac_channel), GFP_KERNEL);
- if (!channel_head)
- return ret;
- netif_dbg(pdata, drv, pdata->netdev,
- "channel_head=%p\n", channel_head);
- tx_ring = kcalloc(pdata->tx_ring_count, sizeof(struct xlgmac_ring),
- GFP_KERNEL);
- if (!tx_ring)
- goto err_tx_ring;
- rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct xlgmac_ring),
- GFP_KERNEL);
- if (!rx_ring)
- goto err_rx_ring;
- for (i = 0, channel = channel_head; i < pdata->channel_count;
- i++, channel++) {
- snprintf(channel->name, sizeof(channel->name), "channel-%u", i);
- channel->pdata = pdata;
- channel->queue_index = i;
- channel->dma_regs = pdata->mac_regs + DMA_CH_BASE +
- (DMA_CH_INC * i);
- if (pdata->per_channel_irq) {
- /* Get the per DMA interrupt */
- ret = pdata->channel_irq[i];
- if (ret < 0) {
- netdev_err(pdata->netdev,
- "get_irq %u failed\n",
- i + 1);
- goto err_irq;
- }
- channel->dma_irq = ret;
- }
- if (i < pdata->tx_ring_count)
- channel->tx_ring = tx_ring++;
- if (i < pdata->rx_ring_count)
- channel->rx_ring = rx_ring++;
- netif_dbg(pdata, drv, pdata->netdev,
- "%s: dma_regs=%p, tx_ring=%p, rx_ring=%p\n",
- channel->name, channel->dma_regs,
- channel->tx_ring, channel->rx_ring);
- }
- pdata->channel_head = channel_head;
- return 0;
- err_irq:
- kfree(rx_ring);
- err_rx_ring:
- kfree(tx_ring);
- err_tx_ring:
- kfree(channel_head);
- return ret;
- }
- static void xlgmac_free_channels_and_rings(struct xlgmac_pdata *pdata)
- {
- xlgmac_free_rings(pdata);
- xlgmac_free_channels(pdata);
- }
- static int xlgmac_alloc_channels_and_rings(struct xlgmac_pdata *pdata)
- {
- int ret;
- ret = xlgmac_alloc_channels(pdata);
- if (ret)
- goto err_alloc;
- ret = xlgmac_alloc_rings(pdata);
- if (ret)
- goto err_alloc;
- return 0;
- err_alloc:
- xlgmac_free_channels_and_rings(pdata);
- return ret;
- }
- static int xlgmac_alloc_pages(struct xlgmac_pdata *pdata,
- struct xlgmac_page_alloc *pa,
- gfp_t gfp, int order)
- {
- struct page *pages = NULL;
- dma_addr_t pages_dma;
- /* Try to obtain pages, decreasing order if necessary */
- gfp |= __GFP_COMP | __GFP_NOWARN;
- while (order >= 0) {
- pages = alloc_pages(gfp, order);
- if (pages)
- break;
- order--;
- }
- if (!pages)
- return -ENOMEM;
- /* Map the pages */
- pages_dma = dma_map_page(pdata->dev, pages, 0,
- PAGE_SIZE << order, DMA_FROM_DEVICE);
- if (dma_mapping_error(pdata->dev, pages_dma)) {
- put_page(pages);
- return -ENOMEM;
- }
- pa->pages = pages;
- pa->pages_len = PAGE_SIZE << order;
- pa->pages_offset = 0;
- pa->pages_dma = pages_dma;
- return 0;
- }
- static void xlgmac_set_buffer_data(struct xlgmac_buffer_data *bd,
- struct xlgmac_page_alloc *pa,
- unsigned int len)
- {
- get_page(pa->pages);
- bd->pa = *pa;
- bd->dma_base = pa->pages_dma;
- bd->dma_off = pa->pages_offset;
- bd->dma_len = len;
- pa->pages_offset += len;
- if ((pa->pages_offset + len) > pa->pages_len) {
- /* This data descriptor is responsible for unmapping page(s) */
- bd->pa_unmap = *pa;
- /* Get a new allocation next time */
- pa->pages = NULL;
- pa->pages_len = 0;
- pa->pages_offset = 0;
- pa->pages_dma = 0;
- }
- }
- static int xlgmac_map_rx_buffer(struct xlgmac_pdata *pdata,
- struct xlgmac_ring *ring,
- struct xlgmac_desc_data *desc_data)
- {
- int order, ret;
- if (!ring->rx_hdr_pa.pages) {
- ret = xlgmac_alloc_pages(pdata, &ring->rx_hdr_pa,
- GFP_ATOMIC, 0);
- if (ret)
- return ret;
- }
- if (!ring->rx_buf_pa.pages) {
- order = max_t(int, PAGE_ALLOC_COSTLY_ORDER - 1, 0);
- ret = xlgmac_alloc_pages(pdata, &ring->rx_buf_pa,
- GFP_ATOMIC, order);
- if (ret)
- return ret;
- }
- /* Set up the header page info */
- xlgmac_set_buffer_data(&desc_data->rx.hdr, &ring->rx_hdr_pa,
- XLGMAC_SKB_ALLOC_SIZE);
- /* Set up the buffer page info */
- xlgmac_set_buffer_data(&desc_data->rx.buf, &ring->rx_buf_pa,
- pdata->rx_buf_size);
- return 0;
- }
- static void xlgmac_tx_desc_init(struct xlgmac_pdata *pdata)
- {
- struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
- struct xlgmac_desc_data *desc_data;
- struct xlgmac_dma_desc *dma_desc;
- struct xlgmac_channel *channel;
- struct xlgmac_ring *ring;
- dma_addr_t dma_desc_addr;
- unsigned int i, j;
- channel = pdata->channel_head;
- for (i = 0; i < pdata->channel_count; i++, channel++) {
- ring = channel->tx_ring;
- if (!ring)
- break;
- dma_desc = ring->dma_desc_head;
- dma_desc_addr = ring->dma_desc_head_addr;
- for (j = 0; j < ring->dma_desc_count; j++) {
- desc_data = XLGMAC_GET_DESC_DATA(ring, j);
- desc_data->dma_desc = dma_desc;
- desc_data->dma_desc_addr = dma_desc_addr;
- dma_desc++;
- dma_desc_addr += sizeof(struct xlgmac_dma_desc);
- }
- ring->cur = 0;
- ring->dirty = 0;
- memset(&ring->tx, 0, sizeof(ring->tx));
- hw_ops->tx_desc_init(channel);
- }
- }
- static void xlgmac_rx_desc_init(struct xlgmac_pdata *pdata)
- {
- struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
- struct xlgmac_desc_data *desc_data;
- struct xlgmac_dma_desc *dma_desc;
- struct xlgmac_channel *channel;
- struct xlgmac_ring *ring;
- dma_addr_t dma_desc_addr;
- unsigned int i, j;
- channel = pdata->channel_head;
- for (i = 0; i < pdata->channel_count; i++, channel++) {
- ring = channel->rx_ring;
- if (!ring)
- break;
- dma_desc = ring->dma_desc_head;
- dma_desc_addr = ring->dma_desc_head_addr;
- for (j = 0; j < ring->dma_desc_count; j++) {
- desc_data = XLGMAC_GET_DESC_DATA(ring, j);
- desc_data->dma_desc = dma_desc;
- desc_data->dma_desc_addr = dma_desc_addr;
- if (xlgmac_map_rx_buffer(pdata, ring, desc_data))
- break;
- dma_desc++;
- dma_desc_addr += sizeof(struct xlgmac_dma_desc);
- }
- ring->cur = 0;
- ring->dirty = 0;
- hw_ops->rx_desc_init(channel);
- }
- }
- static int xlgmac_map_tx_skb(struct xlgmac_channel *channel,
- struct sk_buff *skb)
- {
- struct xlgmac_pdata *pdata = channel->pdata;
- struct xlgmac_ring *ring = channel->tx_ring;
- unsigned int start_index, cur_index;
- struct xlgmac_desc_data *desc_data;
- unsigned int offset, datalen, len;
- struct xlgmac_pkt_info *pkt_info;
- struct skb_frag_struct *frag;
- unsigned int tso, vlan;
- dma_addr_t skb_dma;
- unsigned int i;
- offset = 0;
- start_index = ring->cur;
- cur_index = ring->cur;
- pkt_info = &ring->pkt_info;
- pkt_info->desc_count = 0;
- pkt_info->length = 0;
- tso = XLGMAC_GET_REG_BITS(pkt_info->attributes,
- TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
- TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN);
- vlan = XLGMAC_GET_REG_BITS(pkt_info->attributes,
- TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
- TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN);
- /* Save space for a context descriptor if needed */
- if ((tso && (pkt_info->mss != ring->tx.cur_mss)) ||
- (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag)))
- cur_index++;
- desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
- if (tso) {
- /* Map the TSO header */
- skb_dma = dma_map_single(pdata->dev, skb->data,
- pkt_info->header_len, DMA_TO_DEVICE);
- if (dma_mapping_error(pdata->dev, skb_dma)) {
- netdev_alert(pdata->netdev, "dma_map_single failed\n");
- goto err_out;
- }
- desc_data->skb_dma = skb_dma;
- desc_data->skb_dma_len = pkt_info->header_len;
- netif_dbg(pdata, tx_queued, pdata->netdev,
- "skb header: index=%u, dma=%pad, len=%u\n",
- cur_index, &skb_dma, pkt_info->header_len);
- offset = pkt_info->header_len;
- pkt_info->length += pkt_info->header_len;
- cur_index++;
- desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
- }
- /* Map the (remainder of the) packet */
- for (datalen = skb_headlen(skb) - offset; datalen; ) {
- len = min_t(unsigned int, datalen, XLGMAC_TX_MAX_BUF_SIZE);
- skb_dma = dma_map_single(pdata->dev, skb->data + offset, len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(pdata->dev, skb_dma)) {
- netdev_alert(pdata->netdev, "dma_map_single failed\n");
- goto err_out;
- }
- desc_data->skb_dma = skb_dma;
- desc_data->skb_dma_len = len;
- netif_dbg(pdata, tx_queued, pdata->netdev,
- "skb data: index=%u, dma=%pad, len=%u\n",
- cur_index, &skb_dma, len);
- datalen -= len;
- offset += len;
- pkt_info->length += len;
- cur_index++;
- desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
- }
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- netif_dbg(pdata, tx_queued, pdata->netdev,
- "mapping frag %u\n", i);
- frag = &skb_shinfo(skb)->frags[i];
- offset = 0;
- for (datalen = skb_frag_size(frag); datalen; ) {
- len = min_t(unsigned int, datalen,
- XLGMAC_TX_MAX_BUF_SIZE);
- skb_dma = skb_frag_dma_map(pdata->dev, frag, offset,
- len, DMA_TO_DEVICE);
- if (dma_mapping_error(pdata->dev, skb_dma)) {
- netdev_alert(pdata->netdev,
- "skb_frag_dma_map failed\n");
- goto err_out;
- }
- desc_data->skb_dma = skb_dma;
- desc_data->skb_dma_len = len;
- desc_data->mapped_as_page = 1;
- netif_dbg(pdata, tx_queued, pdata->netdev,
- "skb frag: index=%u, dma=%pad, len=%u\n",
- cur_index, &skb_dma, len);
- datalen -= len;
- offset += len;
- pkt_info->length += len;
- cur_index++;
- desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index);
- }
- }
- /* Save the skb address in the last entry. We always have some data
- * that has been mapped so desc_data is always advanced past the last
- * piece of mapped data - use the entry pointed to by cur_index - 1.
- */
- desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index - 1);
- desc_data->skb = skb;
- /* Save the number of descriptor entries used */
- pkt_info->desc_count = cur_index - start_index;
- return pkt_info->desc_count;
- err_out:
- while (start_index < cur_index) {
- desc_data = XLGMAC_GET_DESC_DATA(ring, start_index++);
- xlgmac_unmap_desc_data(pdata, desc_data);
- }
- return 0;
- }
- void xlgmac_init_desc_ops(struct xlgmac_desc_ops *desc_ops)
- {
- desc_ops->alloc_channles_and_rings = xlgmac_alloc_channels_and_rings;
- desc_ops->free_channels_and_rings = xlgmac_free_channels_and_rings;
- desc_ops->map_tx_skb = xlgmac_map_tx_skb;
- desc_ops->map_rx_buffer = xlgmac_map_rx_buffer;
- desc_ops->unmap_desc_data = xlgmac_unmap_desc_data;
- desc_ops->tx_desc_init = xlgmac_tx_desc_init;
- desc_ops->rx_desc_init = xlgmac_rx_desc_init;
- }
|