18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Lantiq / Intel PMAC driver for XRX200 SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Lantiq Deutschland 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 John Crispin <john@phrozen.org> 78c2ecf20Sopenharmony_ci * Copyright (C) 2017 - 2018 Hauke Mehrtens <hauke@hauke-m.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/of_net.h> 188c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <xway_dma.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* DMA */ 238c2ecf20Sopenharmony_ci#define XRX200_DMA_DATA_LEN 0x600 248c2ecf20Sopenharmony_ci#define XRX200_DMA_RX 0 258c2ecf20Sopenharmony_ci#define XRX200_DMA_TX 1 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* cpu port mac */ 288c2ecf20Sopenharmony_ci#define PMAC_RX_IPG 0x0024 298c2ecf20Sopenharmony_ci#define PMAC_RX_IPG_MASK 0xf 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PMAC_HD_CTL 0x0000 328c2ecf20Sopenharmony_ci/* Add Ethernet header to packets from DMA to PMAC */ 338c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_ADD BIT(0) 348c2ecf20Sopenharmony_ci/* Add VLAN tag to Packets from DMA to PMAC */ 358c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_TAG BIT(1) 368c2ecf20Sopenharmony_ci/* Add CRC to packets from DMA to PMAC */ 378c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_AC BIT(2) 388c2ecf20Sopenharmony_ci/* Add status header to packets from PMAC to DMA */ 398c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_AS BIT(3) 408c2ecf20Sopenharmony_ci/* Remove CRC from packets from PMAC to DMA */ 418c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_RC BIT(4) 428c2ecf20Sopenharmony_ci/* Remove Layer-2 header from packets from PMAC to DMA */ 438c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_RL2 BIT(5) 448c2ecf20Sopenharmony_ci/* Status header is present from DMA to PMAC */ 458c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_RXSH BIT(6) 468c2ecf20Sopenharmony_ci/* Add special tag from PMAC to switch */ 478c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_AST BIT(7) 488c2ecf20Sopenharmony_ci/* Remove specail Tag from PMAC to DMA */ 498c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_RST BIT(8) 508c2ecf20Sopenharmony_ci/* Check CRC from DMA to PMAC */ 518c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_CCRC BIT(9) 528c2ecf20Sopenharmony_ci/* Enable reaction to Pause frames in the PMAC */ 538c2ecf20Sopenharmony_ci#define PMAC_HD_CTL_FC BIT(10) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct xrx200_chan { 568c2ecf20Sopenharmony_ci int tx_free; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci struct napi_struct napi; 598c2ecf20Sopenharmony_ci struct ltq_dma_channel dma; 608c2ecf20Sopenharmony_ci struct sk_buff *skb[LTQ_DESC_NUM]; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci struct xrx200_priv *priv; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct xrx200_priv { 668c2ecf20Sopenharmony_ci struct clk *clk; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci struct xrx200_chan chan_tx; 698c2ecf20Sopenharmony_ci struct xrx200_chan chan_rx; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci struct net_device *net_dev; 728c2ecf20Sopenharmony_ci struct device *dev; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci __iomem void *pmac_reg; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic u32 xrx200_pmac_r32(struct xrx200_priv *priv, u32 offset) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return __raw_readl(priv->pmac_reg + offset); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void xrx200_pmac_w32(struct xrx200_priv *priv, u32 val, u32 offset) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci __raw_writel(val, priv->pmac_reg + offset); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void xrx200_pmac_mask(struct xrx200_priv *priv, u32 clear, u32 set, 888c2ecf20Sopenharmony_ci u32 offset) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci u32 val = xrx200_pmac_r32(priv, offset); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci val &= ~(clear); 938c2ecf20Sopenharmony_ci val |= set; 948c2ecf20Sopenharmony_ci xrx200_pmac_w32(priv, val, offset); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* drop all the packets from the DMA ring */ 988c2ecf20Sopenharmony_cistatic void xrx200_flush_dma(struct xrx200_chan *ch) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int i; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (i = 0; i < LTQ_DESC_NUM; i++) { 1038c2ecf20Sopenharmony_ci struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) != LTQ_DMA_C) 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci desc->ctl = LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | 1098c2ecf20Sopenharmony_ci XRX200_DMA_DATA_LEN; 1108c2ecf20Sopenharmony_ci ch->dma.desc++; 1118c2ecf20Sopenharmony_ci ch->dma.desc %= LTQ_DESC_NUM; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int xrx200_open(struct net_device *net_dev) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct xrx200_priv *priv = netdev_priv(net_dev); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci napi_enable(&priv->chan_tx.napi); 1208c2ecf20Sopenharmony_ci ltq_dma_open(&priv->chan_tx.dma); 1218c2ecf20Sopenharmony_ci ltq_dma_enable_irq(&priv->chan_tx.dma); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci napi_enable(&priv->chan_rx.napi); 1248c2ecf20Sopenharmony_ci ltq_dma_open(&priv->chan_rx.dma); 1258c2ecf20Sopenharmony_ci /* The boot loader does not always deactivate the receiving of frames 1268c2ecf20Sopenharmony_ci * on the ports and then some packets queue up in the PPE buffers. 1278c2ecf20Sopenharmony_ci * They already passed the PMAC so they do not have the tags 1288c2ecf20Sopenharmony_ci * configured here. Read the these packets here and drop them. 1298c2ecf20Sopenharmony_ci * The HW should have written them into memory after 10us 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci usleep_range(20, 40); 1328c2ecf20Sopenharmony_ci xrx200_flush_dma(&priv->chan_rx); 1338c2ecf20Sopenharmony_ci ltq_dma_enable_irq(&priv->chan_rx.dma); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci netif_wake_queue(net_dev); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int xrx200_close(struct net_device *net_dev) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct xrx200_priv *priv = netdev_priv(net_dev); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci netif_stop_queue(net_dev); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci napi_disable(&priv->chan_rx.napi); 1478c2ecf20Sopenharmony_ci ltq_dma_close(&priv->chan_rx.dma); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci napi_disable(&priv->chan_tx.napi); 1508c2ecf20Sopenharmony_ci ltq_dma_close(&priv->chan_tx.dma); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int xrx200_alloc_skb(struct xrx200_chan *ch) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct sk_buff *skb = ch->skb[ch->dma.desc]; 1588c2ecf20Sopenharmony_ci dma_addr_t mapping; 1598c2ecf20Sopenharmony_ci int ret = 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ch->skb[ch->dma.desc] = netdev_alloc_skb_ip_align(ch->priv->net_dev, 1628c2ecf20Sopenharmony_ci XRX200_DMA_DATA_LEN); 1638c2ecf20Sopenharmony_ci if (!ch->skb[ch->dma.desc]) { 1648c2ecf20Sopenharmony_ci ret = -ENOMEM; 1658c2ecf20Sopenharmony_ci goto skip; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci mapping = dma_map_single(ch->priv->dev, ch->skb[ch->dma.desc]->data, 1698c2ecf20Sopenharmony_ci XRX200_DMA_DATA_LEN, DMA_FROM_DEVICE); 1708c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(ch->priv->dev, mapping))) { 1718c2ecf20Sopenharmony_ci dev_kfree_skb_any(ch->skb[ch->dma.desc]); 1728c2ecf20Sopenharmony_ci ch->skb[ch->dma.desc] = skb; 1738c2ecf20Sopenharmony_ci ret = -ENOMEM; 1748c2ecf20Sopenharmony_ci goto skip; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ch->dma.desc_base[ch->dma.desc].addr = mapping; 1788c2ecf20Sopenharmony_ci /* Make sure the address is written before we give it to HW */ 1798c2ecf20Sopenharmony_ci wmb(); 1808c2ecf20Sopenharmony_ciskip: 1818c2ecf20Sopenharmony_ci ch->dma.desc_base[ch->dma.desc].ctl = 1828c2ecf20Sopenharmony_ci LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | 1838c2ecf20Sopenharmony_ci XRX200_DMA_DATA_LEN; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int xrx200_hw_receive(struct xrx200_chan *ch) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct xrx200_priv *priv = ch->priv; 1918c2ecf20Sopenharmony_ci struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; 1928c2ecf20Sopenharmony_ci struct sk_buff *skb = ch->skb[ch->dma.desc]; 1938c2ecf20Sopenharmony_ci int len = (desc->ctl & LTQ_DMA_SIZE_MASK); 1948c2ecf20Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = xrx200_alloc_skb(ch); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ch->dma.desc++; 2008c2ecf20Sopenharmony_ci ch->dma.desc %= LTQ_DESC_NUM; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (ret) { 2038c2ecf20Sopenharmony_ci net_dev->stats.rx_dropped++; 2048c2ecf20Sopenharmony_ci netdev_err(net_dev, "failed to allocate new rx buffer\n"); 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci skb_put(skb, len); 2098c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, net_dev); 2108c2ecf20Sopenharmony_ci netif_receive_skb(skb); 2118c2ecf20Sopenharmony_ci net_dev->stats.rx_packets++; 2128c2ecf20Sopenharmony_ci net_dev->stats.rx_bytes += len; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int xrx200_poll_rx(struct napi_struct *napi, int budget) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct xrx200_chan *ch = container_of(napi, 2208c2ecf20Sopenharmony_ci struct xrx200_chan, napi); 2218c2ecf20Sopenharmony_ci int rx = 0; 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci while (rx < budget) { 2258c2ecf20Sopenharmony_ci struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { 2288c2ecf20Sopenharmony_ci ret = xrx200_hw_receive(ch); 2298c2ecf20Sopenharmony_ci if (ret) 2308c2ecf20Sopenharmony_ci return ret; 2318c2ecf20Sopenharmony_ci rx++; 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (rx < budget) { 2388c2ecf20Sopenharmony_ci if (napi_complete_done(&ch->napi, rx)) 2398c2ecf20Sopenharmony_ci ltq_dma_enable_irq(&ch->dma); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return rx; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int xrx200_tx_housekeeping(struct napi_struct *napi, int budget) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct xrx200_chan *ch = container_of(napi, 2488c2ecf20Sopenharmony_ci struct xrx200_chan, napi); 2498c2ecf20Sopenharmony_ci struct net_device *net_dev = ch->priv->net_dev; 2508c2ecf20Sopenharmony_ci int pkts = 0; 2518c2ecf20Sopenharmony_ci int bytes = 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci netif_tx_lock(net_dev); 2548c2ecf20Sopenharmony_ci while (pkts < budget) { 2558c2ecf20Sopenharmony_ci struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->tx_free]; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { 2588c2ecf20Sopenharmony_ci struct sk_buff *skb = ch->skb[ch->tx_free]; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci pkts++; 2618c2ecf20Sopenharmony_ci bytes += skb->len; 2628c2ecf20Sopenharmony_ci ch->skb[ch->tx_free] = NULL; 2638c2ecf20Sopenharmony_ci consume_skb(skb); 2648c2ecf20Sopenharmony_ci memset(&ch->dma.desc_base[ch->tx_free], 0, 2658c2ecf20Sopenharmony_ci sizeof(struct ltq_dma_desc)); 2668c2ecf20Sopenharmony_ci ch->tx_free++; 2678c2ecf20Sopenharmony_ci ch->tx_free %= LTQ_DESC_NUM; 2688c2ecf20Sopenharmony_ci } else { 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci net_dev->stats.tx_packets += pkts; 2748c2ecf20Sopenharmony_ci net_dev->stats.tx_bytes += bytes; 2758c2ecf20Sopenharmony_ci netdev_completed_queue(ch->priv->net_dev, pkts, bytes); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci netif_tx_unlock(net_dev); 2788c2ecf20Sopenharmony_ci if (netif_queue_stopped(net_dev)) 2798c2ecf20Sopenharmony_ci netif_wake_queue(net_dev); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (pkts < budget) { 2828c2ecf20Sopenharmony_ci if (napi_complete_done(&ch->napi, pkts)) 2838c2ecf20Sopenharmony_ci ltq_dma_enable_irq(&ch->dma); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return pkts; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic netdev_tx_t xrx200_start_xmit(struct sk_buff *skb, 2908c2ecf20Sopenharmony_ci struct net_device *net_dev) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct xrx200_priv *priv = netdev_priv(net_dev); 2938c2ecf20Sopenharmony_ci struct xrx200_chan *ch = &priv->chan_tx; 2948c2ecf20Sopenharmony_ci struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; 2958c2ecf20Sopenharmony_ci u32 byte_offset; 2968c2ecf20Sopenharmony_ci dma_addr_t mapping; 2978c2ecf20Sopenharmony_ci int len; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci skb->dev = net_dev; 3008c2ecf20Sopenharmony_ci if (skb_put_padto(skb, ETH_ZLEN)) { 3018c2ecf20Sopenharmony_ci net_dev->stats.tx_dropped++; 3028c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci len = skb->len; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) { 3088c2ecf20Sopenharmony_ci netdev_err(net_dev, "tx ring full\n"); 3098c2ecf20Sopenharmony_ci netif_stop_queue(net_dev); 3108c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ch->skb[ch->dma.desc] = skb; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci mapping = dma_map_single(priv->dev, skb->data, len, DMA_TO_DEVICE); 3168c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(priv->dev, mapping))) 3178c2ecf20Sopenharmony_ci goto err_drop; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* dma needs to start on a 16 byte aligned address */ 3208c2ecf20Sopenharmony_ci byte_offset = mapping % 16; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci desc->addr = mapping - byte_offset; 3238c2ecf20Sopenharmony_ci /* Make sure the address is written before we give it to HW */ 3248c2ecf20Sopenharmony_ci wmb(); 3258c2ecf20Sopenharmony_ci desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP | 3268c2ecf20Sopenharmony_ci LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK); 3278c2ecf20Sopenharmony_ci ch->dma.desc++; 3288c2ecf20Sopenharmony_ci ch->dma.desc %= LTQ_DESC_NUM; 3298c2ecf20Sopenharmony_ci if (ch->dma.desc == ch->tx_free) 3308c2ecf20Sopenharmony_ci netif_stop_queue(net_dev); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci netdev_sent_queue(net_dev, len); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cierr_drop: 3378c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3388c2ecf20Sopenharmony_ci net_dev->stats.tx_dropped++; 3398c2ecf20Sopenharmony_ci net_dev->stats.tx_errors++; 3408c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic const struct net_device_ops xrx200_netdev_ops = { 3448c2ecf20Sopenharmony_ci .ndo_open = xrx200_open, 3458c2ecf20Sopenharmony_ci .ndo_stop = xrx200_close, 3468c2ecf20Sopenharmony_ci .ndo_start_xmit = xrx200_start_xmit, 3478c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 3488c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic irqreturn_t xrx200_dma_irq(int irq, void *ptr) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct xrx200_chan *ch = ptr; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (napi_schedule_prep(&ch->napi)) { 3568c2ecf20Sopenharmony_ci ltq_dma_disable_irq(&ch->dma); 3578c2ecf20Sopenharmony_ci __napi_schedule(&ch->napi); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ltq_dma_ack_irq(&ch->dma); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int xrx200_dma_init(struct xrx200_priv *priv) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct xrx200_chan *ch_rx = &priv->chan_rx; 3688c2ecf20Sopenharmony_ci struct xrx200_chan *ch_tx = &priv->chan_tx; 3698c2ecf20Sopenharmony_ci int ret = 0; 3708c2ecf20Sopenharmony_ci int i; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ltq_dma_init_port(DMA_PORT_ETOP); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ch_rx->dma.nr = XRX200_DMA_RX; 3758c2ecf20Sopenharmony_ci ch_rx->dma.dev = priv->dev; 3768c2ecf20Sopenharmony_ci ch_rx->priv = priv; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ltq_dma_alloc_rx(&ch_rx->dma); 3798c2ecf20Sopenharmony_ci for (ch_rx->dma.desc = 0; ch_rx->dma.desc < LTQ_DESC_NUM; 3808c2ecf20Sopenharmony_ci ch_rx->dma.desc++) { 3818c2ecf20Sopenharmony_ci ret = xrx200_alloc_skb(ch_rx); 3828c2ecf20Sopenharmony_ci if (ret) 3838c2ecf20Sopenharmony_ci goto rx_free; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci ch_rx->dma.desc = 0; 3868c2ecf20Sopenharmony_ci ret = devm_request_irq(priv->dev, ch_rx->dma.irq, xrx200_dma_irq, 0, 3878c2ecf20Sopenharmony_ci "xrx200_net_rx", &priv->chan_rx); 3888c2ecf20Sopenharmony_ci if (ret) { 3898c2ecf20Sopenharmony_ci dev_err(priv->dev, "failed to request RX irq %d\n", 3908c2ecf20Sopenharmony_ci ch_rx->dma.irq); 3918c2ecf20Sopenharmony_ci goto rx_ring_free; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci ch_tx->dma.nr = XRX200_DMA_TX; 3958c2ecf20Sopenharmony_ci ch_tx->dma.dev = priv->dev; 3968c2ecf20Sopenharmony_ci ch_tx->priv = priv; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ltq_dma_alloc_tx(&ch_tx->dma); 3998c2ecf20Sopenharmony_ci ret = devm_request_irq(priv->dev, ch_tx->dma.irq, xrx200_dma_irq, 0, 4008c2ecf20Sopenharmony_ci "xrx200_net_tx", &priv->chan_tx); 4018c2ecf20Sopenharmony_ci if (ret) { 4028c2ecf20Sopenharmony_ci dev_err(priv->dev, "failed to request TX irq %d\n", 4038c2ecf20Sopenharmony_ci ch_tx->dma.irq); 4048c2ecf20Sopenharmony_ci goto tx_free; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_citx_free: 4108c2ecf20Sopenharmony_ci ltq_dma_free(&ch_tx->dma); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cirx_ring_free: 4138c2ecf20Sopenharmony_ci /* free the allocated RX ring */ 4148c2ecf20Sopenharmony_ci for (i = 0; i < LTQ_DESC_NUM; i++) { 4158c2ecf20Sopenharmony_ci if (priv->chan_rx.skb[i]) 4168c2ecf20Sopenharmony_ci dev_kfree_skb_any(priv->chan_rx.skb[i]); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cirx_free: 4208c2ecf20Sopenharmony_ci ltq_dma_free(&ch_rx->dma); 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic void xrx200_hw_cleanup(struct xrx200_priv *priv) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci int i; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ltq_dma_free(&priv->chan_tx.dma); 4298c2ecf20Sopenharmony_ci ltq_dma_free(&priv->chan_rx.dma); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* free the allocated RX ring */ 4328c2ecf20Sopenharmony_ci for (i = 0; i < LTQ_DESC_NUM; i++) 4338c2ecf20Sopenharmony_ci dev_kfree_skb_any(priv->chan_rx.skb[i]); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int xrx200_probe(struct platform_device *pdev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4398c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4408c2ecf20Sopenharmony_ci struct resource *res; 4418c2ecf20Sopenharmony_ci struct xrx200_priv *priv; 4428c2ecf20Sopenharmony_ci struct net_device *net_dev; 4438c2ecf20Sopenharmony_ci const u8 *mac; 4448c2ecf20Sopenharmony_ci int err; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* alloc the network device */ 4478c2ecf20Sopenharmony_ci net_dev = devm_alloc_etherdev(dev, sizeof(struct xrx200_priv)); 4488c2ecf20Sopenharmony_ci if (!net_dev) 4498c2ecf20Sopenharmony_ci return -ENOMEM; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci priv = netdev_priv(net_dev); 4528c2ecf20Sopenharmony_ci priv->net_dev = net_dev; 4538c2ecf20Sopenharmony_ci priv->dev = dev; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci net_dev->netdev_ops = &xrx200_netdev_ops; 4568c2ecf20Sopenharmony_ci SET_NETDEV_DEV(net_dev, dev); 4578c2ecf20Sopenharmony_ci net_dev->min_mtu = ETH_ZLEN; 4588c2ecf20Sopenharmony_ci net_dev->max_mtu = XRX200_DMA_DATA_LEN; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* load the memory ranges */ 4618c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4628c2ecf20Sopenharmony_ci if (!res) { 4638c2ecf20Sopenharmony_ci dev_err(dev, "failed to get resources\n"); 4648c2ecf20Sopenharmony_ci return -ENOENT; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci priv->pmac_reg = devm_ioremap_resource(dev, res); 4688c2ecf20Sopenharmony_ci if (IS_ERR(priv->pmac_reg)) { 4698c2ecf20Sopenharmony_ci dev_err(dev, "failed to request and remap io ranges\n"); 4708c2ecf20Sopenharmony_ci return PTR_ERR(priv->pmac_reg); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci priv->chan_rx.dma.irq = platform_get_irq_byname(pdev, "rx"); 4748c2ecf20Sopenharmony_ci if (priv->chan_rx.dma.irq < 0) 4758c2ecf20Sopenharmony_ci return -ENOENT; 4768c2ecf20Sopenharmony_ci priv->chan_tx.dma.irq = platform_get_irq_byname(pdev, "tx"); 4778c2ecf20Sopenharmony_ci if (priv->chan_tx.dma.irq < 0) 4788c2ecf20Sopenharmony_ci return -ENOENT; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* get the clock */ 4818c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(dev, NULL); 4828c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 4838c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock\n"); 4848c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci mac = of_get_mac_address(np); 4888c2ecf20Sopenharmony_ci if (!IS_ERR(mac)) 4898c2ecf20Sopenharmony_ci ether_addr_copy(net_dev->dev_addr, mac); 4908c2ecf20Sopenharmony_ci else 4918c2ecf20Sopenharmony_ci eth_hw_addr_random(net_dev); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* bring up the dma engine and IP core */ 4948c2ecf20Sopenharmony_ci err = xrx200_dma_init(priv); 4958c2ecf20Sopenharmony_ci if (err) 4968c2ecf20Sopenharmony_ci return err; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* enable clock gate */ 4998c2ecf20Sopenharmony_ci err = clk_prepare_enable(priv->clk); 5008c2ecf20Sopenharmony_ci if (err) 5018c2ecf20Sopenharmony_ci goto err_uninit_dma; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* set IPG to 12 */ 5048c2ecf20Sopenharmony_ci xrx200_pmac_mask(priv, PMAC_RX_IPG_MASK, 0xb, PMAC_RX_IPG); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* enable status header, enable CRC */ 5078c2ecf20Sopenharmony_ci xrx200_pmac_mask(priv, 0, 5088c2ecf20Sopenharmony_ci PMAC_HD_CTL_RST | PMAC_HD_CTL_AST | PMAC_HD_CTL_RXSH | 5098c2ecf20Sopenharmony_ci PMAC_HD_CTL_AS | PMAC_HD_CTL_AC | PMAC_HD_CTL_RC, 5108c2ecf20Sopenharmony_ci PMAC_HD_CTL); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* setup NAPI */ 5138c2ecf20Sopenharmony_ci netif_napi_add(net_dev, &priv->chan_rx.napi, xrx200_poll_rx, 32); 5148c2ecf20Sopenharmony_ci netif_tx_napi_add(net_dev, &priv->chan_tx.napi, xrx200_tx_housekeeping, 32); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci err = register_netdev(net_dev); 5198c2ecf20Sopenharmony_ci if (err) 5208c2ecf20Sopenharmony_ci goto err_unprepare_clk; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cierr_unprepare_clk: 5258c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cierr_uninit_dma: 5288c2ecf20Sopenharmony_ci xrx200_hw_cleanup(priv); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return err; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int xrx200_remove(struct platform_device *pdev) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct xrx200_priv *priv = platform_get_drvdata(pdev); 5368c2ecf20Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* free stack related instances */ 5398c2ecf20Sopenharmony_ci netif_stop_queue(net_dev); 5408c2ecf20Sopenharmony_ci netif_napi_del(&priv->chan_tx.napi); 5418c2ecf20Sopenharmony_ci netif_napi_del(&priv->chan_rx.napi); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* remove the actual device */ 5448c2ecf20Sopenharmony_ci unregister_netdev(net_dev); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* release the clock */ 5478c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* shut down hardware */ 5508c2ecf20Sopenharmony_ci xrx200_hw_cleanup(priv); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic const struct of_device_id xrx200_match[] = { 5568c2ecf20Sopenharmony_ci { .compatible = "lantiq,xrx200-net" }, 5578c2ecf20Sopenharmony_ci {}, 5588c2ecf20Sopenharmony_ci}; 5598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xrx200_match); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic struct platform_driver xrx200_driver = { 5628c2ecf20Sopenharmony_ci .probe = xrx200_probe, 5638c2ecf20Sopenharmony_ci .remove = xrx200_remove, 5648c2ecf20Sopenharmony_ci .driver = { 5658c2ecf20Sopenharmony_ci .name = "lantiq,xrx200-net", 5668c2ecf20Sopenharmony_ci .of_match_table = xrx200_match, 5678c2ecf20Sopenharmony_ci }, 5688c2ecf20Sopenharmony_ci}; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cimodule_platform_driver(xrx200_driver); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ciMODULE_AUTHOR("John Crispin <john@phrozen.org>"); 5738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Lantiq SoC XRX200 ethernet"); 5748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 575