18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for BCM963xx builtin Ethernet mac 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 158c2ecf20Sopenharmony_ci#include <linux/crc32.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <bcm63xx_dev_enet.h> 228c2ecf20Sopenharmony_ci#include "bcm63xx_enet.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic char bcm_enet_driver_name[] = "bcm63xx_enet"; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int copybreak __read_mostly = 128; 278c2ecf20Sopenharmony_cimodule_param(copybreak, int, 0); 288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(copybreak, "Receive copy threshold"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* io registers memory shared between all devices */ 318c2ecf20Sopenharmony_cistatic void __iomem *bcm_enet_shared_base[3]; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * io helpers to access mac registers 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic inline u32 enet_readl(struct bcm_enet_priv *priv, u32 off) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return bcm_readl(priv->base + off); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline void enet_writel(struct bcm_enet_priv *priv, 428c2ecf20Sopenharmony_ci u32 val, u32 off) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci bcm_writel(val, priv->base + off); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * io helpers to access switch registers 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic inline u32 enetsw_readl(struct bcm_enet_priv *priv, u32 off) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return bcm_readl(priv->base + off); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline void enetsw_writel(struct bcm_enet_priv *priv, 568c2ecf20Sopenharmony_ci u32 val, u32 off) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci bcm_writel(val, priv->base + off); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline u16 enetsw_readw(struct bcm_enet_priv *priv, u32 off) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return bcm_readw(priv->base + off); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic inline void enetsw_writew(struct bcm_enet_priv *priv, 678c2ecf20Sopenharmony_ci u16 val, u32 off) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci bcm_writew(val, priv->base + off); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline u8 enetsw_readb(struct bcm_enet_priv *priv, u32 off) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return bcm_readb(priv->base + off); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline void enetsw_writeb(struct bcm_enet_priv *priv, 788c2ecf20Sopenharmony_ci u8 val, u32 off) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci bcm_writeb(val, priv->base + off); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* io helpers to access shared registers */ 858c2ecf20Sopenharmony_cistatic inline u32 enet_dma_readl(struct bcm_enet_priv *priv, u32 off) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return bcm_readl(bcm_enet_shared_base[0] + off); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic inline void enet_dma_writel(struct bcm_enet_priv *priv, 918c2ecf20Sopenharmony_ci u32 val, u32 off) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci bcm_writel(val, bcm_enet_shared_base[0] + off); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline u32 enet_dmac_readl(struct bcm_enet_priv *priv, u32 off, int chan) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return bcm_readl(bcm_enet_shared_base[1] + 998c2ecf20Sopenharmony_ci bcm63xx_enetdmacreg(off) + chan * priv->dma_chan_width); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline void enet_dmac_writel(struct bcm_enet_priv *priv, 1038c2ecf20Sopenharmony_ci u32 val, u32 off, int chan) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci bcm_writel(val, bcm_enet_shared_base[1] + 1068c2ecf20Sopenharmony_ci bcm63xx_enetdmacreg(off) + chan * priv->dma_chan_width); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline u32 enet_dmas_readl(struct bcm_enet_priv *priv, u32 off, int chan) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci return bcm_readl(bcm_enet_shared_base[2] + off + chan * priv->dma_chan_width); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic inline void enet_dmas_writel(struct bcm_enet_priv *priv, 1158c2ecf20Sopenharmony_ci u32 val, u32 off, int chan) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci bcm_writel(val, bcm_enet_shared_base[2] + off + chan * priv->dma_chan_width); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * write given data into mii register and wait for transfer to end 1228c2ecf20Sopenharmony_ci * with timeout (average measured transfer time is 25us) 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic int do_mdio_op(struct bcm_enet_priv *priv, unsigned int data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci int limit; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* make sure mii interrupt status is cleared */ 1298c2ecf20Sopenharmony_ci enet_writel(priv, ENET_IR_MII, ENET_IR_REG); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci enet_writel(priv, data, ENET_MIIDATA_REG); 1328c2ecf20Sopenharmony_ci wmb(); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* busy wait on mii interrupt bit, with timeout */ 1358c2ecf20Sopenharmony_ci limit = 1000; 1368c2ecf20Sopenharmony_ci do { 1378c2ecf20Sopenharmony_ci if (enet_readl(priv, ENET_IR_REG) & ENET_IR_MII) 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci udelay(1); 1408c2ecf20Sopenharmony_ci } while (limit-- > 0); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return (limit < 0) ? 1 : 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * MII internal read callback 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cistatic int bcm_enet_mdio_read(struct bcm_enet_priv *priv, int mii_id, 1498c2ecf20Sopenharmony_ci int regnum) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci u32 tmp, val; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci tmp = regnum << ENET_MIIDATA_REG_SHIFT; 1548c2ecf20Sopenharmony_ci tmp |= 0x2 << ENET_MIIDATA_TA_SHIFT; 1558c2ecf20Sopenharmony_ci tmp |= mii_id << ENET_MIIDATA_PHYID_SHIFT; 1568c2ecf20Sopenharmony_ci tmp |= ENET_MIIDATA_OP_READ_MASK; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (do_mdio_op(priv, tmp)) 1598c2ecf20Sopenharmony_ci return -1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_MIIDATA_REG); 1628c2ecf20Sopenharmony_ci val &= 0xffff; 1638c2ecf20Sopenharmony_ci return val; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* 1678c2ecf20Sopenharmony_ci * MII internal write callback 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_cistatic int bcm_enet_mdio_write(struct bcm_enet_priv *priv, int mii_id, 1708c2ecf20Sopenharmony_ci int regnum, u16 value) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci u32 tmp; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci tmp = (value & 0xffff) << ENET_MIIDATA_DATA_SHIFT; 1758c2ecf20Sopenharmony_ci tmp |= 0x2 << ENET_MIIDATA_TA_SHIFT; 1768c2ecf20Sopenharmony_ci tmp |= regnum << ENET_MIIDATA_REG_SHIFT; 1778c2ecf20Sopenharmony_ci tmp |= mii_id << ENET_MIIDATA_PHYID_SHIFT; 1788c2ecf20Sopenharmony_ci tmp |= ENET_MIIDATA_OP_WRITE_MASK; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci (void)do_mdio_op(priv, tmp); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * MII read callback from phylib 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistatic int bcm_enet_mdio_read_phylib(struct mii_bus *bus, int mii_id, 1888c2ecf20Sopenharmony_ci int regnum) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci return bcm_enet_mdio_read(bus->priv, mii_id, regnum); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * MII write callback from phylib 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic int bcm_enet_mdio_write_phylib(struct mii_bus *bus, int mii_id, 1978c2ecf20Sopenharmony_ci int regnum, u16 value) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return bcm_enet_mdio_write(bus->priv, mii_id, regnum, value); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * MII read callback from mii core 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic int bcm_enet_mdio_read_mii(struct net_device *dev, int mii_id, 2068c2ecf20Sopenharmony_ci int regnum) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return bcm_enet_mdio_read(netdev_priv(dev), mii_id, regnum); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * MII write callback from mii core 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic void bcm_enet_mdio_write_mii(struct net_device *dev, int mii_id, 2158c2ecf20Sopenharmony_ci int regnum, int value) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci bcm_enet_mdio_write(netdev_priv(dev), mii_id, regnum, value); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* 2218c2ecf20Sopenharmony_ci * refill rx queue 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_cistatic int bcm_enet_refill_rx(struct net_device *dev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci while (priv->rx_desc_count < priv->rx_ring_size) { 2308c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 2318c2ecf20Sopenharmony_ci struct sk_buff *skb; 2328c2ecf20Sopenharmony_ci dma_addr_t p; 2338c2ecf20Sopenharmony_ci int desc_idx; 2348c2ecf20Sopenharmony_ci u32 len_stat; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci desc_idx = priv->rx_dirty_desc; 2378c2ecf20Sopenharmony_ci desc = &priv->rx_desc_cpu[desc_idx]; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!priv->rx_skb[desc_idx]) { 2408c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, priv->rx_skb_size); 2418c2ecf20Sopenharmony_ci if (!skb) 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci priv->rx_skb[desc_idx] = skb; 2448c2ecf20Sopenharmony_ci p = dma_map_single(&priv->pdev->dev, skb->data, 2458c2ecf20Sopenharmony_ci priv->rx_skb_size, 2468c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2478c2ecf20Sopenharmony_ci desc->address = p; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT; 2518c2ecf20Sopenharmony_ci len_stat |= DMADESC_OWNER_MASK; 2528c2ecf20Sopenharmony_ci if (priv->rx_dirty_desc == priv->rx_ring_size - 1) { 2538c2ecf20Sopenharmony_ci len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift); 2548c2ecf20Sopenharmony_ci priv->rx_dirty_desc = 0; 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci priv->rx_dirty_desc++; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci wmb(); 2598c2ecf20Sopenharmony_ci desc->len_stat = len_stat; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci priv->rx_desc_count++; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* tell dma engine we allocated one buffer */ 2648c2ecf20Sopenharmony_ci if (priv->dma_has_sram) 2658c2ecf20Sopenharmony_ci enet_dma_writel(priv, 1, ENETDMA_BUFALLOC_REG(priv->rx_chan)); 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 1, ENETDMAC_BUFALLOC, priv->rx_chan); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* If rx ring is still empty, set a timer to try allocating 2718c2ecf20Sopenharmony_ci * again at a later time. */ 2728c2ecf20Sopenharmony_ci if (priv->rx_desc_count == 0 && netif_running(dev)) { 2738c2ecf20Sopenharmony_ci dev_warn(&priv->pdev->dev, "unable to refill rx ring\n"); 2748c2ecf20Sopenharmony_ci priv->rx_timeout.expires = jiffies + HZ; 2758c2ecf20Sopenharmony_ci add_timer(&priv->rx_timeout); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* 2828c2ecf20Sopenharmony_ci * timer callback to defer refill rx queue in case we're OOM 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic void bcm_enet_refill_rx_timer(struct timer_list *t) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv = from_timer(priv, t, rx_timeout); 2878c2ecf20Sopenharmony_ci struct net_device *dev = priv->net_dev; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_lock(&priv->rx_lock); 2908c2ecf20Sopenharmony_ci bcm_enet_refill_rx(dev); 2918c2ecf20Sopenharmony_ci spin_unlock(&priv->rx_lock); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * extract packet from rx queue 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic int bcm_enet_receive_queue(struct net_device *dev, int budget) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 3008c2ecf20Sopenharmony_ci struct device *kdev; 3018c2ecf20Sopenharmony_ci int processed; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 3048c2ecf20Sopenharmony_ci kdev = &priv->pdev->dev; 3058c2ecf20Sopenharmony_ci processed = 0; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* don't scan ring further than number of refilled 3088c2ecf20Sopenharmony_ci * descriptor */ 3098c2ecf20Sopenharmony_ci if (budget > priv->rx_desc_count) 3108c2ecf20Sopenharmony_ci budget = priv->rx_desc_count; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci do { 3138c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 3148c2ecf20Sopenharmony_ci struct sk_buff *skb; 3158c2ecf20Sopenharmony_ci int desc_idx; 3168c2ecf20Sopenharmony_ci u32 len_stat; 3178c2ecf20Sopenharmony_ci unsigned int len; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci desc_idx = priv->rx_curr_desc; 3208c2ecf20Sopenharmony_ci desc = &priv->rx_desc_cpu[desc_idx]; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* make sure we actually read the descriptor status at 3238c2ecf20Sopenharmony_ci * each loop */ 3248c2ecf20Sopenharmony_ci rmb(); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci len_stat = desc->len_stat; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* break if dma ownership belongs to hw */ 3298c2ecf20Sopenharmony_ci if (len_stat & DMADESC_OWNER_MASK) 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci processed++; 3338c2ecf20Sopenharmony_ci priv->rx_curr_desc++; 3348c2ecf20Sopenharmony_ci if (priv->rx_curr_desc == priv->rx_ring_size) 3358c2ecf20Sopenharmony_ci priv->rx_curr_desc = 0; 3368c2ecf20Sopenharmony_ci priv->rx_desc_count--; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* if the packet does not have start of packet _and_ 3398c2ecf20Sopenharmony_ci * end of packet flag set, then just recycle it */ 3408c2ecf20Sopenharmony_ci if ((len_stat & (DMADESC_ESOP_MASK >> priv->dma_desc_shift)) != 3418c2ecf20Sopenharmony_ci (DMADESC_ESOP_MASK >> priv->dma_desc_shift)) { 3428c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 3438c2ecf20Sopenharmony_ci continue; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* recycle packet if it's marked as bad */ 3478c2ecf20Sopenharmony_ci if (!priv->enet_is_sw && 3488c2ecf20Sopenharmony_ci unlikely(len_stat & DMADESC_ERR_MASK)) { 3498c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (len_stat & DMADESC_OVSIZE_MASK) 3528c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 3538c2ecf20Sopenharmony_ci if (len_stat & DMADESC_CRC_MASK) 3548c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 3558c2ecf20Sopenharmony_ci if (len_stat & DMADESC_UNDER_MASK) 3568c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 3578c2ecf20Sopenharmony_ci if (len_stat & DMADESC_OV_MASK) 3588c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors++; 3598c2ecf20Sopenharmony_ci continue; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* valid packet */ 3638c2ecf20Sopenharmony_ci skb = priv->rx_skb[desc_idx]; 3648c2ecf20Sopenharmony_ci len = (len_stat & DMADESC_LENGTH_MASK) >> DMADESC_LENGTH_SHIFT; 3658c2ecf20Sopenharmony_ci /* don't include FCS */ 3668c2ecf20Sopenharmony_ci len -= 4; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (len < copybreak) { 3698c2ecf20Sopenharmony_ci struct sk_buff *nskb; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci nskb = napi_alloc_skb(&priv->napi, len); 3728c2ecf20Sopenharmony_ci if (!nskb) { 3738c2ecf20Sopenharmony_ci /* forget packet, just rearm desc */ 3748c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 3758c2ecf20Sopenharmony_ci continue; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(kdev, desc->address, 3798c2ecf20Sopenharmony_ci len, DMA_FROM_DEVICE); 3808c2ecf20Sopenharmony_ci memcpy(nskb->data, skb->data, len); 3818c2ecf20Sopenharmony_ci dma_sync_single_for_device(kdev, desc->address, 3828c2ecf20Sopenharmony_ci len, DMA_FROM_DEVICE); 3838c2ecf20Sopenharmony_ci skb = nskb; 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, desc->address, 3868c2ecf20Sopenharmony_ci priv->rx_skb_size, DMA_FROM_DEVICE); 3878c2ecf20Sopenharmony_ci priv->rx_skb[desc_idx] = NULL; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci skb_put(skb, len); 3918c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 3928c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 3938c2ecf20Sopenharmony_ci dev->stats.rx_bytes += len; 3948c2ecf20Sopenharmony_ci netif_receive_skb(skb); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci } while (--budget > 0); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (processed || !priv->rx_desc_count) { 3998c2ecf20Sopenharmony_ci bcm_enet_refill_rx(dev); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* kick rx dma */ 4028c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_en_mask, 4038c2ecf20Sopenharmony_ci ENETDMAC_CHANCFG, priv->rx_chan); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return processed; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* 4118c2ecf20Sopenharmony_ci * try to or force reclaim of transmitted buffers 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_cistatic int bcm_enet_tx_reclaim(struct net_device *dev, int force) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 4168c2ecf20Sopenharmony_ci int released; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 4198c2ecf20Sopenharmony_ci released = 0; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci while (priv->tx_desc_count < priv->tx_ring_size) { 4228c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 4238c2ecf20Sopenharmony_ci struct sk_buff *skb; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* We run in a bh and fight against start_xmit, which 4268c2ecf20Sopenharmony_ci * is called with bh disabled */ 4278c2ecf20Sopenharmony_ci spin_lock(&priv->tx_lock); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci desc = &priv->tx_desc_cpu[priv->tx_dirty_desc]; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!force && (desc->len_stat & DMADESC_OWNER_MASK)) { 4328c2ecf20Sopenharmony_ci spin_unlock(&priv->tx_lock); 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* ensure other field of the descriptor were not read 4378c2ecf20Sopenharmony_ci * before we checked ownership */ 4388c2ecf20Sopenharmony_ci rmb(); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci skb = priv->tx_skb[priv->tx_dirty_desc]; 4418c2ecf20Sopenharmony_ci priv->tx_skb[priv->tx_dirty_desc] = NULL; 4428c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, desc->address, skb->len, 4438c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci priv->tx_dirty_desc++; 4468c2ecf20Sopenharmony_ci if (priv->tx_dirty_desc == priv->tx_ring_size) 4478c2ecf20Sopenharmony_ci priv->tx_dirty_desc = 0; 4488c2ecf20Sopenharmony_ci priv->tx_desc_count++; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci spin_unlock(&priv->tx_lock); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (desc->len_stat & DMADESC_UNDER_MASK) 4538c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 4568c2ecf20Sopenharmony_ci released++; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev) && released) 4608c2ecf20Sopenharmony_ci netif_wake_queue(dev); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return released; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/* 4668c2ecf20Sopenharmony_ci * poll func, called by network core 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_cistatic int bcm_enet_poll(struct napi_struct *napi, int budget) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 4718c2ecf20Sopenharmony_ci struct net_device *dev; 4728c2ecf20Sopenharmony_ci int rx_work_done; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci priv = container_of(napi, struct bcm_enet_priv, napi); 4758c2ecf20Sopenharmony_ci dev = priv->net_dev; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* ack interrupts */ 4788c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 4798c2ecf20Sopenharmony_ci ENETDMAC_IR, priv->rx_chan); 4808c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 4818c2ecf20Sopenharmony_ci ENETDMAC_IR, priv->tx_chan); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* reclaim sent skb */ 4848c2ecf20Sopenharmony_ci bcm_enet_tx_reclaim(dev, 0); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci spin_lock(&priv->rx_lock); 4878c2ecf20Sopenharmony_ci rx_work_done = bcm_enet_receive_queue(dev, budget); 4888c2ecf20Sopenharmony_ci spin_unlock(&priv->rx_lock); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (rx_work_done >= budget) { 4918c2ecf20Sopenharmony_ci /* rx queue is not yet empty/clean */ 4928c2ecf20Sopenharmony_ci return rx_work_done; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* no more packet in rx/tx queue, remove device from poll 4968c2ecf20Sopenharmony_ci * queue */ 4978c2ecf20Sopenharmony_ci napi_complete_done(napi, rx_work_done); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* restore rx/tx interrupt */ 5008c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 5018c2ecf20Sopenharmony_ci ENETDMAC_IRMASK, priv->rx_chan); 5028c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 5038c2ecf20Sopenharmony_ci ENETDMAC_IRMASK, priv->tx_chan); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return rx_work_done; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/* 5098c2ecf20Sopenharmony_ci * mac interrupt handler 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_cistatic irqreturn_t bcm_enet_isr_mac(int irq, void *dev_id) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct net_device *dev; 5148c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 5158c2ecf20Sopenharmony_ci u32 stat; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci dev = dev_id; 5188c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci stat = enet_readl(priv, ENET_IR_REG); 5218c2ecf20Sopenharmony_ci if (!(stat & ENET_IR_MIB)) 5228c2ecf20Sopenharmony_ci return IRQ_NONE; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* clear & mask interrupt */ 5258c2ecf20Sopenharmony_ci enet_writel(priv, ENET_IR_MIB, ENET_IR_REG); 5268c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_IRMASK_REG); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* read mib registers in workqueue */ 5298c2ecf20Sopenharmony_ci schedule_work(&priv->mib_update_task); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/* 5358c2ecf20Sopenharmony_ci * rx/tx dma interrupt handler 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_cistatic irqreturn_t bcm_enet_isr_dma(int irq, void *dev_id) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct net_device *dev; 5408c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci dev = dev_id; 5438c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* mask rx/tx interrupts */ 5468c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); 5478c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci napi_schedule(&priv->napi); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/* 5558c2ecf20Sopenharmony_ci * tx request callback 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic netdev_tx_t 5588c2ecf20Sopenharmony_cibcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 5618c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 5628c2ecf20Sopenharmony_ci u32 len_stat; 5638c2ecf20Sopenharmony_ci netdev_tx_t ret; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* lock against tx reclaim */ 5688c2ecf20Sopenharmony_ci spin_lock(&priv->tx_lock); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* make sure the tx hw queue is not full, should not happen 5718c2ecf20Sopenharmony_ci * since we stop queue before it's the case */ 5728c2ecf20Sopenharmony_ci if (unlikely(!priv->tx_desc_count)) { 5738c2ecf20Sopenharmony_ci netif_stop_queue(dev); 5748c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "xmit called with no tx desc " 5758c2ecf20Sopenharmony_ci "available?\n"); 5768c2ecf20Sopenharmony_ci ret = NETDEV_TX_BUSY; 5778c2ecf20Sopenharmony_ci goto out_unlock; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* pad small packets sent on a switch device */ 5818c2ecf20Sopenharmony_ci if (priv->enet_is_sw && skb->len < 64) { 5828c2ecf20Sopenharmony_ci int needed = 64 - skb->len; 5838c2ecf20Sopenharmony_ci char *data; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (unlikely(skb_tailroom(skb) < needed)) { 5868c2ecf20Sopenharmony_ci struct sk_buff *nskb; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci nskb = skb_copy_expand(skb, 0, needed, GFP_ATOMIC); 5898c2ecf20Sopenharmony_ci if (!nskb) { 5908c2ecf20Sopenharmony_ci ret = NETDEV_TX_BUSY; 5918c2ecf20Sopenharmony_ci goto out_unlock; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5948c2ecf20Sopenharmony_ci skb = nskb; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci data = skb_put_zero(skb, needed); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* point to the next available desc */ 6008c2ecf20Sopenharmony_ci desc = &priv->tx_desc_cpu[priv->tx_curr_desc]; 6018c2ecf20Sopenharmony_ci priv->tx_skb[priv->tx_curr_desc] = skb; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* fill descriptor */ 6048c2ecf20Sopenharmony_ci desc->address = dma_map_single(&priv->pdev->dev, skb->data, skb->len, 6058c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci len_stat = (skb->len << DMADESC_LENGTH_SHIFT) & DMADESC_LENGTH_MASK; 6088c2ecf20Sopenharmony_ci len_stat |= (DMADESC_ESOP_MASK >> priv->dma_desc_shift) | 6098c2ecf20Sopenharmony_ci DMADESC_APPEND_CRC | 6108c2ecf20Sopenharmony_ci DMADESC_OWNER_MASK; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci priv->tx_curr_desc++; 6138c2ecf20Sopenharmony_ci if (priv->tx_curr_desc == priv->tx_ring_size) { 6148c2ecf20Sopenharmony_ci priv->tx_curr_desc = 0; 6158c2ecf20Sopenharmony_ci len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci priv->tx_desc_count--; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* dma might be already polling, make sure we update desc 6208c2ecf20Sopenharmony_ci * fields in correct order */ 6218c2ecf20Sopenharmony_ci wmb(); 6228c2ecf20Sopenharmony_ci desc->len_stat = len_stat; 6238c2ecf20Sopenharmony_ci wmb(); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* kick tx dma */ 6268c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_en_mask, 6278c2ecf20Sopenharmony_ci ENETDMAC_CHANCFG, priv->tx_chan); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* stop queue if no more desc available */ 6308c2ecf20Sopenharmony_ci if (!priv->tx_desc_count) 6318c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 6348c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 6358c2ecf20Sopenharmony_ci ret = NETDEV_TX_OK; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ciout_unlock: 6388c2ecf20Sopenharmony_ci spin_unlock(&priv->tx_lock); 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* 6438c2ecf20Sopenharmony_ci * Change the interface's mac address. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_cistatic int bcm_enet_set_mac_address(struct net_device *dev, void *p) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 6488c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 6498c2ecf20Sopenharmony_ci u32 val; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 6528c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* use perfect match register 0 to store my mac address */ 6558c2ecf20Sopenharmony_ci val = (dev->dev_addr[2] << 24) | (dev->dev_addr[3] << 16) | 6568c2ecf20Sopenharmony_ci (dev->dev_addr[4] << 8) | dev->dev_addr[5]; 6578c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_PML_REG(0)); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci val = (dev->dev_addr[0] << 8 | dev->dev_addr[1]); 6608c2ecf20Sopenharmony_ci val |= ENET_PMH_DATAVALID_MASK; 6618c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_PMH_REG(0)); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* 6678c2ecf20Sopenharmony_ci * Change rx mode (promiscuous/allmulti) and update multicast list 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_cistatic void bcm_enet_set_multicast_list(struct net_device *dev) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 6728c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 6738c2ecf20Sopenharmony_ci u32 val; 6748c2ecf20Sopenharmony_ci int i; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_RXCFG_REG); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) 6818c2ecf20Sopenharmony_ci val |= ENET_RXCFG_PROMISC_MASK; 6828c2ecf20Sopenharmony_ci else 6838c2ecf20Sopenharmony_ci val &= ~ENET_RXCFG_PROMISC_MASK; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* only 3 perfect match registers left, first one is used for 6868c2ecf20Sopenharmony_ci * own mac address */ 6878c2ecf20Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) || netdev_mc_count(dev) > 3) 6888c2ecf20Sopenharmony_ci val |= ENET_RXCFG_ALLMCAST_MASK; 6898c2ecf20Sopenharmony_ci else 6908c2ecf20Sopenharmony_ci val &= ~ENET_RXCFG_ALLMCAST_MASK; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* no need to set perfect match registers if we catch all 6938c2ecf20Sopenharmony_ci * multicast */ 6948c2ecf20Sopenharmony_ci if (val & ENET_RXCFG_ALLMCAST_MASK) { 6958c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_RXCFG_REG); 6968c2ecf20Sopenharmony_ci return; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci i = 0; 7008c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 7018c2ecf20Sopenharmony_ci u8 *dmi_addr; 7028c2ecf20Sopenharmony_ci u32 tmp; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (i == 3) 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci /* update perfect match registers */ 7078c2ecf20Sopenharmony_ci dmi_addr = ha->addr; 7088c2ecf20Sopenharmony_ci tmp = (dmi_addr[2] << 24) | (dmi_addr[3] << 16) | 7098c2ecf20Sopenharmony_ci (dmi_addr[4] << 8) | dmi_addr[5]; 7108c2ecf20Sopenharmony_ci enet_writel(priv, tmp, ENET_PML_REG(i + 1)); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci tmp = (dmi_addr[0] << 8 | dmi_addr[1]); 7138c2ecf20Sopenharmony_ci tmp |= ENET_PMH_DATAVALID_MASK; 7148c2ecf20Sopenharmony_ci enet_writel(priv, tmp, ENET_PMH_REG(i++ + 1)); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci for (; i < 3; i++) { 7188c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_PML_REG(i + 1)); 7198c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_PMH_REG(i + 1)); 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_RXCFG_REG); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/* 7268c2ecf20Sopenharmony_ci * set mac duplex parameters 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic void bcm_enet_set_duplex(struct bcm_enet_priv *priv, int fullduplex) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci u32 val; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_TXCTL_REG); 7338c2ecf20Sopenharmony_ci if (fullduplex) 7348c2ecf20Sopenharmony_ci val |= ENET_TXCTL_FD_MASK; 7358c2ecf20Sopenharmony_ci else 7368c2ecf20Sopenharmony_ci val &= ~ENET_TXCTL_FD_MASK; 7378c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_TXCTL_REG); 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci/* 7418c2ecf20Sopenharmony_ci * set mac flow control parameters 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_cistatic void bcm_enet_set_flow(struct bcm_enet_priv *priv, int rx_en, int tx_en) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci u32 val; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* rx flow control (pause frame handling) */ 7488c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_RXCFG_REG); 7498c2ecf20Sopenharmony_ci if (rx_en) 7508c2ecf20Sopenharmony_ci val |= ENET_RXCFG_ENFLOW_MASK; 7518c2ecf20Sopenharmony_ci else 7528c2ecf20Sopenharmony_ci val &= ~ENET_RXCFG_ENFLOW_MASK; 7538c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_RXCFG_REG); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (!priv->dma_has_sram) 7568c2ecf20Sopenharmony_ci return; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* tx flow control (pause frame generation) */ 7598c2ecf20Sopenharmony_ci val = enet_dma_readl(priv, ENETDMA_CFG_REG); 7608c2ecf20Sopenharmony_ci if (tx_en) 7618c2ecf20Sopenharmony_ci val |= ENETDMA_CFG_FLOWCH_MASK(priv->rx_chan); 7628c2ecf20Sopenharmony_ci else 7638c2ecf20Sopenharmony_ci val &= ~ENETDMA_CFG_FLOWCH_MASK(priv->rx_chan); 7648c2ecf20Sopenharmony_ci enet_dma_writel(priv, val, ENETDMA_CFG_REG); 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/* 7688c2ecf20Sopenharmony_ci * link changed callback (from phylib) 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic void bcm_enet_adjust_phy_link(struct net_device *dev) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 7738c2ecf20Sopenharmony_ci struct phy_device *phydev; 7748c2ecf20Sopenharmony_ci int status_changed; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 7778c2ecf20Sopenharmony_ci phydev = dev->phydev; 7788c2ecf20Sopenharmony_ci status_changed = 0; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (priv->old_link != phydev->link) { 7818c2ecf20Sopenharmony_ci status_changed = 1; 7828c2ecf20Sopenharmony_ci priv->old_link = phydev->link; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* reflect duplex change in mac configuration */ 7868c2ecf20Sopenharmony_ci if (phydev->link && phydev->duplex != priv->old_duplex) { 7878c2ecf20Sopenharmony_ci bcm_enet_set_duplex(priv, 7888c2ecf20Sopenharmony_ci (phydev->duplex == DUPLEX_FULL) ? 1 : 0); 7898c2ecf20Sopenharmony_ci status_changed = 1; 7908c2ecf20Sopenharmony_ci priv->old_duplex = phydev->duplex; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* enable flow control if remote advertise it (trust phylib to 7948c2ecf20Sopenharmony_ci * check that duplex is full */ 7958c2ecf20Sopenharmony_ci if (phydev->link && phydev->pause != priv->old_pause) { 7968c2ecf20Sopenharmony_ci int rx_pause_en, tx_pause_en; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (phydev->pause) { 7998c2ecf20Sopenharmony_ci /* pause was advertised by lpa and us */ 8008c2ecf20Sopenharmony_ci rx_pause_en = 1; 8018c2ecf20Sopenharmony_ci tx_pause_en = 1; 8028c2ecf20Sopenharmony_ci } else if (!priv->pause_auto) { 8038c2ecf20Sopenharmony_ci /* pause setting overridden by user */ 8048c2ecf20Sopenharmony_ci rx_pause_en = priv->pause_rx; 8058c2ecf20Sopenharmony_ci tx_pause_en = priv->pause_tx; 8068c2ecf20Sopenharmony_ci } else { 8078c2ecf20Sopenharmony_ci rx_pause_en = 0; 8088c2ecf20Sopenharmony_ci tx_pause_en = 0; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci bcm_enet_set_flow(priv, rx_pause_en, tx_pause_en); 8128c2ecf20Sopenharmony_ci status_changed = 1; 8138c2ecf20Sopenharmony_ci priv->old_pause = phydev->pause; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (status_changed) { 8178c2ecf20Sopenharmony_ci pr_info("%s: link %s", dev->name, phydev->link ? 8188c2ecf20Sopenharmony_ci "UP" : "DOWN"); 8198c2ecf20Sopenharmony_ci if (phydev->link) 8208c2ecf20Sopenharmony_ci pr_cont(" - %d/%s - flow control %s", phydev->speed, 8218c2ecf20Sopenharmony_ci DUPLEX_FULL == phydev->duplex ? "full" : "half", 8228c2ecf20Sopenharmony_ci phydev->pause == 1 ? "rx&tx" : "off"); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci pr_cont("\n"); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci/* 8298c2ecf20Sopenharmony_ci * link changed callback (if phylib is not used) 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_cistatic void bcm_enet_adjust_link(struct net_device *dev) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 8368c2ecf20Sopenharmony_ci bcm_enet_set_duplex(priv, priv->force_duplex_full); 8378c2ecf20Sopenharmony_ci bcm_enet_set_flow(priv, priv->pause_rx, priv->pause_tx); 8388c2ecf20Sopenharmony_ci netif_carrier_on(dev); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci pr_info("%s: link forced UP - %d/%s - flow control %s/%s\n", 8418c2ecf20Sopenharmony_ci dev->name, 8428c2ecf20Sopenharmony_ci priv->force_speed_100 ? 100 : 10, 8438c2ecf20Sopenharmony_ci priv->force_duplex_full ? "full" : "half", 8448c2ecf20Sopenharmony_ci priv->pause_rx ? "rx" : "off", 8458c2ecf20Sopenharmony_ci priv->pause_tx ? "tx" : "off"); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci/* 8498c2ecf20Sopenharmony_ci * open callback, allocate dma rings & buffers and start rx operation 8508c2ecf20Sopenharmony_ci */ 8518c2ecf20Sopenharmony_cistatic int bcm_enet_open(struct net_device *dev) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 8548c2ecf20Sopenharmony_ci struct sockaddr addr; 8558c2ecf20Sopenharmony_ci struct device *kdev; 8568c2ecf20Sopenharmony_ci struct phy_device *phydev; 8578c2ecf20Sopenharmony_ci int i, ret; 8588c2ecf20Sopenharmony_ci unsigned int size; 8598c2ecf20Sopenharmony_ci char phy_id[MII_BUS_ID_SIZE + 3]; 8608c2ecf20Sopenharmony_ci void *p; 8618c2ecf20Sopenharmony_ci u32 val; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 8648c2ecf20Sopenharmony_ci kdev = &priv->pdev->dev; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (priv->has_phy) { 8678c2ecf20Sopenharmony_ci /* connect to PHY */ 8688c2ecf20Sopenharmony_ci snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, 8698c2ecf20Sopenharmony_ci priv->mii_bus->id, priv->phy_id); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, 8728c2ecf20Sopenharmony_ci PHY_INTERFACE_MODE_MII); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) { 8758c2ecf20Sopenharmony_ci dev_err(kdev, "could not attach to PHY\n"); 8768c2ecf20Sopenharmony_ci return PTR_ERR(phydev); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* mask with MAC supported features */ 8808c2ecf20Sopenharmony_ci phy_support_sym_pause(phydev); 8818c2ecf20Sopenharmony_ci phy_set_max_speed(phydev, SPEED_100); 8828c2ecf20Sopenharmony_ci phy_set_sym_pause(phydev, priv->pause_rx, priv->pause_rx, 8838c2ecf20Sopenharmony_ci priv->pause_auto); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci phy_attached_info(phydev); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci priv->old_link = 0; 8888c2ecf20Sopenharmony_ci priv->old_duplex = -1; 8898c2ecf20Sopenharmony_ci priv->old_pause = -1; 8908c2ecf20Sopenharmony_ci } else { 8918c2ecf20Sopenharmony_ci phydev = NULL; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* mask all interrupts and request them */ 8958c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_IRMASK_REG); 8968c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); 8978c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci ret = request_irq(dev->irq, bcm_enet_isr_mac, 0, dev->name, dev); 9008c2ecf20Sopenharmony_ci if (ret) 9018c2ecf20Sopenharmony_ci goto out_phy_disconnect; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci ret = request_irq(priv->irq_rx, bcm_enet_isr_dma, 0, 9048c2ecf20Sopenharmony_ci dev->name, dev); 9058c2ecf20Sopenharmony_ci if (ret) 9068c2ecf20Sopenharmony_ci goto out_freeirq; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci ret = request_irq(priv->irq_tx, bcm_enet_isr_dma, 9098c2ecf20Sopenharmony_ci 0, dev->name, dev); 9108c2ecf20Sopenharmony_ci if (ret) 9118c2ecf20Sopenharmony_ci goto out_freeirq_rx; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* initialize perfect match registers */ 9148c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 9158c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_PML_REG(i)); 9168c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_PMH_REG(i)); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* write device mac address */ 9208c2ecf20Sopenharmony_ci memcpy(addr.sa_data, dev->dev_addr, ETH_ALEN); 9218c2ecf20Sopenharmony_ci bcm_enet_set_mac_address(dev, &addr); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* allocate rx dma ring */ 9248c2ecf20Sopenharmony_ci size = priv->rx_ring_size * sizeof(struct bcm_enet_desc); 9258c2ecf20Sopenharmony_ci p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL); 9268c2ecf20Sopenharmony_ci if (!p) { 9278c2ecf20Sopenharmony_ci ret = -ENOMEM; 9288c2ecf20Sopenharmony_ci goto out_freeirq_tx; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci priv->rx_desc_alloc_size = size; 9328c2ecf20Sopenharmony_ci priv->rx_desc_cpu = p; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* allocate tx dma ring */ 9358c2ecf20Sopenharmony_ci size = priv->tx_ring_size * sizeof(struct bcm_enet_desc); 9368c2ecf20Sopenharmony_ci p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL); 9378c2ecf20Sopenharmony_ci if (!p) { 9388c2ecf20Sopenharmony_ci ret = -ENOMEM; 9398c2ecf20Sopenharmony_ci goto out_free_rx_ring; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci priv->tx_desc_alloc_size = size; 9438c2ecf20Sopenharmony_ci priv->tx_desc_cpu = p; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci priv->tx_skb = kcalloc(priv->tx_ring_size, sizeof(struct sk_buff *), 9468c2ecf20Sopenharmony_ci GFP_KERNEL); 9478c2ecf20Sopenharmony_ci if (!priv->tx_skb) { 9488c2ecf20Sopenharmony_ci ret = -ENOMEM; 9498c2ecf20Sopenharmony_ci goto out_free_tx_ring; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci priv->tx_desc_count = priv->tx_ring_size; 9538c2ecf20Sopenharmony_ci priv->tx_dirty_desc = 0; 9548c2ecf20Sopenharmony_ci priv->tx_curr_desc = 0; 9558c2ecf20Sopenharmony_ci spin_lock_init(&priv->tx_lock); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* init & fill rx ring with skbs */ 9588c2ecf20Sopenharmony_ci priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *), 9598c2ecf20Sopenharmony_ci GFP_KERNEL); 9608c2ecf20Sopenharmony_ci if (!priv->rx_skb) { 9618c2ecf20Sopenharmony_ci ret = -ENOMEM; 9628c2ecf20Sopenharmony_ci goto out_free_tx_skb; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci priv->rx_desc_count = 0; 9668c2ecf20Sopenharmony_ci priv->rx_dirty_desc = 0; 9678c2ecf20Sopenharmony_ci priv->rx_curr_desc = 0; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* initialize flow control buffer allocation */ 9708c2ecf20Sopenharmony_ci if (priv->dma_has_sram) 9718c2ecf20Sopenharmony_ci enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, 9728c2ecf20Sopenharmony_ci ENETDMA_BUFALLOC_REG(priv->rx_chan)); 9738c2ecf20Sopenharmony_ci else 9748c2ecf20Sopenharmony_ci enet_dmac_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, 9758c2ecf20Sopenharmony_ci ENETDMAC_BUFALLOC, priv->rx_chan); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (bcm_enet_refill_rx(dev)) { 9788c2ecf20Sopenharmony_ci dev_err(kdev, "cannot allocate rx skb queue\n"); 9798c2ecf20Sopenharmony_ci ret = -ENOMEM; 9808c2ecf20Sopenharmony_ci goto out; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* write rx & tx ring addresses */ 9848c2ecf20Sopenharmony_ci if (priv->dma_has_sram) { 9858c2ecf20Sopenharmony_ci enet_dmas_writel(priv, priv->rx_desc_dma, 9868c2ecf20Sopenharmony_ci ENETDMAS_RSTART_REG, priv->rx_chan); 9878c2ecf20Sopenharmony_ci enet_dmas_writel(priv, priv->tx_desc_dma, 9888c2ecf20Sopenharmony_ci ENETDMAS_RSTART_REG, priv->tx_chan); 9898c2ecf20Sopenharmony_ci } else { 9908c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->rx_desc_dma, 9918c2ecf20Sopenharmony_ci ENETDMAC_RSTART, priv->rx_chan); 9928c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->tx_desc_dma, 9938c2ecf20Sopenharmony_ci ENETDMAC_RSTART, priv->tx_chan); 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* clear remaining state ram for rx & tx channel */ 9978c2ecf20Sopenharmony_ci if (priv->dma_has_sram) { 9988c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->rx_chan); 9998c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->tx_chan); 10008c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->rx_chan); 10018c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->tx_chan); 10028c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->rx_chan); 10038c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->tx_chan); 10048c2ecf20Sopenharmony_ci } else { 10058c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_FC, priv->rx_chan); 10068c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_FC, priv->tx_chan); 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* set max rx/tx length */ 10108c2ecf20Sopenharmony_ci enet_writel(priv, priv->hw_mtu, ENET_RXMAXLEN_REG); 10118c2ecf20Sopenharmony_ci enet_writel(priv, priv->hw_mtu, ENET_TXMAXLEN_REG); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* set dma maximum burst len */ 10148c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_maxburst, 10158c2ecf20Sopenharmony_ci ENETDMAC_MAXBURST, priv->rx_chan); 10168c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_maxburst, 10178c2ecf20Sopenharmony_ci ENETDMAC_MAXBURST, priv->tx_chan); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* set correct transmit fifo watermark */ 10208c2ecf20Sopenharmony_ci enet_writel(priv, BCMENET_TX_FIFO_TRESH, ENET_TXWMARK_REG); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* set flow control low/high threshold to 1/3 / 2/3 */ 10238c2ecf20Sopenharmony_ci if (priv->dma_has_sram) { 10248c2ecf20Sopenharmony_ci val = priv->rx_ring_size / 3; 10258c2ecf20Sopenharmony_ci enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan)); 10268c2ecf20Sopenharmony_ci val = (priv->rx_ring_size * 2) / 3; 10278c2ecf20Sopenharmony_ci enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan)); 10288c2ecf20Sopenharmony_ci } else { 10298c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 5, ENETDMAC_FC, priv->rx_chan); 10308c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->rx_ring_size, ENETDMAC_LEN, priv->rx_chan); 10318c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->tx_ring_size, ENETDMAC_LEN, priv->tx_chan); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* all set, enable mac and interrupts, start dma engine and 10358c2ecf20Sopenharmony_ci * kick rx dma channel */ 10368c2ecf20Sopenharmony_ci wmb(); 10378c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_CTL_REG); 10388c2ecf20Sopenharmony_ci val |= ENET_CTL_ENABLE_MASK; 10398c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_CTL_REG); 10408c2ecf20Sopenharmony_ci if (priv->dma_has_sram) 10418c2ecf20Sopenharmony_ci enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); 10428c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_en_mask, 10438c2ecf20Sopenharmony_ci ENETDMAC_CHANCFG, priv->rx_chan); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* watch "mib counters about to overflow" interrupt */ 10468c2ecf20Sopenharmony_ci enet_writel(priv, ENET_IR_MIB, ENET_IR_REG); 10478c2ecf20Sopenharmony_ci enet_writel(priv, ENET_IR_MIB, ENET_IRMASK_REG); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* watch "packet transferred" interrupt in rx and tx */ 10508c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 10518c2ecf20Sopenharmony_ci ENETDMAC_IR, priv->rx_chan); 10528c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 10538c2ecf20Sopenharmony_ci ENETDMAC_IR, priv->tx_chan); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* make sure we enable napi before rx interrupt */ 10568c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 10598c2ecf20Sopenharmony_ci ENETDMAC_IRMASK, priv->rx_chan); 10608c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_chan_int_mask, 10618c2ecf20Sopenharmony_ci ENETDMAC_IRMASK, priv->tx_chan); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (phydev) 10648c2ecf20Sopenharmony_ci phy_start(phydev); 10658c2ecf20Sopenharmony_ci else 10668c2ecf20Sopenharmony_ci bcm_enet_adjust_link(dev); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci netif_start_queue(dev); 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ciout: 10728c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 10738c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (!priv->rx_skb[i]) 10768c2ecf20Sopenharmony_ci continue; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci desc = &priv->rx_desc_cpu[i]; 10798c2ecf20Sopenharmony_ci dma_unmap_single(kdev, desc->address, priv->rx_skb_size, 10808c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 10818c2ecf20Sopenharmony_ci kfree_skb(priv->rx_skb[i]); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci kfree(priv->rx_skb); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ciout_free_tx_skb: 10868c2ecf20Sopenharmony_ci kfree(priv->tx_skb); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ciout_free_tx_ring: 10898c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->tx_desc_alloc_size, 10908c2ecf20Sopenharmony_ci priv->tx_desc_cpu, priv->tx_desc_dma); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ciout_free_rx_ring: 10938c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->rx_desc_alloc_size, 10948c2ecf20Sopenharmony_ci priv->rx_desc_cpu, priv->rx_desc_dma); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ciout_freeirq_tx: 10978c2ecf20Sopenharmony_ci free_irq(priv->irq_tx, dev); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ciout_freeirq_rx: 11008c2ecf20Sopenharmony_ci free_irq(priv->irq_rx, dev); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ciout_freeirq: 11038c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ciout_phy_disconnect: 11068c2ecf20Sopenharmony_ci if (phydev) 11078c2ecf20Sopenharmony_ci phy_disconnect(phydev); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci return ret; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci/* 11138c2ecf20Sopenharmony_ci * disable mac 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_cistatic void bcm_enet_disable_mac(struct bcm_enet_priv *priv) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci int limit; 11188c2ecf20Sopenharmony_ci u32 val; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_CTL_REG); 11218c2ecf20Sopenharmony_ci val |= ENET_CTL_DISABLE_MASK; 11228c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_CTL_REG); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci limit = 1000; 11258c2ecf20Sopenharmony_ci do { 11268c2ecf20Sopenharmony_ci u32 val; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_CTL_REG); 11298c2ecf20Sopenharmony_ci if (!(val & ENET_CTL_DISABLE_MASK)) 11308c2ecf20Sopenharmony_ci break; 11318c2ecf20Sopenharmony_ci udelay(1); 11328c2ecf20Sopenharmony_ci } while (limit--); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* 11368c2ecf20Sopenharmony_ci * disable dma in given channel 11378c2ecf20Sopenharmony_ci */ 11388c2ecf20Sopenharmony_cistatic void bcm_enet_disable_dma(struct bcm_enet_priv *priv, int chan) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci int limit; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_CHANCFG, chan); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci limit = 1000; 11458c2ecf20Sopenharmony_ci do { 11468c2ecf20Sopenharmony_ci u32 val; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci val = enet_dmac_readl(priv, ENETDMAC_CHANCFG, chan); 11498c2ecf20Sopenharmony_ci if (!(val & ENETDMAC_CHANCFG_EN_MASK)) 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci udelay(1); 11528c2ecf20Sopenharmony_ci } while (limit--); 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci/* 11568c2ecf20Sopenharmony_ci * stop callback 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_cistatic int bcm_enet_stop(struct net_device *dev) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 11618c2ecf20Sopenharmony_ci struct device *kdev; 11628c2ecf20Sopenharmony_ci int i; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 11658c2ecf20Sopenharmony_ci kdev = &priv->pdev->dev; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci netif_stop_queue(dev); 11688c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 11698c2ecf20Sopenharmony_ci if (priv->has_phy) 11708c2ecf20Sopenharmony_ci phy_stop(dev->phydev); 11718c2ecf20Sopenharmony_ci del_timer_sync(&priv->rx_timeout); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* mask all interrupts */ 11748c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_IRMASK_REG); 11758c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); 11768c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* make sure no mib update is scheduled */ 11798c2ecf20Sopenharmony_ci cancel_work_sync(&priv->mib_update_task); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* disable dma & mac */ 11828c2ecf20Sopenharmony_ci bcm_enet_disable_dma(priv, priv->tx_chan); 11838c2ecf20Sopenharmony_ci bcm_enet_disable_dma(priv, priv->rx_chan); 11848c2ecf20Sopenharmony_ci bcm_enet_disable_mac(priv); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* force reclaim of all tx buffers */ 11878c2ecf20Sopenharmony_ci bcm_enet_tx_reclaim(dev, 1); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* free the rx skb ring */ 11908c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 11918c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (!priv->rx_skb[i]) 11948c2ecf20Sopenharmony_ci continue; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci desc = &priv->rx_desc_cpu[i]; 11978c2ecf20Sopenharmony_ci dma_unmap_single(kdev, desc->address, priv->rx_skb_size, 11988c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 11998c2ecf20Sopenharmony_ci kfree_skb(priv->rx_skb[i]); 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* free remaining allocated memory */ 12038c2ecf20Sopenharmony_ci kfree(priv->rx_skb); 12048c2ecf20Sopenharmony_ci kfree(priv->tx_skb); 12058c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->rx_desc_alloc_size, 12068c2ecf20Sopenharmony_ci priv->rx_desc_cpu, priv->rx_desc_dma); 12078c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->tx_desc_alloc_size, 12088c2ecf20Sopenharmony_ci priv->tx_desc_cpu, priv->tx_desc_dma); 12098c2ecf20Sopenharmony_ci free_irq(priv->irq_tx, dev); 12108c2ecf20Sopenharmony_ci free_irq(priv->irq_rx, dev); 12118c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* release phy */ 12148c2ecf20Sopenharmony_ci if (priv->has_phy) 12158c2ecf20Sopenharmony_ci phy_disconnect(dev->phydev); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci return 0; 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci/* 12218c2ecf20Sopenharmony_ci * ethtool callbacks 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_cistruct bcm_enet_stats { 12248c2ecf20Sopenharmony_ci char stat_string[ETH_GSTRING_LEN]; 12258c2ecf20Sopenharmony_ci int sizeof_stat; 12268c2ecf20Sopenharmony_ci int stat_offset; 12278c2ecf20Sopenharmony_ci int mib_reg; 12288c2ecf20Sopenharmony_ci}; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci#define GEN_STAT(m) sizeof(((struct bcm_enet_priv *)0)->m), \ 12318c2ecf20Sopenharmony_ci offsetof(struct bcm_enet_priv, m) 12328c2ecf20Sopenharmony_ci#define DEV_STAT(m) sizeof(((struct net_device_stats *)0)->m), \ 12338c2ecf20Sopenharmony_ci offsetof(struct net_device_stats, m) 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic const struct bcm_enet_stats bcm_enet_gstrings_stats[] = { 12368c2ecf20Sopenharmony_ci { "rx_packets", DEV_STAT(rx_packets), -1 }, 12378c2ecf20Sopenharmony_ci { "tx_packets", DEV_STAT(tx_packets), -1 }, 12388c2ecf20Sopenharmony_ci { "rx_bytes", DEV_STAT(rx_bytes), -1 }, 12398c2ecf20Sopenharmony_ci { "tx_bytes", DEV_STAT(tx_bytes), -1 }, 12408c2ecf20Sopenharmony_ci { "rx_errors", DEV_STAT(rx_errors), -1 }, 12418c2ecf20Sopenharmony_ci { "tx_errors", DEV_STAT(tx_errors), -1 }, 12428c2ecf20Sopenharmony_ci { "rx_dropped", DEV_STAT(rx_dropped), -1 }, 12438c2ecf20Sopenharmony_ci { "tx_dropped", DEV_STAT(tx_dropped), -1 }, 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci { "rx_good_octets", GEN_STAT(mib.rx_gd_octets), ETH_MIB_RX_GD_OCTETS}, 12468c2ecf20Sopenharmony_ci { "rx_good_pkts", GEN_STAT(mib.rx_gd_pkts), ETH_MIB_RX_GD_PKTS }, 12478c2ecf20Sopenharmony_ci { "rx_broadcast", GEN_STAT(mib.rx_brdcast), ETH_MIB_RX_BRDCAST }, 12488c2ecf20Sopenharmony_ci { "rx_multicast", GEN_STAT(mib.rx_mult), ETH_MIB_RX_MULT }, 12498c2ecf20Sopenharmony_ci { "rx_64_octets", GEN_STAT(mib.rx_64), ETH_MIB_RX_64 }, 12508c2ecf20Sopenharmony_ci { "rx_65_127_oct", GEN_STAT(mib.rx_65_127), ETH_MIB_RX_65_127 }, 12518c2ecf20Sopenharmony_ci { "rx_128_255_oct", GEN_STAT(mib.rx_128_255), ETH_MIB_RX_128_255 }, 12528c2ecf20Sopenharmony_ci { "rx_256_511_oct", GEN_STAT(mib.rx_256_511), ETH_MIB_RX_256_511 }, 12538c2ecf20Sopenharmony_ci { "rx_512_1023_oct", GEN_STAT(mib.rx_512_1023), ETH_MIB_RX_512_1023 }, 12548c2ecf20Sopenharmony_ci { "rx_1024_max_oct", GEN_STAT(mib.rx_1024_max), ETH_MIB_RX_1024_MAX }, 12558c2ecf20Sopenharmony_ci { "rx_jabber", GEN_STAT(mib.rx_jab), ETH_MIB_RX_JAB }, 12568c2ecf20Sopenharmony_ci { "rx_oversize", GEN_STAT(mib.rx_ovr), ETH_MIB_RX_OVR }, 12578c2ecf20Sopenharmony_ci { "rx_fragment", GEN_STAT(mib.rx_frag), ETH_MIB_RX_FRAG }, 12588c2ecf20Sopenharmony_ci { "rx_dropped", GEN_STAT(mib.rx_drop), ETH_MIB_RX_DROP }, 12598c2ecf20Sopenharmony_ci { "rx_crc_align", GEN_STAT(mib.rx_crc_align), ETH_MIB_RX_CRC_ALIGN }, 12608c2ecf20Sopenharmony_ci { "rx_undersize", GEN_STAT(mib.rx_und), ETH_MIB_RX_UND }, 12618c2ecf20Sopenharmony_ci { "rx_crc", GEN_STAT(mib.rx_crc), ETH_MIB_RX_CRC }, 12628c2ecf20Sopenharmony_ci { "rx_align", GEN_STAT(mib.rx_align), ETH_MIB_RX_ALIGN }, 12638c2ecf20Sopenharmony_ci { "rx_symbol_error", GEN_STAT(mib.rx_sym), ETH_MIB_RX_SYM }, 12648c2ecf20Sopenharmony_ci { "rx_pause", GEN_STAT(mib.rx_pause), ETH_MIB_RX_PAUSE }, 12658c2ecf20Sopenharmony_ci { "rx_control", GEN_STAT(mib.rx_cntrl), ETH_MIB_RX_CNTRL }, 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci { "tx_good_octets", GEN_STAT(mib.tx_gd_octets), ETH_MIB_TX_GD_OCTETS }, 12688c2ecf20Sopenharmony_ci { "tx_good_pkts", GEN_STAT(mib.tx_gd_pkts), ETH_MIB_TX_GD_PKTS }, 12698c2ecf20Sopenharmony_ci { "tx_broadcast", GEN_STAT(mib.tx_brdcast), ETH_MIB_TX_BRDCAST }, 12708c2ecf20Sopenharmony_ci { "tx_multicast", GEN_STAT(mib.tx_mult), ETH_MIB_TX_MULT }, 12718c2ecf20Sopenharmony_ci { "tx_64_oct", GEN_STAT(mib.tx_64), ETH_MIB_TX_64 }, 12728c2ecf20Sopenharmony_ci { "tx_65_127_oct", GEN_STAT(mib.tx_65_127), ETH_MIB_TX_65_127 }, 12738c2ecf20Sopenharmony_ci { "tx_128_255_oct", GEN_STAT(mib.tx_128_255), ETH_MIB_TX_128_255 }, 12748c2ecf20Sopenharmony_ci { "tx_256_511_oct", GEN_STAT(mib.tx_256_511), ETH_MIB_TX_256_511 }, 12758c2ecf20Sopenharmony_ci { "tx_512_1023_oct", GEN_STAT(mib.tx_512_1023), ETH_MIB_TX_512_1023}, 12768c2ecf20Sopenharmony_ci { "tx_1024_max_oct", GEN_STAT(mib.tx_1024_max), ETH_MIB_TX_1024_MAX }, 12778c2ecf20Sopenharmony_ci { "tx_jabber", GEN_STAT(mib.tx_jab), ETH_MIB_TX_JAB }, 12788c2ecf20Sopenharmony_ci { "tx_oversize", GEN_STAT(mib.tx_ovr), ETH_MIB_TX_OVR }, 12798c2ecf20Sopenharmony_ci { "tx_fragment", GEN_STAT(mib.tx_frag), ETH_MIB_TX_FRAG }, 12808c2ecf20Sopenharmony_ci { "tx_underrun", GEN_STAT(mib.tx_underrun), ETH_MIB_TX_UNDERRUN }, 12818c2ecf20Sopenharmony_ci { "tx_collisions", GEN_STAT(mib.tx_col), ETH_MIB_TX_COL }, 12828c2ecf20Sopenharmony_ci { "tx_single_collision", GEN_STAT(mib.tx_1_col), ETH_MIB_TX_1_COL }, 12838c2ecf20Sopenharmony_ci { "tx_multiple_collision", GEN_STAT(mib.tx_m_col), ETH_MIB_TX_M_COL }, 12848c2ecf20Sopenharmony_ci { "tx_excess_collision", GEN_STAT(mib.tx_ex_col), ETH_MIB_TX_EX_COL }, 12858c2ecf20Sopenharmony_ci { "tx_late_collision", GEN_STAT(mib.tx_late), ETH_MIB_TX_LATE }, 12868c2ecf20Sopenharmony_ci { "tx_deferred", GEN_STAT(mib.tx_def), ETH_MIB_TX_DEF }, 12878c2ecf20Sopenharmony_ci { "tx_carrier_sense", GEN_STAT(mib.tx_crs), ETH_MIB_TX_CRS }, 12888c2ecf20Sopenharmony_ci { "tx_pause", GEN_STAT(mib.tx_pause), ETH_MIB_TX_PAUSE }, 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci}; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci#define BCM_ENET_STATS_LEN ARRAY_SIZE(bcm_enet_gstrings_stats) 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_cistatic const u32 unused_mib_regs[] = { 12958c2ecf20Sopenharmony_ci ETH_MIB_TX_ALL_OCTETS, 12968c2ecf20Sopenharmony_ci ETH_MIB_TX_ALL_PKTS, 12978c2ecf20Sopenharmony_ci ETH_MIB_RX_ALL_OCTETS, 12988c2ecf20Sopenharmony_ci ETH_MIB_RX_ALL_PKTS, 12998c2ecf20Sopenharmony_ci}; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic void bcm_enet_get_drvinfo(struct net_device *netdev, 13038c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, bcm_enet_driver_name, sizeof(drvinfo->driver)); 13068c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int bcm_enet_get_sset_count(struct net_device *netdev, 13108c2ecf20Sopenharmony_ci int string_set) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci switch (string_set) { 13138c2ecf20Sopenharmony_ci case ETH_SS_STATS: 13148c2ecf20Sopenharmony_ci return BCM_ENET_STATS_LEN; 13158c2ecf20Sopenharmony_ci default: 13168c2ecf20Sopenharmony_ci return -EINVAL; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic void bcm_enet_get_strings(struct net_device *netdev, 13218c2ecf20Sopenharmony_ci u32 stringset, u8 *data) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci int i; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci switch (stringset) { 13268c2ecf20Sopenharmony_ci case ETH_SS_STATS: 13278c2ecf20Sopenharmony_ci for (i = 0; i < BCM_ENET_STATS_LEN; i++) { 13288c2ecf20Sopenharmony_ci memcpy(data + i * ETH_GSTRING_LEN, 13298c2ecf20Sopenharmony_ci bcm_enet_gstrings_stats[i].stat_string, 13308c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci break; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic void update_mib_counters(struct bcm_enet_priv *priv) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci int i; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci for (i = 0; i < BCM_ENET_STATS_LEN; i++) { 13418c2ecf20Sopenharmony_ci const struct bcm_enet_stats *s; 13428c2ecf20Sopenharmony_ci u32 val; 13438c2ecf20Sopenharmony_ci char *p; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci s = &bcm_enet_gstrings_stats[i]; 13468c2ecf20Sopenharmony_ci if (s->mib_reg == -1) 13478c2ecf20Sopenharmony_ci continue; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_MIB_REG(s->mib_reg)); 13508c2ecf20Sopenharmony_ci p = (char *)priv + s->stat_offset; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (s->sizeof_stat == sizeof(u64)) 13538c2ecf20Sopenharmony_ci *(u64 *)p += val; 13548c2ecf20Sopenharmony_ci else 13558c2ecf20Sopenharmony_ci *(u32 *)p += val; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci /* also empty unused mib counters to make sure mib counter 13598c2ecf20Sopenharmony_ci * overflow interrupt is cleared */ 13608c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(unused_mib_regs); i++) 13618c2ecf20Sopenharmony_ci (void)enet_readl(priv, ENET_MIB_REG(unused_mib_regs[i])); 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic void bcm_enet_update_mib_counters_defer(struct work_struct *t) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci priv = container_of(t, struct bcm_enet_priv, mib_update_task); 13698c2ecf20Sopenharmony_ci mutex_lock(&priv->mib_update_lock); 13708c2ecf20Sopenharmony_ci update_mib_counters(priv); 13718c2ecf20Sopenharmony_ci mutex_unlock(&priv->mib_update_lock); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* reenable mib interrupt */ 13748c2ecf20Sopenharmony_ci if (netif_running(priv->net_dev)) 13758c2ecf20Sopenharmony_ci enet_writel(priv, ENET_IR_MIB, ENET_IRMASK_REG); 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic void bcm_enet_get_ethtool_stats(struct net_device *netdev, 13798c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 13808c2ecf20Sopenharmony_ci u64 *data) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 13838c2ecf20Sopenharmony_ci int i; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci priv = netdev_priv(netdev); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci mutex_lock(&priv->mib_update_lock); 13888c2ecf20Sopenharmony_ci update_mib_counters(priv); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci for (i = 0; i < BCM_ENET_STATS_LEN; i++) { 13918c2ecf20Sopenharmony_ci const struct bcm_enet_stats *s; 13928c2ecf20Sopenharmony_ci char *p; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci s = &bcm_enet_gstrings_stats[i]; 13958c2ecf20Sopenharmony_ci if (s->mib_reg == -1) 13968c2ecf20Sopenharmony_ci p = (char *)&netdev->stats; 13978c2ecf20Sopenharmony_ci else 13988c2ecf20Sopenharmony_ci p = (char *)priv; 13998c2ecf20Sopenharmony_ci p += s->stat_offset; 14008c2ecf20Sopenharmony_ci data[i] = (s->sizeof_stat == sizeof(u64)) ? 14018c2ecf20Sopenharmony_ci *(u64 *)p : *(u32 *)p; 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci mutex_unlock(&priv->mib_update_lock); 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic int bcm_enet_nway_reset(struct net_device *dev) 14078c2ecf20Sopenharmony_ci{ 14088c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 14118c2ecf20Sopenharmony_ci if (priv->has_phy) 14128c2ecf20Sopenharmony_ci return phy_ethtool_nway_reset(dev); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cistatic int bcm_enet_get_link_ksettings(struct net_device *dev, 14188c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 14218c2ecf20Sopenharmony_ci u32 supported, advertising; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (priv->has_phy) { 14268c2ecf20Sopenharmony_ci if (!dev->phydev) 14278c2ecf20Sopenharmony_ci return -ENODEV; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(dev->phydev, cmd); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci return 0; 14328c2ecf20Sopenharmony_ci } else { 14338c2ecf20Sopenharmony_ci cmd->base.autoneg = 0; 14348c2ecf20Sopenharmony_ci cmd->base.speed = (priv->force_speed_100) ? 14358c2ecf20Sopenharmony_ci SPEED_100 : SPEED_10; 14368c2ecf20Sopenharmony_ci cmd->base.duplex = (priv->force_duplex_full) ? 14378c2ecf20Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 14388c2ecf20Sopenharmony_ci supported = ADVERTISED_10baseT_Half | 14398c2ecf20Sopenharmony_ci ADVERTISED_10baseT_Full | 14408c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Half | 14418c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Full; 14428c2ecf20Sopenharmony_ci advertising = 0; 14438c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode( 14448c2ecf20Sopenharmony_ci cmd->link_modes.supported, supported); 14458c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode( 14468c2ecf20Sopenharmony_ci cmd->link_modes.advertising, advertising); 14478c2ecf20Sopenharmony_ci cmd->base.port = PORT_MII; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci return 0; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic int bcm_enet_set_link_ksettings(struct net_device *dev, 14538c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 14588c2ecf20Sopenharmony_ci if (priv->has_phy) { 14598c2ecf20Sopenharmony_ci if (!dev->phydev) 14608c2ecf20Sopenharmony_ci return -ENODEV; 14618c2ecf20Sopenharmony_ci return phy_ethtool_ksettings_set(dev->phydev, cmd); 14628c2ecf20Sopenharmony_ci } else { 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (cmd->base.autoneg || 14658c2ecf20Sopenharmony_ci (cmd->base.speed != SPEED_100 && 14668c2ecf20Sopenharmony_ci cmd->base.speed != SPEED_10) || 14678c2ecf20Sopenharmony_ci cmd->base.port != PORT_MII) 14688c2ecf20Sopenharmony_ci return -EINVAL; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci priv->force_speed_100 = 14718c2ecf20Sopenharmony_ci (cmd->base.speed == SPEED_100) ? 1 : 0; 14728c2ecf20Sopenharmony_ci priv->force_duplex_full = 14738c2ecf20Sopenharmony_ci (cmd->base.duplex == DUPLEX_FULL) ? 1 : 0; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci if (netif_running(dev)) 14768c2ecf20Sopenharmony_ci bcm_enet_adjust_link(dev); 14778c2ecf20Sopenharmony_ci return 0; 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic void bcm_enet_get_ringparam(struct net_device *dev, 14828c2ecf20Sopenharmony_ci struct ethtool_ringparam *ering) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* rx/tx ring is actually only limited by memory */ 14898c2ecf20Sopenharmony_ci ering->rx_max_pending = 8192; 14908c2ecf20Sopenharmony_ci ering->tx_max_pending = 8192; 14918c2ecf20Sopenharmony_ci ering->rx_pending = priv->rx_ring_size; 14928c2ecf20Sopenharmony_ci ering->tx_pending = priv->tx_ring_size; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic int bcm_enet_set_ringparam(struct net_device *dev, 14968c2ecf20Sopenharmony_ci struct ethtool_ringparam *ering) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 14998c2ecf20Sopenharmony_ci int was_running; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci was_running = 0; 15048c2ecf20Sopenharmony_ci if (netif_running(dev)) { 15058c2ecf20Sopenharmony_ci bcm_enet_stop(dev); 15068c2ecf20Sopenharmony_ci was_running = 1; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci priv->rx_ring_size = ering->rx_pending; 15108c2ecf20Sopenharmony_ci priv->tx_ring_size = ering->tx_pending; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (was_running) { 15138c2ecf20Sopenharmony_ci int err; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci err = bcm_enet_open(dev); 15168c2ecf20Sopenharmony_ci if (err) 15178c2ecf20Sopenharmony_ci dev_close(dev); 15188c2ecf20Sopenharmony_ci else 15198c2ecf20Sopenharmony_ci bcm_enet_set_multicast_list(dev); 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci return 0; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic void bcm_enet_get_pauseparam(struct net_device *dev, 15258c2ecf20Sopenharmony_ci struct ethtool_pauseparam *ecmd) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 15308c2ecf20Sopenharmony_ci ecmd->autoneg = priv->pause_auto; 15318c2ecf20Sopenharmony_ci ecmd->rx_pause = priv->pause_rx; 15328c2ecf20Sopenharmony_ci ecmd->tx_pause = priv->pause_tx; 15338c2ecf20Sopenharmony_ci} 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_cistatic int bcm_enet_set_pauseparam(struct net_device *dev, 15368c2ecf20Sopenharmony_ci struct ethtool_pauseparam *ecmd) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if (priv->has_phy) { 15438c2ecf20Sopenharmony_ci if (ecmd->autoneg && (ecmd->rx_pause != ecmd->tx_pause)) { 15448c2ecf20Sopenharmony_ci /* asymetric pause mode not supported, 15458c2ecf20Sopenharmony_ci * actually possible but integrated PHY has RO 15468c2ecf20Sopenharmony_ci * asym_pause bit */ 15478c2ecf20Sopenharmony_ci return -EINVAL; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci } else { 15508c2ecf20Sopenharmony_ci /* no pause autoneg on direct mii connection */ 15518c2ecf20Sopenharmony_ci if (ecmd->autoneg) 15528c2ecf20Sopenharmony_ci return -EINVAL; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci priv->pause_auto = ecmd->autoneg; 15568c2ecf20Sopenharmony_ci priv->pause_rx = ecmd->rx_pause; 15578c2ecf20Sopenharmony_ci priv->pause_tx = ecmd->tx_pause; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci return 0; 15608c2ecf20Sopenharmony_ci} 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic const struct ethtool_ops bcm_enet_ethtool_ops = { 15638c2ecf20Sopenharmony_ci .get_strings = bcm_enet_get_strings, 15648c2ecf20Sopenharmony_ci .get_sset_count = bcm_enet_get_sset_count, 15658c2ecf20Sopenharmony_ci .get_ethtool_stats = bcm_enet_get_ethtool_stats, 15668c2ecf20Sopenharmony_ci .nway_reset = bcm_enet_nway_reset, 15678c2ecf20Sopenharmony_ci .get_drvinfo = bcm_enet_get_drvinfo, 15688c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 15698c2ecf20Sopenharmony_ci .get_ringparam = bcm_enet_get_ringparam, 15708c2ecf20Sopenharmony_ci .set_ringparam = bcm_enet_set_ringparam, 15718c2ecf20Sopenharmony_ci .get_pauseparam = bcm_enet_get_pauseparam, 15728c2ecf20Sopenharmony_ci .set_pauseparam = bcm_enet_set_pauseparam, 15738c2ecf20Sopenharmony_ci .get_link_ksettings = bcm_enet_get_link_ksettings, 15748c2ecf20Sopenharmony_ci .set_link_ksettings = bcm_enet_set_link_ksettings, 15758c2ecf20Sopenharmony_ci}; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_cistatic int bcm_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 15828c2ecf20Sopenharmony_ci if (priv->has_phy) { 15838c2ecf20Sopenharmony_ci if (!dev->phydev) 15848c2ecf20Sopenharmony_ci return -ENODEV; 15858c2ecf20Sopenharmony_ci return phy_mii_ioctl(dev->phydev, rq, cmd); 15868c2ecf20Sopenharmony_ci } else { 15878c2ecf20Sopenharmony_ci struct mii_if_info mii; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci mii.dev = dev; 15908c2ecf20Sopenharmony_ci mii.mdio_read = bcm_enet_mdio_read_mii; 15918c2ecf20Sopenharmony_ci mii.mdio_write = bcm_enet_mdio_write_mii; 15928c2ecf20Sopenharmony_ci mii.phy_id = 0; 15938c2ecf20Sopenharmony_ci mii.phy_id_mask = 0x3f; 15948c2ecf20Sopenharmony_ci mii.reg_num_mask = 0x1f; 15958c2ecf20Sopenharmony_ci return generic_mii_ioctl(&mii, if_mii(rq), cmd, NULL); 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci/* 16008c2ecf20Sopenharmony_ci * adjust mtu, can't be called while device is running 16018c2ecf20Sopenharmony_ci */ 16028c2ecf20Sopenharmony_cistatic int bcm_enet_change_mtu(struct net_device *dev, int new_mtu) 16038c2ecf20Sopenharmony_ci{ 16048c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv = netdev_priv(dev); 16058c2ecf20Sopenharmony_ci int actual_mtu = new_mtu; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (netif_running(dev)) 16088c2ecf20Sopenharmony_ci return -EBUSY; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci /* add ethernet header + vlan tag size */ 16118c2ecf20Sopenharmony_ci actual_mtu += VLAN_ETH_HLEN; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci /* 16148c2ecf20Sopenharmony_ci * setup maximum size before we get overflow mark in 16158c2ecf20Sopenharmony_ci * descriptor, note that this will not prevent reception of 16168c2ecf20Sopenharmony_ci * big frames, they will be split into multiple buffers 16178c2ecf20Sopenharmony_ci * anyway 16188c2ecf20Sopenharmony_ci */ 16198c2ecf20Sopenharmony_ci priv->hw_mtu = actual_mtu; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci /* 16228c2ecf20Sopenharmony_ci * align rx buffer size to dma burst len, account FCS since 16238c2ecf20Sopenharmony_ci * it's appended 16248c2ecf20Sopenharmony_ci */ 16258c2ecf20Sopenharmony_ci priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN, 16268c2ecf20Sopenharmony_ci priv->dma_maxburst * 4); 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 16298c2ecf20Sopenharmony_ci return 0; 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci/* 16338c2ecf20Sopenharmony_ci * preinit hardware to allow mii operation while device is down 16348c2ecf20Sopenharmony_ci */ 16358c2ecf20Sopenharmony_cistatic void bcm_enet_hw_preinit(struct bcm_enet_priv *priv) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci u32 val; 16388c2ecf20Sopenharmony_ci int limit; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* make sure mac is disabled */ 16418c2ecf20Sopenharmony_ci bcm_enet_disable_mac(priv); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci /* soft reset mac */ 16448c2ecf20Sopenharmony_ci val = ENET_CTL_SRESET_MASK; 16458c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_CTL_REG); 16468c2ecf20Sopenharmony_ci wmb(); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci limit = 1000; 16498c2ecf20Sopenharmony_ci do { 16508c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_CTL_REG); 16518c2ecf20Sopenharmony_ci if (!(val & ENET_CTL_SRESET_MASK)) 16528c2ecf20Sopenharmony_ci break; 16538c2ecf20Sopenharmony_ci udelay(1); 16548c2ecf20Sopenharmony_ci } while (limit--); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* select correct mii interface */ 16578c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_CTL_REG); 16588c2ecf20Sopenharmony_ci if (priv->use_external_mii) 16598c2ecf20Sopenharmony_ci val |= ENET_CTL_EPHYSEL_MASK; 16608c2ecf20Sopenharmony_ci else 16618c2ecf20Sopenharmony_ci val &= ~ENET_CTL_EPHYSEL_MASK; 16628c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_CTL_REG); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci /* turn on mdc clock */ 16658c2ecf20Sopenharmony_ci enet_writel(priv, (0x1f << ENET_MIISC_MDCFREQDIV_SHIFT) | 16668c2ecf20Sopenharmony_ci ENET_MIISC_PREAMBLEEN_MASK, ENET_MIISC_REG); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* set mib counters to self-clear when read */ 16698c2ecf20Sopenharmony_ci val = enet_readl(priv, ENET_MIBCTL_REG); 16708c2ecf20Sopenharmony_ci val |= ENET_MIBCTL_RDCLEAR_MASK; 16718c2ecf20Sopenharmony_ci enet_writel(priv, val, ENET_MIBCTL_REG); 16728c2ecf20Sopenharmony_ci} 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_cistatic const struct net_device_ops bcm_enet_ops = { 16758c2ecf20Sopenharmony_ci .ndo_open = bcm_enet_open, 16768c2ecf20Sopenharmony_ci .ndo_stop = bcm_enet_stop, 16778c2ecf20Sopenharmony_ci .ndo_start_xmit = bcm_enet_start_xmit, 16788c2ecf20Sopenharmony_ci .ndo_set_mac_address = bcm_enet_set_mac_address, 16798c2ecf20Sopenharmony_ci .ndo_set_rx_mode = bcm_enet_set_multicast_list, 16808c2ecf20Sopenharmony_ci .ndo_do_ioctl = bcm_enet_ioctl, 16818c2ecf20Sopenharmony_ci .ndo_change_mtu = bcm_enet_change_mtu, 16828c2ecf20Sopenharmony_ci}; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci/* 16858c2ecf20Sopenharmony_ci * allocate netdevice, request register memory and register device. 16868c2ecf20Sopenharmony_ci */ 16878c2ecf20Sopenharmony_cistatic int bcm_enet_probe(struct platform_device *pdev) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 16908c2ecf20Sopenharmony_ci struct net_device *dev; 16918c2ecf20Sopenharmony_ci struct bcm63xx_enet_platform_data *pd; 16928c2ecf20Sopenharmony_ci struct resource *res_irq, *res_irq_rx, *res_irq_tx; 16938c2ecf20Sopenharmony_ci struct mii_bus *bus; 16948c2ecf20Sopenharmony_ci int i, ret; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci if (!bcm_enet_shared_base[0]) 16978c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 17008c2ecf20Sopenharmony_ci res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, 1); 17018c2ecf20Sopenharmony_ci res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, 2); 17028c2ecf20Sopenharmony_ci if (!res_irq || !res_irq_rx || !res_irq_tx) 17038c2ecf20Sopenharmony_ci return -ENODEV; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*priv)); 17068c2ecf20Sopenharmony_ci if (!dev) 17078c2ecf20Sopenharmony_ci return -ENOMEM; 17088c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci priv->enet_is_sw = false; 17118c2ecf20Sopenharmony_ci priv->dma_maxburst = BCMENET_DMA_MAXBURST; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci ret = bcm_enet_change_mtu(dev, dev->mtu); 17148c2ecf20Sopenharmony_ci if (ret) 17158c2ecf20Sopenharmony_ci goto out; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci priv->base = devm_platform_ioremap_resource(pdev, 0); 17188c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) { 17198c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->base); 17208c2ecf20Sopenharmony_ci goto out; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci dev->irq = priv->irq = res_irq->start; 17248c2ecf20Sopenharmony_ci priv->irq_rx = res_irq_rx->start; 17258c2ecf20Sopenharmony_ci priv->irq_tx = res_irq_tx->start; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci priv->mac_clk = devm_clk_get(&pdev->dev, "enet"); 17288c2ecf20Sopenharmony_ci if (IS_ERR(priv->mac_clk)) { 17298c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->mac_clk); 17308c2ecf20Sopenharmony_ci goto out; 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->mac_clk); 17338c2ecf20Sopenharmony_ci if (ret) 17348c2ecf20Sopenharmony_ci goto out; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* initialize default and fetch platform data */ 17378c2ecf20Sopenharmony_ci priv->rx_ring_size = BCMENET_DEF_RX_DESC; 17388c2ecf20Sopenharmony_ci priv->tx_ring_size = BCMENET_DEF_TX_DESC; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci pd = dev_get_platdata(&pdev->dev); 17418c2ecf20Sopenharmony_ci if (pd) { 17428c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); 17438c2ecf20Sopenharmony_ci priv->has_phy = pd->has_phy; 17448c2ecf20Sopenharmony_ci priv->phy_id = pd->phy_id; 17458c2ecf20Sopenharmony_ci priv->has_phy_interrupt = pd->has_phy_interrupt; 17468c2ecf20Sopenharmony_ci priv->phy_interrupt = pd->phy_interrupt; 17478c2ecf20Sopenharmony_ci priv->use_external_mii = !pd->use_internal_phy; 17488c2ecf20Sopenharmony_ci priv->pause_auto = pd->pause_auto; 17498c2ecf20Sopenharmony_ci priv->pause_rx = pd->pause_rx; 17508c2ecf20Sopenharmony_ci priv->pause_tx = pd->pause_tx; 17518c2ecf20Sopenharmony_ci priv->force_duplex_full = pd->force_duplex_full; 17528c2ecf20Sopenharmony_ci priv->force_speed_100 = pd->force_speed_100; 17538c2ecf20Sopenharmony_ci priv->dma_chan_en_mask = pd->dma_chan_en_mask; 17548c2ecf20Sopenharmony_ci priv->dma_chan_int_mask = pd->dma_chan_int_mask; 17558c2ecf20Sopenharmony_ci priv->dma_chan_width = pd->dma_chan_width; 17568c2ecf20Sopenharmony_ci priv->dma_has_sram = pd->dma_has_sram; 17578c2ecf20Sopenharmony_ci priv->dma_desc_shift = pd->dma_desc_shift; 17588c2ecf20Sopenharmony_ci priv->rx_chan = pd->rx_chan; 17598c2ecf20Sopenharmony_ci priv->tx_chan = pd->tx_chan; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (priv->has_phy && !priv->use_external_mii) { 17638c2ecf20Sopenharmony_ci /* using internal PHY, enable clock */ 17648c2ecf20Sopenharmony_ci priv->phy_clk = devm_clk_get(&pdev->dev, "ephy"); 17658c2ecf20Sopenharmony_ci if (IS_ERR(priv->phy_clk)) { 17668c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->phy_clk); 17678c2ecf20Sopenharmony_ci priv->phy_clk = NULL; 17688c2ecf20Sopenharmony_ci goto out_disable_clk_mac; 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->phy_clk); 17718c2ecf20Sopenharmony_ci if (ret) 17728c2ecf20Sopenharmony_ci goto out_disable_clk_mac; 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci /* do minimal hardware init to be able to probe mii bus */ 17768c2ecf20Sopenharmony_ci bcm_enet_hw_preinit(priv); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* MII bus registration */ 17798c2ecf20Sopenharmony_ci if (priv->has_phy) { 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci priv->mii_bus = mdiobus_alloc(); 17828c2ecf20Sopenharmony_ci if (!priv->mii_bus) { 17838c2ecf20Sopenharmony_ci ret = -ENOMEM; 17848c2ecf20Sopenharmony_ci goto out_uninit_hw; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci bus = priv->mii_bus; 17888c2ecf20Sopenharmony_ci bus->name = "bcm63xx_enet MII bus"; 17898c2ecf20Sopenharmony_ci bus->parent = &pdev->dev; 17908c2ecf20Sopenharmony_ci bus->priv = priv; 17918c2ecf20Sopenharmony_ci bus->read = bcm_enet_mdio_read_phylib; 17928c2ecf20Sopenharmony_ci bus->write = bcm_enet_mdio_write_phylib; 17938c2ecf20Sopenharmony_ci sprintf(bus->id, "%s-%d", pdev->name, pdev->id); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci /* only probe bus where we think the PHY is, because 17968c2ecf20Sopenharmony_ci * the mdio read operation return 0 instead of 0xffff 17978c2ecf20Sopenharmony_ci * if a slave is not present on hw */ 17988c2ecf20Sopenharmony_ci bus->phy_mask = ~(1 << priv->phy_id); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (priv->has_phy_interrupt) 18018c2ecf20Sopenharmony_ci bus->irq[priv->phy_id] = priv->phy_interrupt; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci ret = mdiobus_register(bus); 18048c2ecf20Sopenharmony_ci if (ret) { 18058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to register mdio bus\n"); 18068c2ecf20Sopenharmony_ci goto out_free_mdio; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci } else { 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* run platform code to initialize PHY device */ 18118c2ecf20Sopenharmony_ci if (pd && pd->mii_config && 18128c2ecf20Sopenharmony_ci pd->mii_config(dev, 1, bcm_enet_mdio_read_mii, 18138c2ecf20Sopenharmony_ci bcm_enet_mdio_write_mii)) { 18148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to configure mdio bus\n"); 18158c2ecf20Sopenharmony_ci goto out_uninit_hw; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci spin_lock_init(&priv->rx_lock); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci /* init rx timeout (used for oom) */ 18228c2ecf20Sopenharmony_ci timer_setup(&priv->rx_timeout, bcm_enet_refill_rx_timer, 0); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci /* init the mib update lock&work */ 18258c2ecf20Sopenharmony_ci mutex_init(&priv->mib_update_lock); 18268c2ecf20Sopenharmony_ci INIT_WORK(&priv->mib_update_task, bcm_enet_update_mib_counters_defer); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci /* zero mib counters */ 18298c2ecf20Sopenharmony_ci for (i = 0; i < ENET_MIB_REG_COUNT; i++) 18308c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_MIB_REG(i)); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* register netdevice */ 18338c2ecf20Sopenharmony_ci dev->netdev_ops = &bcm_enet_ops; 18348c2ecf20Sopenharmony_ci netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci dev->ethtool_ops = &bcm_enet_ethtool_ops; 18378c2ecf20Sopenharmony_ci /* MTU range: 46 - 2028 */ 18388c2ecf20Sopenharmony_ci dev->min_mtu = ETH_ZLEN - ETH_HLEN; 18398c2ecf20Sopenharmony_ci dev->max_mtu = BCMENET_MAX_MTU - VLAN_ETH_HLEN; 18408c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci ret = register_netdev(dev); 18438c2ecf20Sopenharmony_ci if (ret) 18448c2ecf20Sopenharmony_ci goto out_unregister_mdio; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci netif_carrier_off(dev); 18478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 18488c2ecf20Sopenharmony_ci priv->pdev = pdev; 18498c2ecf20Sopenharmony_ci priv->net_dev = dev; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci return 0; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ciout_unregister_mdio: 18548c2ecf20Sopenharmony_ci if (priv->mii_bus) 18558c2ecf20Sopenharmony_ci mdiobus_unregister(priv->mii_bus); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ciout_free_mdio: 18588c2ecf20Sopenharmony_ci if (priv->mii_bus) 18598c2ecf20Sopenharmony_ci mdiobus_free(priv->mii_bus); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ciout_uninit_hw: 18628c2ecf20Sopenharmony_ci /* turn off mdc clock */ 18638c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_MIISC_REG); 18648c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->phy_clk); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ciout_disable_clk_mac: 18678c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->mac_clk); 18688c2ecf20Sopenharmony_ciout: 18698c2ecf20Sopenharmony_ci free_netdev(dev); 18708c2ecf20Sopenharmony_ci return ret; 18718c2ecf20Sopenharmony_ci} 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci/* 18758c2ecf20Sopenharmony_ci * exit func, stops hardware and unregisters netdevice 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_cistatic int bcm_enet_remove(struct platform_device *pdev) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 18808c2ecf20Sopenharmony_ci struct net_device *dev; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci /* stop netdevice */ 18838c2ecf20Sopenharmony_ci dev = platform_get_drvdata(pdev); 18848c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 18858c2ecf20Sopenharmony_ci unregister_netdev(dev); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci /* turn off mdc clock */ 18888c2ecf20Sopenharmony_ci enet_writel(priv, 0, ENET_MIISC_REG); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (priv->has_phy) { 18918c2ecf20Sopenharmony_ci mdiobus_unregister(priv->mii_bus); 18928c2ecf20Sopenharmony_ci mdiobus_free(priv->mii_bus); 18938c2ecf20Sopenharmony_ci } else { 18948c2ecf20Sopenharmony_ci struct bcm63xx_enet_platform_data *pd; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci pd = dev_get_platdata(&pdev->dev); 18978c2ecf20Sopenharmony_ci if (pd && pd->mii_config) 18988c2ecf20Sopenharmony_ci pd->mii_config(dev, 0, bcm_enet_mdio_read_mii, 18998c2ecf20Sopenharmony_ci bcm_enet_mdio_write_mii); 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci /* disable hw block clocks */ 19038c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->phy_clk); 19048c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->mac_clk); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci free_netdev(dev); 19078c2ecf20Sopenharmony_ci return 0; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistruct platform_driver bcm63xx_enet_driver = { 19118c2ecf20Sopenharmony_ci .probe = bcm_enet_probe, 19128c2ecf20Sopenharmony_ci .remove = bcm_enet_remove, 19138c2ecf20Sopenharmony_ci .driver = { 19148c2ecf20Sopenharmony_ci .name = "bcm63xx_enet", 19158c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 19168c2ecf20Sopenharmony_ci }, 19178c2ecf20Sopenharmony_ci}; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci/* 19208c2ecf20Sopenharmony_ci * switch mii access callbacks 19218c2ecf20Sopenharmony_ci */ 19228c2ecf20Sopenharmony_cistatic int bcmenet_sw_mdio_read(struct bcm_enet_priv *priv, 19238c2ecf20Sopenharmony_ci int ext, int phy_id, int location) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci u32 reg; 19268c2ecf20Sopenharmony_ci int ret; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci spin_lock_bh(&priv->enetsw_mdio_lock); 19298c2ecf20Sopenharmony_ci enetsw_writel(priv, 0, ENETSW_MDIOC_REG); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci reg = ENETSW_MDIOC_RD_MASK | 19328c2ecf20Sopenharmony_ci (phy_id << ENETSW_MDIOC_PHYID_SHIFT) | 19338c2ecf20Sopenharmony_ci (location << ENETSW_MDIOC_REG_SHIFT); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci if (ext) 19368c2ecf20Sopenharmony_ci reg |= ENETSW_MDIOC_EXT_MASK; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci enetsw_writel(priv, reg, ENETSW_MDIOC_REG); 19398c2ecf20Sopenharmony_ci udelay(50); 19408c2ecf20Sopenharmony_ci ret = enetsw_readw(priv, ENETSW_MDIOD_REG); 19418c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->enetsw_mdio_lock); 19428c2ecf20Sopenharmony_ci return ret; 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_cistatic void bcmenet_sw_mdio_write(struct bcm_enet_priv *priv, 19468c2ecf20Sopenharmony_ci int ext, int phy_id, int location, 19478c2ecf20Sopenharmony_ci uint16_t data) 19488c2ecf20Sopenharmony_ci{ 19498c2ecf20Sopenharmony_ci u32 reg; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci spin_lock_bh(&priv->enetsw_mdio_lock); 19528c2ecf20Sopenharmony_ci enetsw_writel(priv, 0, ENETSW_MDIOC_REG); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci reg = ENETSW_MDIOC_WR_MASK | 19558c2ecf20Sopenharmony_ci (phy_id << ENETSW_MDIOC_PHYID_SHIFT) | 19568c2ecf20Sopenharmony_ci (location << ENETSW_MDIOC_REG_SHIFT); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (ext) 19598c2ecf20Sopenharmony_ci reg |= ENETSW_MDIOC_EXT_MASK; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci reg |= data; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci enetsw_writel(priv, reg, ENETSW_MDIOC_REG); 19648c2ecf20Sopenharmony_ci udelay(50); 19658c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->enetsw_mdio_lock); 19668c2ecf20Sopenharmony_ci} 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_cistatic inline int bcm_enet_port_is_rgmii(int portid) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci return portid >= ENETSW_RGMII_PORT0; 19718c2ecf20Sopenharmony_ci} 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci/* 19748c2ecf20Sopenharmony_ci * enet sw PHY polling 19758c2ecf20Sopenharmony_ci */ 19768c2ecf20Sopenharmony_cistatic void swphy_poll_timer(struct timer_list *t) 19778c2ecf20Sopenharmony_ci{ 19788c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv = from_timer(priv, t, swphy_poll); 19798c2ecf20Sopenharmony_ci unsigned int i; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 19828c2ecf20Sopenharmony_ci struct bcm63xx_enetsw_port *port; 19838c2ecf20Sopenharmony_ci int val, j, up, advertise, lpa, speed, duplex, media; 19848c2ecf20Sopenharmony_ci int external_phy = bcm_enet_port_is_rgmii(i); 19858c2ecf20Sopenharmony_ci u8 override; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci port = &priv->used_ports[i]; 19888c2ecf20Sopenharmony_ci if (!port->used) 19898c2ecf20Sopenharmony_ci continue; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci if (port->bypass_link) 19928c2ecf20Sopenharmony_ci continue; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci /* dummy read to clear */ 19958c2ecf20Sopenharmony_ci for (j = 0; j < 2; j++) 19968c2ecf20Sopenharmony_ci val = bcmenet_sw_mdio_read(priv, external_phy, 19978c2ecf20Sopenharmony_ci port->phy_id, MII_BMSR); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci if (val == 0xffff) 20008c2ecf20Sopenharmony_ci continue; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci up = (val & BMSR_LSTATUS) ? 1 : 0; 20038c2ecf20Sopenharmony_ci if (!(up ^ priv->sw_port_link[i])) 20048c2ecf20Sopenharmony_ci continue; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci priv->sw_port_link[i] = up; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* link changed */ 20098c2ecf20Sopenharmony_ci if (!up) { 20108c2ecf20Sopenharmony_ci dev_info(&priv->pdev->dev, "link DOWN on %s\n", 20118c2ecf20Sopenharmony_ci port->name); 20128c2ecf20Sopenharmony_ci enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK, 20138c2ecf20Sopenharmony_ci ENETSW_PORTOV_REG(i)); 20148c2ecf20Sopenharmony_ci enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK | 20158c2ecf20Sopenharmony_ci ENETSW_PTCTRL_TXDIS_MASK, 20168c2ecf20Sopenharmony_ci ENETSW_PTCTRL_REG(i)); 20178c2ecf20Sopenharmony_ci continue; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci advertise = bcmenet_sw_mdio_read(priv, external_phy, 20218c2ecf20Sopenharmony_ci port->phy_id, MII_ADVERTISE); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci lpa = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id, 20248c2ecf20Sopenharmony_ci MII_LPA); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci /* figure out media and duplex from advertise and LPA values */ 20278c2ecf20Sopenharmony_ci media = mii_nway_result(lpa & advertise); 20288c2ecf20Sopenharmony_ci duplex = (media & ADVERTISE_FULL) ? 1 : 0; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) 20318c2ecf20Sopenharmony_ci speed = 100; 20328c2ecf20Sopenharmony_ci else 20338c2ecf20Sopenharmony_ci speed = 10; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci if (val & BMSR_ESTATEN) { 20368c2ecf20Sopenharmony_ci advertise = bcmenet_sw_mdio_read(priv, external_phy, 20378c2ecf20Sopenharmony_ci port->phy_id, MII_CTRL1000); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci lpa = bcmenet_sw_mdio_read(priv, external_phy, 20408c2ecf20Sopenharmony_ci port->phy_id, MII_STAT1000); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (advertise & (ADVERTISE_1000FULL | ADVERTISE_1000HALF) 20438c2ecf20Sopenharmony_ci && lpa & (LPA_1000FULL | LPA_1000HALF)) { 20448c2ecf20Sopenharmony_ci speed = 1000; 20458c2ecf20Sopenharmony_ci duplex = (lpa & LPA_1000FULL); 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci dev_info(&priv->pdev->dev, 20508c2ecf20Sopenharmony_ci "link UP on %s, %dMbps, %s-duplex\n", 20518c2ecf20Sopenharmony_ci port->name, speed, duplex ? "full" : "half"); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci override = ENETSW_PORTOV_ENABLE_MASK | 20548c2ecf20Sopenharmony_ci ENETSW_PORTOV_LINKUP_MASK; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci if (speed == 1000) 20578c2ecf20Sopenharmony_ci override |= ENETSW_IMPOV_1000_MASK; 20588c2ecf20Sopenharmony_ci else if (speed == 100) 20598c2ecf20Sopenharmony_ci override |= ENETSW_IMPOV_100_MASK; 20608c2ecf20Sopenharmony_ci if (duplex) 20618c2ecf20Sopenharmony_ci override |= ENETSW_IMPOV_FDX_MASK; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i)); 20648c2ecf20Sopenharmony_ci enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i)); 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci priv->swphy_poll.expires = jiffies + HZ; 20688c2ecf20Sopenharmony_ci add_timer(&priv->swphy_poll); 20698c2ecf20Sopenharmony_ci} 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci/* 20728c2ecf20Sopenharmony_ci * open callback, allocate dma rings & buffers and start rx operation 20738c2ecf20Sopenharmony_ci */ 20748c2ecf20Sopenharmony_cistatic int bcm_enetsw_open(struct net_device *dev) 20758c2ecf20Sopenharmony_ci{ 20768c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 20778c2ecf20Sopenharmony_ci struct device *kdev; 20788c2ecf20Sopenharmony_ci int i, ret; 20798c2ecf20Sopenharmony_ci unsigned int size; 20808c2ecf20Sopenharmony_ci void *p; 20818c2ecf20Sopenharmony_ci u32 val; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 20848c2ecf20Sopenharmony_ci kdev = &priv->pdev->dev; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci /* mask all interrupts and request them */ 20878c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); 20888c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci ret = request_irq(priv->irq_rx, bcm_enet_isr_dma, 20918c2ecf20Sopenharmony_ci 0, dev->name, dev); 20928c2ecf20Sopenharmony_ci if (ret) 20938c2ecf20Sopenharmony_ci goto out_freeirq; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci if (priv->irq_tx != -1) { 20968c2ecf20Sopenharmony_ci ret = request_irq(priv->irq_tx, bcm_enet_isr_dma, 20978c2ecf20Sopenharmony_ci 0, dev->name, dev); 20988c2ecf20Sopenharmony_ci if (ret) 20998c2ecf20Sopenharmony_ci goto out_freeirq_rx; 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci /* allocate rx dma ring */ 21038c2ecf20Sopenharmony_ci size = priv->rx_ring_size * sizeof(struct bcm_enet_desc); 21048c2ecf20Sopenharmony_ci p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL); 21058c2ecf20Sopenharmony_ci if (!p) { 21068c2ecf20Sopenharmony_ci dev_err(kdev, "cannot allocate rx ring %u\n", size); 21078c2ecf20Sopenharmony_ci ret = -ENOMEM; 21088c2ecf20Sopenharmony_ci goto out_freeirq_tx; 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci priv->rx_desc_alloc_size = size; 21128c2ecf20Sopenharmony_ci priv->rx_desc_cpu = p; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci /* allocate tx dma ring */ 21158c2ecf20Sopenharmony_ci size = priv->tx_ring_size * sizeof(struct bcm_enet_desc); 21168c2ecf20Sopenharmony_ci p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL); 21178c2ecf20Sopenharmony_ci if (!p) { 21188c2ecf20Sopenharmony_ci dev_err(kdev, "cannot allocate tx ring\n"); 21198c2ecf20Sopenharmony_ci ret = -ENOMEM; 21208c2ecf20Sopenharmony_ci goto out_free_rx_ring; 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci priv->tx_desc_alloc_size = size; 21248c2ecf20Sopenharmony_ci priv->tx_desc_cpu = p; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci priv->tx_skb = kcalloc(priv->tx_ring_size, sizeof(struct sk_buff *), 21278c2ecf20Sopenharmony_ci GFP_KERNEL); 21288c2ecf20Sopenharmony_ci if (!priv->tx_skb) { 21298c2ecf20Sopenharmony_ci dev_err(kdev, "cannot allocate rx skb queue\n"); 21308c2ecf20Sopenharmony_ci ret = -ENOMEM; 21318c2ecf20Sopenharmony_ci goto out_free_tx_ring; 21328c2ecf20Sopenharmony_ci } 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci priv->tx_desc_count = priv->tx_ring_size; 21358c2ecf20Sopenharmony_ci priv->tx_dirty_desc = 0; 21368c2ecf20Sopenharmony_ci priv->tx_curr_desc = 0; 21378c2ecf20Sopenharmony_ci spin_lock_init(&priv->tx_lock); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci /* init & fill rx ring with skbs */ 21408c2ecf20Sopenharmony_ci priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *), 21418c2ecf20Sopenharmony_ci GFP_KERNEL); 21428c2ecf20Sopenharmony_ci if (!priv->rx_skb) { 21438c2ecf20Sopenharmony_ci dev_err(kdev, "cannot allocate rx skb queue\n"); 21448c2ecf20Sopenharmony_ci ret = -ENOMEM; 21458c2ecf20Sopenharmony_ci goto out_free_tx_skb; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci priv->rx_desc_count = 0; 21498c2ecf20Sopenharmony_ci priv->rx_dirty_desc = 0; 21508c2ecf20Sopenharmony_ci priv->rx_curr_desc = 0; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci /* disable all ports */ 21538c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 21548c2ecf20Sopenharmony_ci enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK, 21558c2ecf20Sopenharmony_ci ENETSW_PORTOV_REG(i)); 21568c2ecf20Sopenharmony_ci enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK | 21578c2ecf20Sopenharmony_ci ENETSW_PTCTRL_TXDIS_MASK, 21588c2ecf20Sopenharmony_ci ENETSW_PTCTRL_REG(i)); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci priv->sw_port_link[i] = 0; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* reset mib */ 21648c2ecf20Sopenharmony_ci val = enetsw_readb(priv, ENETSW_GMCR_REG); 21658c2ecf20Sopenharmony_ci val |= ENETSW_GMCR_RST_MIB_MASK; 21668c2ecf20Sopenharmony_ci enetsw_writeb(priv, val, ENETSW_GMCR_REG); 21678c2ecf20Sopenharmony_ci mdelay(1); 21688c2ecf20Sopenharmony_ci val &= ~ENETSW_GMCR_RST_MIB_MASK; 21698c2ecf20Sopenharmony_ci enetsw_writeb(priv, val, ENETSW_GMCR_REG); 21708c2ecf20Sopenharmony_ci mdelay(1); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* force CPU port state */ 21738c2ecf20Sopenharmony_ci val = enetsw_readb(priv, ENETSW_IMPOV_REG); 21748c2ecf20Sopenharmony_ci val |= ENETSW_IMPOV_FORCE_MASK | ENETSW_IMPOV_LINKUP_MASK; 21758c2ecf20Sopenharmony_ci enetsw_writeb(priv, val, ENETSW_IMPOV_REG); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci /* enable switch forward engine */ 21788c2ecf20Sopenharmony_ci val = enetsw_readb(priv, ENETSW_SWMODE_REG); 21798c2ecf20Sopenharmony_ci val |= ENETSW_SWMODE_FWD_EN_MASK; 21808c2ecf20Sopenharmony_ci enetsw_writeb(priv, val, ENETSW_SWMODE_REG); 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* enable jumbo on all ports */ 21838c2ecf20Sopenharmony_ci enetsw_writel(priv, 0x1ff, ENETSW_JMBCTL_PORT_REG); 21848c2ecf20Sopenharmony_ci enetsw_writew(priv, 9728, ENETSW_JMBCTL_MAXSIZE_REG); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci /* initialize flow control buffer allocation */ 21878c2ecf20Sopenharmony_ci enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, 21888c2ecf20Sopenharmony_ci ENETDMA_BUFALLOC_REG(priv->rx_chan)); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (bcm_enet_refill_rx(dev)) { 21918c2ecf20Sopenharmony_ci dev_err(kdev, "cannot allocate rx skb queue\n"); 21928c2ecf20Sopenharmony_ci ret = -ENOMEM; 21938c2ecf20Sopenharmony_ci goto out; 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci /* write rx & tx ring addresses */ 21978c2ecf20Sopenharmony_ci enet_dmas_writel(priv, priv->rx_desc_dma, 21988c2ecf20Sopenharmony_ci ENETDMAS_RSTART_REG, priv->rx_chan); 21998c2ecf20Sopenharmony_ci enet_dmas_writel(priv, priv->tx_desc_dma, 22008c2ecf20Sopenharmony_ci ENETDMAS_RSTART_REG, priv->tx_chan); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* clear remaining state ram for rx & tx channel */ 22038c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->rx_chan); 22048c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->tx_chan); 22058c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->rx_chan); 22068c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->tx_chan); 22078c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->rx_chan); 22088c2ecf20Sopenharmony_ci enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->tx_chan); 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci /* set dma maximum burst len */ 22118c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_maxburst, 22128c2ecf20Sopenharmony_ci ENETDMAC_MAXBURST, priv->rx_chan); 22138c2ecf20Sopenharmony_ci enet_dmac_writel(priv, priv->dma_maxburst, 22148c2ecf20Sopenharmony_ci ENETDMAC_MAXBURST, priv->tx_chan); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci /* set flow control low/high threshold to 1/3 / 2/3 */ 22178c2ecf20Sopenharmony_ci val = priv->rx_ring_size / 3; 22188c2ecf20Sopenharmony_ci enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan)); 22198c2ecf20Sopenharmony_ci val = (priv->rx_ring_size * 2) / 3; 22208c2ecf20Sopenharmony_ci enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan)); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci /* all set, enable mac and interrupts, start dma engine and 22238c2ecf20Sopenharmony_ci * kick rx dma channel 22248c2ecf20Sopenharmony_ci */ 22258c2ecf20Sopenharmony_ci wmb(); 22268c2ecf20Sopenharmony_ci enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); 22278c2ecf20Sopenharmony_ci enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK, 22288c2ecf20Sopenharmony_ci ENETDMAC_CHANCFG, priv->rx_chan); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci /* watch "packet transferred" interrupt in rx and tx */ 22318c2ecf20Sopenharmony_ci enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, 22328c2ecf20Sopenharmony_ci ENETDMAC_IR, priv->rx_chan); 22338c2ecf20Sopenharmony_ci enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, 22348c2ecf20Sopenharmony_ci ENETDMAC_IR, priv->tx_chan); 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci /* make sure we enable napi before rx interrupt */ 22378c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, 22408c2ecf20Sopenharmony_ci ENETDMAC_IRMASK, priv->rx_chan); 22418c2ecf20Sopenharmony_ci enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, 22428c2ecf20Sopenharmony_ci ENETDMAC_IRMASK, priv->tx_chan); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci netif_carrier_on(dev); 22458c2ecf20Sopenharmony_ci netif_start_queue(dev); 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci /* apply override config for bypass_link ports here. */ 22488c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 22498c2ecf20Sopenharmony_ci struct bcm63xx_enetsw_port *port; 22508c2ecf20Sopenharmony_ci u8 override; 22518c2ecf20Sopenharmony_ci port = &priv->used_ports[i]; 22528c2ecf20Sopenharmony_ci if (!port->used) 22538c2ecf20Sopenharmony_ci continue; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci if (!port->bypass_link) 22568c2ecf20Sopenharmony_ci continue; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci override = ENETSW_PORTOV_ENABLE_MASK | 22598c2ecf20Sopenharmony_ci ENETSW_PORTOV_LINKUP_MASK; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci switch (port->force_speed) { 22628c2ecf20Sopenharmony_ci case 1000: 22638c2ecf20Sopenharmony_ci override |= ENETSW_IMPOV_1000_MASK; 22648c2ecf20Sopenharmony_ci break; 22658c2ecf20Sopenharmony_ci case 100: 22668c2ecf20Sopenharmony_ci override |= ENETSW_IMPOV_100_MASK; 22678c2ecf20Sopenharmony_ci break; 22688c2ecf20Sopenharmony_ci case 10: 22698c2ecf20Sopenharmony_ci break; 22708c2ecf20Sopenharmony_ci default: 22718c2ecf20Sopenharmony_ci pr_warn("invalid forced speed on port %s: assume 10\n", 22728c2ecf20Sopenharmony_ci port->name); 22738c2ecf20Sopenharmony_ci break; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (port->force_duplex_full) 22778c2ecf20Sopenharmony_ci override |= ENETSW_IMPOV_FDX_MASK; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i)); 22818c2ecf20Sopenharmony_ci enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i)); 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci /* start phy polling timer */ 22858c2ecf20Sopenharmony_ci timer_setup(&priv->swphy_poll, swphy_poll_timer, 0); 22868c2ecf20Sopenharmony_ci mod_timer(&priv->swphy_poll, jiffies); 22878c2ecf20Sopenharmony_ci return 0; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ciout: 22908c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 22918c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci if (!priv->rx_skb[i]) 22948c2ecf20Sopenharmony_ci continue; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci desc = &priv->rx_desc_cpu[i]; 22978c2ecf20Sopenharmony_ci dma_unmap_single(kdev, desc->address, priv->rx_skb_size, 22988c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 22998c2ecf20Sopenharmony_ci kfree_skb(priv->rx_skb[i]); 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci kfree(priv->rx_skb); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ciout_free_tx_skb: 23048c2ecf20Sopenharmony_ci kfree(priv->tx_skb); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ciout_free_tx_ring: 23078c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->tx_desc_alloc_size, 23088c2ecf20Sopenharmony_ci priv->tx_desc_cpu, priv->tx_desc_dma); 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ciout_free_rx_ring: 23118c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->rx_desc_alloc_size, 23128c2ecf20Sopenharmony_ci priv->rx_desc_cpu, priv->rx_desc_dma); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ciout_freeirq_tx: 23158c2ecf20Sopenharmony_ci if (priv->irq_tx != -1) 23168c2ecf20Sopenharmony_ci free_irq(priv->irq_tx, dev); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ciout_freeirq_rx: 23198c2ecf20Sopenharmony_ci free_irq(priv->irq_rx, dev); 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ciout_freeirq: 23228c2ecf20Sopenharmony_ci return ret; 23238c2ecf20Sopenharmony_ci} 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci/* stop callback */ 23268c2ecf20Sopenharmony_cistatic int bcm_enetsw_stop(struct net_device *dev) 23278c2ecf20Sopenharmony_ci{ 23288c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 23298c2ecf20Sopenharmony_ci struct device *kdev; 23308c2ecf20Sopenharmony_ci int i; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 23338c2ecf20Sopenharmony_ci kdev = &priv->pdev->dev; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci del_timer_sync(&priv->swphy_poll); 23368c2ecf20Sopenharmony_ci netif_stop_queue(dev); 23378c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 23388c2ecf20Sopenharmony_ci del_timer_sync(&priv->rx_timeout); 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci /* mask all interrupts */ 23418c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); 23428c2ecf20Sopenharmony_ci enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci /* disable dma & mac */ 23458c2ecf20Sopenharmony_ci bcm_enet_disable_dma(priv, priv->tx_chan); 23468c2ecf20Sopenharmony_ci bcm_enet_disable_dma(priv, priv->rx_chan); 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci /* force reclaim of all tx buffers */ 23498c2ecf20Sopenharmony_ci bcm_enet_tx_reclaim(dev, 1); 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci /* free the rx skb ring */ 23528c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 23538c2ecf20Sopenharmony_ci struct bcm_enet_desc *desc; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (!priv->rx_skb[i]) 23568c2ecf20Sopenharmony_ci continue; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci desc = &priv->rx_desc_cpu[i]; 23598c2ecf20Sopenharmony_ci dma_unmap_single(kdev, desc->address, priv->rx_skb_size, 23608c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 23618c2ecf20Sopenharmony_ci kfree_skb(priv->rx_skb[i]); 23628c2ecf20Sopenharmony_ci } 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci /* free remaining allocated memory */ 23658c2ecf20Sopenharmony_ci kfree(priv->rx_skb); 23668c2ecf20Sopenharmony_ci kfree(priv->tx_skb); 23678c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->rx_desc_alloc_size, 23688c2ecf20Sopenharmony_ci priv->rx_desc_cpu, priv->rx_desc_dma); 23698c2ecf20Sopenharmony_ci dma_free_coherent(kdev, priv->tx_desc_alloc_size, 23708c2ecf20Sopenharmony_ci priv->tx_desc_cpu, priv->tx_desc_dma); 23718c2ecf20Sopenharmony_ci if (priv->irq_tx != -1) 23728c2ecf20Sopenharmony_ci free_irq(priv->irq_tx, dev); 23738c2ecf20Sopenharmony_ci free_irq(priv->irq_rx, dev); 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci return 0; 23768c2ecf20Sopenharmony_ci} 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci/* try to sort out phy external status by walking the used_port field 23798c2ecf20Sopenharmony_ci * in the bcm_enet_priv structure. in case the phy address is not 23808c2ecf20Sopenharmony_ci * assigned to any physical port on the switch, assume it is external 23818c2ecf20Sopenharmony_ci * (and yell at the user). 23828c2ecf20Sopenharmony_ci */ 23838c2ecf20Sopenharmony_cistatic int bcm_enetsw_phy_is_external(struct bcm_enet_priv *priv, int phy_id) 23848c2ecf20Sopenharmony_ci{ 23858c2ecf20Sopenharmony_ci int i; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_ports; ++i) { 23888c2ecf20Sopenharmony_ci if (!priv->used_ports[i].used) 23898c2ecf20Sopenharmony_ci continue; 23908c2ecf20Sopenharmony_ci if (priv->used_ports[i].phy_id == phy_id) 23918c2ecf20Sopenharmony_ci return bcm_enet_port_is_rgmii(i); 23928c2ecf20Sopenharmony_ci } 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ci printk_once(KERN_WARNING "bcm63xx_enet: could not find a used port with phy_id %i, assuming phy is external\n", 23958c2ecf20Sopenharmony_ci phy_id); 23968c2ecf20Sopenharmony_ci return 1; 23978c2ecf20Sopenharmony_ci} 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci/* can't use bcmenet_sw_mdio_read directly as we need to sort out 24008c2ecf20Sopenharmony_ci * external/internal status of the given phy_id first. 24018c2ecf20Sopenharmony_ci */ 24028c2ecf20Sopenharmony_cistatic int bcm_enetsw_mii_mdio_read(struct net_device *dev, int phy_id, 24038c2ecf20Sopenharmony_ci int location) 24048c2ecf20Sopenharmony_ci{ 24058c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 24088c2ecf20Sopenharmony_ci return bcmenet_sw_mdio_read(priv, 24098c2ecf20Sopenharmony_ci bcm_enetsw_phy_is_external(priv, phy_id), 24108c2ecf20Sopenharmony_ci phy_id, location); 24118c2ecf20Sopenharmony_ci} 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci/* can't use bcmenet_sw_mdio_write directly as we need to sort out 24148c2ecf20Sopenharmony_ci * external/internal status of the given phy_id first. 24158c2ecf20Sopenharmony_ci */ 24168c2ecf20Sopenharmony_cistatic void bcm_enetsw_mii_mdio_write(struct net_device *dev, int phy_id, 24178c2ecf20Sopenharmony_ci int location, 24188c2ecf20Sopenharmony_ci int val) 24198c2ecf20Sopenharmony_ci{ 24208c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 24238c2ecf20Sopenharmony_ci bcmenet_sw_mdio_write(priv, bcm_enetsw_phy_is_external(priv, phy_id), 24248c2ecf20Sopenharmony_ci phy_id, location, val); 24258c2ecf20Sopenharmony_ci} 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_cistatic int bcm_enetsw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 24288c2ecf20Sopenharmony_ci{ 24298c2ecf20Sopenharmony_ci struct mii_if_info mii; 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci mii.dev = dev; 24328c2ecf20Sopenharmony_ci mii.mdio_read = bcm_enetsw_mii_mdio_read; 24338c2ecf20Sopenharmony_ci mii.mdio_write = bcm_enetsw_mii_mdio_write; 24348c2ecf20Sopenharmony_ci mii.phy_id = 0; 24358c2ecf20Sopenharmony_ci mii.phy_id_mask = 0x3f; 24368c2ecf20Sopenharmony_ci mii.reg_num_mask = 0x1f; 24378c2ecf20Sopenharmony_ci return generic_mii_ioctl(&mii, if_mii(rq), cmd, NULL); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci} 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_cistatic const struct net_device_ops bcm_enetsw_ops = { 24428c2ecf20Sopenharmony_ci .ndo_open = bcm_enetsw_open, 24438c2ecf20Sopenharmony_ci .ndo_stop = bcm_enetsw_stop, 24448c2ecf20Sopenharmony_ci .ndo_start_xmit = bcm_enet_start_xmit, 24458c2ecf20Sopenharmony_ci .ndo_change_mtu = bcm_enet_change_mtu, 24468c2ecf20Sopenharmony_ci .ndo_do_ioctl = bcm_enetsw_ioctl, 24478c2ecf20Sopenharmony_ci}; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_cistatic const struct bcm_enet_stats bcm_enetsw_gstrings_stats[] = { 24518c2ecf20Sopenharmony_ci { "rx_packets", DEV_STAT(rx_packets), -1 }, 24528c2ecf20Sopenharmony_ci { "tx_packets", DEV_STAT(tx_packets), -1 }, 24538c2ecf20Sopenharmony_ci { "rx_bytes", DEV_STAT(rx_bytes), -1 }, 24548c2ecf20Sopenharmony_ci { "tx_bytes", DEV_STAT(tx_bytes), -1 }, 24558c2ecf20Sopenharmony_ci { "rx_errors", DEV_STAT(rx_errors), -1 }, 24568c2ecf20Sopenharmony_ci { "tx_errors", DEV_STAT(tx_errors), -1 }, 24578c2ecf20Sopenharmony_ci { "rx_dropped", DEV_STAT(rx_dropped), -1 }, 24588c2ecf20Sopenharmony_ci { "tx_dropped", DEV_STAT(tx_dropped), -1 }, 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci { "tx_good_octets", GEN_STAT(mib.tx_gd_octets), ETHSW_MIB_RX_GD_OCT }, 24618c2ecf20Sopenharmony_ci { "tx_unicast", GEN_STAT(mib.tx_unicast), ETHSW_MIB_RX_BRDCAST }, 24628c2ecf20Sopenharmony_ci { "tx_broadcast", GEN_STAT(mib.tx_brdcast), ETHSW_MIB_RX_BRDCAST }, 24638c2ecf20Sopenharmony_ci { "tx_multicast", GEN_STAT(mib.tx_mult), ETHSW_MIB_RX_MULT }, 24648c2ecf20Sopenharmony_ci { "tx_64_octets", GEN_STAT(mib.tx_64), ETHSW_MIB_RX_64 }, 24658c2ecf20Sopenharmony_ci { "tx_65_127_oct", GEN_STAT(mib.tx_65_127), ETHSW_MIB_RX_65_127 }, 24668c2ecf20Sopenharmony_ci { "tx_128_255_oct", GEN_STAT(mib.tx_128_255), ETHSW_MIB_RX_128_255 }, 24678c2ecf20Sopenharmony_ci { "tx_256_511_oct", GEN_STAT(mib.tx_256_511), ETHSW_MIB_RX_256_511 }, 24688c2ecf20Sopenharmony_ci { "tx_512_1023_oct", GEN_STAT(mib.tx_512_1023), ETHSW_MIB_RX_512_1023}, 24698c2ecf20Sopenharmony_ci { "tx_1024_1522_oct", GEN_STAT(mib.tx_1024_max), 24708c2ecf20Sopenharmony_ci ETHSW_MIB_RX_1024_1522 }, 24718c2ecf20Sopenharmony_ci { "tx_1523_2047_oct", GEN_STAT(mib.tx_1523_2047), 24728c2ecf20Sopenharmony_ci ETHSW_MIB_RX_1523_2047 }, 24738c2ecf20Sopenharmony_ci { "tx_2048_4095_oct", GEN_STAT(mib.tx_2048_4095), 24748c2ecf20Sopenharmony_ci ETHSW_MIB_RX_2048_4095 }, 24758c2ecf20Sopenharmony_ci { "tx_4096_8191_oct", GEN_STAT(mib.tx_4096_8191), 24768c2ecf20Sopenharmony_ci ETHSW_MIB_RX_4096_8191 }, 24778c2ecf20Sopenharmony_ci { "tx_8192_9728_oct", GEN_STAT(mib.tx_8192_9728), 24788c2ecf20Sopenharmony_ci ETHSW_MIB_RX_8192_9728 }, 24798c2ecf20Sopenharmony_ci { "tx_oversize", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR }, 24808c2ecf20Sopenharmony_ci { "tx_oversize_drop", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR_DISC }, 24818c2ecf20Sopenharmony_ci { "tx_dropped", GEN_STAT(mib.tx_drop), ETHSW_MIB_RX_DROP }, 24828c2ecf20Sopenharmony_ci { "tx_undersize", GEN_STAT(mib.tx_underrun), ETHSW_MIB_RX_UND }, 24838c2ecf20Sopenharmony_ci { "tx_pause", GEN_STAT(mib.tx_pause), ETHSW_MIB_RX_PAUSE }, 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci { "rx_good_octets", GEN_STAT(mib.rx_gd_octets), ETHSW_MIB_TX_ALL_OCT }, 24868c2ecf20Sopenharmony_ci { "rx_broadcast", GEN_STAT(mib.rx_brdcast), ETHSW_MIB_TX_BRDCAST }, 24878c2ecf20Sopenharmony_ci { "rx_multicast", GEN_STAT(mib.rx_mult), ETHSW_MIB_TX_MULT }, 24888c2ecf20Sopenharmony_ci { "rx_unicast", GEN_STAT(mib.rx_unicast), ETHSW_MIB_TX_MULT }, 24898c2ecf20Sopenharmony_ci { "rx_pause", GEN_STAT(mib.rx_pause), ETHSW_MIB_TX_PAUSE }, 24908c2ecf20Sopenharmony_ci { "rx_dropped", GEN_STAT(mib.rx_drop), ETHSW_MIB_TX_DROP_PKTS }, 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci}; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci#define BCM_ENETSW_STATS_LEN \ 24958c2ecf20Sopenharmony_ci (sizeof(bcm_enetsw_gstrings_stats) / sizeof(struct bcm_enet_stats)) 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_cistatic void bcm_enetsw_get_strings(struct net_device *netdev, 24988c2ecf20Sopenharmony_ci u32 stringset, u8 *data) 24998c2ecf20Sopenharmony_ci{ 25008c2ecf20Sopenharmony_ci int i; 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci switch (stringset) { 25038c2ecf20Sopenharmony_ci case ETH_SS_STATS: 25048c2ecf20Sopenharmony_ci for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { 25058c2ecf20Sopenharmony_ci memcpy(data + i * ETH_GSTRING_LEN, 25068c2ecf20Sopenharmony_ci bcm_enetsw_gstrings_stats[i].stat_string, 25078c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 25088c2ecf20Sopenharmony_ci } 25098c2ecf20Sopenharmony_ci break; 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci} 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_cistatic int bcm_enetsw_get_sset_count(struct net_device *netdev, 25148c2ecf20Sopenharmony_ci int string_set) 25158c2ecf20Sopenharmony_ci{ 25168c2ecf20Sopenharmony_ci switch (string_set) { 25178c2ecf20Sopenharmony_ci case ETH_SS_STATS: 25188c2ecf20Sopenharmony_ci return BCM_ENETSW_STATS_LEN; 25198c2ecf20Sopenharmony_ci default: 25208c2ecf20Sopenharmony_ci return -EINVAL; 25218c2ecf20Sopenharmony_ci } 25228c2ecf20Sopenharmony_ci} 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_cistatic void bcm_enetsw_get_drvinfo(struct net_device *netdev, 25258c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 25268c2ecf20Sopenharmony_ci{ 25278c2ecf20Sopenharmony_ci strncpy(drvinfo->driver, bcm_enet_driver_name, sizeof(drvinfo->driver)); 25288c2ecf20Sopenharmony_ci strncpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); 25298c2ecf20Sopenharmony_ci} 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_cistatic void bcm_enetsw_get_ethtool_stats(struct net_device *netdev, 25328c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 25338c2ecf20Sopenharmony_ci u64 *data) 25348c2ecf20Sopenharmony_ci{ 25358c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 25368c2ecf20Sopenharmony_ci int i; 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci priv = netdev_priv(netdev); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { 25418c2ecf20Sopenharmony_ci const struct bcm_enet_stats *s; 25428c2ecf20Sopenharmony_ci u32 lo, hi; 25438c2ecf20Sopenharmony_ci char *p; 25448c2ecf20Sopenharmony_ci int reg; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci s = &bcm_enetsw_gstrings_stats[i]; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci reg = s->mib_reg; 25498c2ecf20Sopenharmony_ci if (reg == -1) 25508c2ecf20Sopenharmony_ci continue; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci lo = enetsw_readl(priv, ENETSW_MIB_REG(reg)); 25538c2ecf20Sopenharmony_ci p = (char *)priv + s->stat_offset; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci if (s->sizeof_stat == sizeof(u64)) { 25568c2ecf20Sopenharmony_ci hi = enetsw_readl(priv, ENETSW_MIB_REG(reg + 1)); 25578c2ecf20Sopenharmony_ci *(u64 *)p = ((u64)hi << 32 | lo); 25588c2ecf20Sopenharmony_ci } else { 25598c2ecf20Sopenharmony_ci *(u32 *)p = lo; 25608c2ecf20Sopenharmony_ci } 25618c2ecf20Sopenharmony_ci } 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { 25648c2ecf20Sopenharmony_ci const struct bcm_enet_stats *s; 25658c2ecf20Sopenharmony_ci char *p; 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci s = &bcm_enetsw_gstrings_stats[i]; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci if (s->mib_reg == -1) 25708c2ecf20Sopenharmony_ci p = (char *)&netdev->stats + s->stat_offset; 25718c2ecf20Sopenharmony_ci else 25728c2ecf20Sopenharmony_ci p = (char *)priv + s->stat_offset; 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci data[i] = (s->sizeof_stat == sizeof(u64)) ? 25758c2ecf20Sopenharmony_ci *(u64 *)p : *(u32 *)p; 25768c2ecf20Sopenharmony_ci } 25778c2ecf20Sopenharmony_ci} 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_cistatic void bcm_enetsw_get_ringparam(struct net_device *dev, 25808c2ecf20Sopenharmony_ci struct ethtool_ringparam *ering) 25818c2ecf20Sopenharmony_ci{ 25828c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci /* rx/tx ring is actually only limited by memory */ 25878c2ecf20Sopenharmony_ci ering->rx_max_pending = 8192; 25888c2ecf20Sopenharmony_ci ering->tx_max_pending = 8192; 25898c2ecf20Sopenharmony_ci ering->rx_mini_max_pending = 0; 25908c2ecf20Sopenharmony_ci ering->rx_jumbo_max_pending = 0; 25918c2ecf20Sopenharmony_ci ering->rx_pending = priv->rx_ring_size; 25928c2ecf20Sopenharmony_ci ering->tx_pending = priv->tx_ring_size; 25938c2ecf20Sopenharmony_ci} 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_cistatic int bcm_enetsw_set_ringparam(struct net_device *dev, 25968c2ecf20Sopenharmony_ci struct ethtool_ringparam *ering) 25978c2ecf20Sopenharmony_ci{ 25988c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 25998c2ecf20Sopenharmony_ci int was_running; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci was_running = 0; 26048c2ecf20Sopenharmony_ci if (netif_running(dev)) { 26058c2ecf20Sopenharmony_ci bcm_enetsw_stop(dev); 26068c2ecf20Sopenharmony_ci was_running = 1; 26078c2ecf20Sopenharmony_ci } 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci priv->rx_ring_size = ering->rx_pending; 26108c2ecf20Sopenharmony_ci priv->tx_ring_size = ering->tx_pending; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci if (was_running) { 26138c2ecf20Sopenharmony_ci int err; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci err = bcm_enetsw_open(dev); 26168c2ecf20Sopenharmony_ci if (err) 26178c2ecf20Sopenharmony_ci dev_close(dev); 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci return 0; 26208c2ecf20Sopenharmony_ci} 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_cistatic const struct ethtool_ops bcm_enetsw_ethtool_ops = { 26238c2ecf20Sopenharmony_ci .get_strings = bcm_enetsw_get_strings, 26248c2ecf20Sopenharmony_ci .get_sset_count = bcm_enetsw_get_sset_count, 26258c2ecf20Sopenharmony_ci .get_ethtool_stats = bcm_enetsw_get_ethtool_stats, 26268c2ecf20Sopenharmony_ci .get_drvinfo = bcm_enetsw_get_drvinfo, 26278c2ecf20Sopenharmony_ci .get_ringparam = bcm_enetsw_get_ringparam, 26288c2ecf20Sopenharmony_ci .set_ringparam = bcm_enetsw_set_ringparam, 26298c2ecf20Sopenharmony_ci}; 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci/* allocate netdevice, request register memory and register device. */ 26328c2ecf20Sopenharmony_cistatic int bcm_enetsw_probe(struct platform_device *pdev) 26338c2ecf20Sopenharmony_ci{ 26348c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 26358c2ecf20Sopenharmony_ci struct net_device *dev; 26368c2ecf20Sopenharmony_ci struct bcm63xx_enetsw_platform_data *pd; 26378c2ecf20Sopenharmony_ci struct resource *res_mem; 26388c2ecf20Sopenharmony_ci int ret, irq_rx, irq_tx; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci if (!bcm_enet_shared_base[0]) 26418c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 26448c2ecf20Sopenharmony_ci irq_rx = platform_get_irq(pdev, 0); 26458c2ecf20Sopenharmony_ci irq_tx = platform_get_irq(pdev, 1); 26468c2ecf20Sopenharmony_ci if (!res_mem || irq_rx < 0) 26478c2ecf20Sopenharmony_ci return -ENODEV; 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci ret = 0; 26508c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*priv)); 26518c2ecf20Sopenharmony_ci if (!dev) 26528c2ecf20Sopenharmony_ci return -ENOMEM; 26538c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci /* initialize default and fetch platform data */ 26568c2ecf20Sopenharmony_ci priv->enet_is_sw = true; 26578c2ecf20Sopenharmony_ci priv->irq_rx = irq_rx; 26588c2ecf20Sopenharmony_ci priv->irq_tx = irq_tx; 26598c2ecf20Sopenharmony_ci priv->rx_ring_size = BCMENET_DEF_RX_DESC; 26608c2ecf20Sopenharmony_ci priv->tx_ring_size = BCMENET_DEF_TX_DESC; 26618c2ecf20Sopenharmony_ci priv->dma_maxburst = BCMENETSW_DMA_MAXBURST; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci pd = dev_get_platdata(&pdev->dev); 26648c2ecf20Sopenharmony_ci if (pd) { 26658c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); 26668c2ecf20Sopenharmony_ci memcpy(priv->used_ports, pd->used_ports, 26678c2ecf20Sopenharmony_ci sizeof(pd->used_ports)); 26688c2ecf20Sopenharmony_ci priv->num_ports = pd->num_ports; 26698c2ecf20Sopenharmony_ci priv->dma_has_sram = pd->dma_has_sram; 26708c2ecf20Sopenharmony_ci priv->dma_chan_en_mask = pd->dma_chan_en_mask; 26718c2ecf20Sopenharmony_ci priv->dma_chan_int_mask = pd->dma_chan_int_mask; 26728c2ecf20Sopenharmony_ci priv->dma_chan_width = pd->dma_chan_width; 26738c2ecf20Sopenharmony_ci } 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci ret = bcm_enet_change_mtu(dev, dev->mtu); 26768c2ecf20Sopenharmony_ci if (ret) 26778c2ecf20Sopenharmony_ci goto out; 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci priv->base = devm_ioremap_resource(&pdev->dev, res_mem); 26808c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) { 26818c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->base); 26828c2ecf20Sopenharmony_ci goto out; 26838c2ecf20Sopenharmony_ci } 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci priv->mac_clk = devm_clk_get(&pdev->dev, "enetsw"); 26868c2ecf20Sopenharmony_ci if (IS_ERR(priv->mac_clk)) { 26878c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->mac_clk); 26888c2ecf20Sopenharmony_ci goto out; 26898c2ecf20Sopenharmony_ci } 26908c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->mac_clk); 26918c2ecf20Sopenharmony_ci if (ret) 26928c2ecf20Sopenharmony_ci goto out; 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_ci priv->rx_chan = 0; 26958c2ecf20Sopenharmony_ci priv->tx_chan = 1; 26968c2ecf20Sopenharmony_ci spin_lock_init(&priv->rx_lock); 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci /* init rx timeout (used for oom) */ 26998c2ecf20Sopenharmony_ci timer_setup(&priv->rx_timeout, bcm_enet_refill_rx_timer, 0); 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci /* register netdevice */ 27028c2ecf20Sopenharmony_ci dev->netdev_ops = &bcm_enetsw_ops; 27038c2ecf20Sopenharmony_ci netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); 27048c2ecf20Sopenharmony_ci dev->ethtool_ops = &bcm_enetsw_ethtool_ops; 27058c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci spin_lock_init(&priv->enetsw_mdio_lock); 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci ret = register_netdev(dev); 27108c2ecf20Sopenharmony_ci if (ret) 27118c2ecf20Sopenharmony_ci goto out_disable_clk; 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci netif_carrier_off(dev); 27148c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 27158c2ecf20Sopenharmony_ci priv->pdev = pdev; 27168c2ecf20Sopenharmony_ci priv->net_dev = dev; 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci return 0; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ciout_disable_clk: 27218c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->mac_clk); 27228c2ecf20Sopenharmony_ciout: 27238c2ecf20Sopenharmony_ci free_netdev(dev); 27248c2ecf20Sopenharmony_ci return ret; 27258c2ecf20Sopenharmony_ci} 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci/* exit func, stops hardware and unregisters netdevice */ 27298c2ecf20Sopenharmony_cistatic int bcm_enetsw_remove(struct platform_device *pdev) 27308c2ecf20Sopenharmony_ci{ 27318c2ecf20Sopenharmony_ci struct bcm_enet_priv *priv; 27328c2ecf20Sopenharmony_ci struct net_device *dev; 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci /* stop netdevice */ 27358c2ecf20Sopenharmony_ci dev = platform_get_drvdata(pdev); 27368c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 27378c2ecf20Sopenharmony_ci unregister_netdev(dev); 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->mac_clk); 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci free_netdev(dev); 27428c2ecf20Sopenharmony_ci return 0; 27438c2ecf20Sopenharmony_ci} 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_cistruct platform_driver bcm63xx_enetsw_driver = { 27468c2ecf20Sopenharmony_ci .probe = bcm_enetsw_probe, 27478c2ecf20Sopenharmony_ci .remove = bcm_enetsw_remove, 27488c2ecf20Sopenharmony_ci .driver = { 27498c2ecf20Sopenharmony_ci .name = "bcm63xx_enetsw", 27508c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 27518c2ecf20Sopenharmony_ci }, 27528c2ecf20Sopenharmony_ci}; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci/* reserve & remap memory space shared between all macs */ 27558c2ecf20Sopenharmony_cistatic int bcm_enet_shared_probe(struct platform_device *pdev) 27568c2ecf20Sopenharmony_ci{ 27578c2ecf20Sopenharmony_ci void __iomem *p[3]; 27588c2ecf20Sopenharmony_ci unsigned int i; 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci memset(bcm_enet_shared_base, 0, sizeof(bcm_enet_shared_base)); 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 27638c2ecf20Sopenharmony_ci p[i] = devm_platform_ioremap_resource(pdev, i); 27648c2ecf20Sopenharmony_ci if (IS_ERR(p[i])) 27658c2ecf20Sopenharmony_ci return PTR_ERR(p[i]); 27668c2ecf20Sopenharmony_ci } 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci memcpy(bcm_enet_shared_base, p, sizeof(bcm_enet_shared_base)); 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ci return 0; 27718c2ecf20Sopenharmony_ci} 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_cistatic int bcm_enet_shared_remove(struct platform_device *pdev) 27748c2ecf20Sopenharmony_ci{ 27758c2ecf20Sopenharmony_ci return 0; 27768c2ecf20Sopenharmony_ci} 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci/* this "shared" driver is needed because both macs share a single 27798c2ecf20Sopenharmony_ci * address space 27808c2ecf20Sopenharmony_ci */ 27818c2ecf20Sopenharmony_cistruct platform_driver bcm63xx_enet_shared_driver = { 27828c2ecf20Sopenharmony_ci .probe = bcm_enet_shared_probe, 27838c2ecf20Sopenharmony_ci .remove = bcm_enet_shared_remove, 27848c2ecf20Sopenharmony_ci .driver = { 27858c2ecf20Sopenharmony_ci .name = "bcm63xx_enet_shared", 27868c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 27878c2ecf20Sopenharmony_ci }, 27888c2ecf20Sopenharmony_ci}; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_cistatic struct platform_driver * const drivers[] = { 27918c2ecf20Sopenharmony_ci &bcm63xx_enet_shared_driver, 27928c2ecf20Sopenharmony_ci &bcm63xx_enet_driver, 27938c2ecf20Sopenharmony_ci &bcm63xx_enetsw_driver, 27948c2ecf20Sopenharmony_ci}; 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci/* entry point */ 27978c2ecf20Sopenharmony_cistatic int __init bcm_enet_init(void) 27988c2ecf20Sopenharmony_ci{ 27998c2ecf20Sopenharmony_ci return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 28008c2ecf20Sopenharmony_ci} 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_cistatic void __exit bcm_enet_exit(void) 28038c2ecf20Sopenharmony_ci{ 28048c2ecf20Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 28058c2ecf20Sopenharmony_ci} 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_cimodule_init(bcm_enet_init); 28098c2ecf20Sopenharmony_cimodule_exit(bcm_enet_exit); 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BCM63xx internal ethernet mac driver"); 28128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); 28138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2814