18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Advanced Micro Devices 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ] 78c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c] 88c2ecf20Sopenharmony_ci * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ] 98c2ecf20Sopenharmony_ci * Derived from the lance driver written 1993,1994,1995 by Donald Becker. 108c2ecf20Sopenharmony_ci * Copyright 1993 United States Government as represented by the 118c2ecf20Sopenharmony_ci * Director, National Security Agency.[ pcnet32.c ] 128c2ecf20Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ] 138c2ecf20Sopenharmony_ci * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciModule Name: 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci amd8111e.c 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciAbstract: 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci AMD8111 based 10/100 Ethernet Controller Driver. 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciEnvironment: 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci Kernel Mode 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciRevision History: 298c2ecf20Sopenharmony_ci 3.0.0 308c2ecf20Sopenharmony_ci Initial Revision. 318c2ecf20Sopenharmony_ci 3.0.1 328c2ecf20Sopenharmony_ci 1. Dynamic interrupt coalescing. 338c2ecf20Sopenharmony_ci 2. Removed prev_stats. 348c2ecf20Sopenharmony_ci 3. MII support. 358c2ecf20Sopenharmony_ci 4. Dynamic IPG support 368c2ecf20Sopenharmony_ci 3.0.2 05/29/2003 378c2ecf20Sopenharmony_ci 1. Bug fix: Fixed failure to send jumbo packets larger than 4k. 388c2ecf20Sopenharmony_ci 2. Bug fix: Fixed VLAN support failure. 398c2ecf20Sopenharmony_ci 3. Bug fix: Fixed receive interrupt coalescing bug. 408c2ecf20Sopenharmony_ci 4. Dynamic IPG support is disabled by default. 418c2ecf20Sopenharmony_ci 3.0.3 06/05/2003 428c2ecf20Sopenharmony_ci 1. Bug fix: Fixed failure to close the interface if SMP is enabled. 438c2ecf20Sopenharmony_ci 3.0.4 12/09/2003 448c2ecf20Sopenharmony_ci 1. Added set_mac_address routine for bonding driver support. 458c2ecf20Sopenharmony_ci 2. Tested the driver for bonding support 468c2ecf20Sopenharmony_ci 3. Bug fix: Fixed mismach in actual receive buffer lenth and lenth 478c2ecf20Sopenharmony_ci indicated to the h/w. 488c2ecf20Sopenharmony_ci 4. Modified amd8111e_rx() routine to receive all the received packets 498c2ecf20Sopenharmony_ci in the first interrupt. 508c2ecf20Sopenharmony_ci 5. Bug fix: Corrected rx_errors reported in get_stats() function. 518c2ecf20Sopenharmony_ci 3.0.5 03/22/2004 528c2ecf20Sopenharmony_ci 1. Added NAPI support 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci*/ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#include <linux/module.h> 588c2ecf20Sopenharmony_ci#include <linux/kernel.h> 598c2ecf20Sopenharmony_ci#include <linux/types.h> 608c2ecf20Sopenharmony_ci#include <linux/compiler.h> 618c2ecf20Sopenharmony_ci#include <linux/delay.h> 628c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 638c2ecf20Sopenharmony_ci#include <linux/ioport.h> 648c2ecf20Sopenharmony_ci#include <linux/pci.h> 658c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 668c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 678c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 688c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 698c2ecf20Sopenharmony_ci#include <linux/mii.h> 708c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 718c2ecf20Sopenharmony_ci#include <linux/ctype.h> 728c2ecf20Sopenharmony_ci#include <linux/crc32.h> 738c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#include <asm/io.h> 768c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 778c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VLAN_8021Q) 808c2ecf20Sopenharmony_ci#define AMD8111E_VLAN_TAG_USED 1 818c2ecf20Sopenharmony_ci#else 828c2ecf20Sopenharmony_ci#define AMD8111E_VLAN_TAG_USED 0 838c2ecf20Sopenharmony_ci#endif 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#include "amd8111e.h" 868c2ecf20Sopenharmony_ci#define MODULE_NAME "amd8111e" 878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Advanced Micro Devices, Inc."); 888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD8111 based 10/100 Ethernet Controller."); 898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 908c2ecf20Sopenharmony_cimodule_param_array(speed_duplex, int, NULL, 0); 918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotiate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex"); 928c2ecf20Sopenharmony_cimodule_param_array(coalesce, bool, NULL, 0); 938c2ecf20Sopenharmony_ciMODULE_PARM_DESC(coalesce, "Enable or Disable interrupt coalescing, 1: Enable, 0: Disable"); 948c2ecf20Sopenharmony_cimodule_param_array(dynamic_ipg, bool, NULL, 0); 958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dynamic_ipg, "Enable or Disable dynamic IPG, 1: Enable, 0: Disable"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* This function will read the PHY registers. */ 988c2ecf20Sopenharmony_cistatic int amd8111e_read_phy(struct amd8111e_priv *lp, 998c2ecf20Sopenharmony_ci int phy_id, int reg, u32 *val) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 1028c2ecf20Sopenharmony_ci unsigned int reg_val; 1038c2ecf20Sopenharmony_ci unsigned int repeat= REPEAT_CNT; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci reg_val = readl(mmio + PHY_ACCESS); 1068c2ecf20Sopenharmony_ci while (reg_val & PHY_CMD_ACTIVE) 1078c2ecf20Sopenharmony_ci reg_val = readl( mmio + PHY_ACCESS ); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci writel( PHY_RD_CMD | ((phy_id & 0x1f) << 21) | 1108c2ecf20Sopenharmony_ci ((reg & 0x1f) << 16), mmio +PHY_ACCESS); 1118c2ecf20Sopenharmony_ci do{ 1128c2ecf20Sopenharmony_ci reg_val = readl(mmio + PHY_ACCESS); 1138c2ecf20Sopenharmony_ci udelay(30); /* It takes 30 us to read/write data */ 1148c2ecf20Sopenharmony_ci } while (--repeat && (reg_val & PHY_CMD_ACTIVE)); 1158c2ecf20Sopenharmony_ci if(reg_val & PHY_RD_ERR) 1168c2ecf20Sopenharmony_ci goto err_phy_read; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci *val = reg_val & 0xffff; 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_cierr_phy_read: 1218c2ecf20Sopenharmony_ci *val = 0; 1228c2ecf20Sopenharmony_ci return -EINVAL; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* This function will write into PHY registers. */ 1278c2ecf20Sopenharmony_cistatic int amd8111e_write_phy(struct amd8111e_priv *lp, 1288c2ecf20Sopenharmony_ci int phy_id, int reg, u32 val) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned int repeat = REPEAT_CNT; 1318c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 1328c2ecf20Sopenharmony_ci unsigned int reg_val; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci reg_val = readl(mmio + PHY_ACCESS); 1358c2ecf20Sopenharmony_ci while (reg_val & PHY_CMD_ACTIVE) 1368c2ecf20Sopenharmony_ci reg_val = readl( mmio + PHY_ACCESS ); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci writel( PHY_WR_CMD | ((phy_id & 0x1f) << 21) | 1398c2ecf20Sopenharmony_ci ((reg & 0x1f) << 16)|val, mmio + PHY_ACCESS); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci do{ 1428c2ecf20Sopenharmony_ci reg_val = readl(mmio + PHY_ACCESS); 1438c2ecf20Sopenharmony_ci udelay(30); /* It takes 30 us to read/write the data */ 1448c2ecf20Sopenharmony_ci } while (--repeat && (reg_val & PHY_CMD_ACTIVE)); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if(reg_val & PHY_RD_ERR) 1478c2ecf20Sopenharmony_ci goto err_phy_write; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cierr_phy_write: 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* This is the mii register read function provided to the mii interface. */ 1578c2ecf20Sopenharmony_cistatic int amd8111e_mdio_read(struct net_device *dev, int phy_id, int reg_num) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 1608c2ecf20Sopenharmony_ci unsigned int reg_val; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci amd8111e_read_phy(lp,phy_id,reg_num,®_val); 1638c2ecf20Sopenharmony_ci return reg_val; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* This is the mii register write function provided to the mii interface. */ 1688c2ecf20Sopenharmony_cistatic void amd8111e_mdio_write(struct net_device *dev, 1698c2ecf20Sopenharmony_ci int phy_id, int reg_num, int val) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci amd8111e_write_phy(lp, phy_id, reg_num, val); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* This function will set PHY speed. During initialization sets 1778c2ecf20Sopenharmony_ci * the original speed to 100 full 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistatic void amd8111e_set_ext_phy(struct net_device *dev) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 1828c2ecf20Sopenharmony_ci u32 bmcr,advert,tmp; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Determine mii register values to set the speed */ 1858c2ecf20Sopenharmony_ci advert = amd8111e_mdio_read(dev, lp->ext_phy_addr, MII_ADVERTISE); 1868c2ecf20Sopenharmony_ci tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 1878c2ecf20Sopenharmony_ci switch (lp->ext_phy_option){ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci default: 1908c2ecf20Sopenharmony_ci case SPEED_AUTONEG: /* advertise all values */ 1918c2ecf20Sopenharmony_ci tmp |= ( ADVERTISE_10HALF|ADVERTISE_10FULL| 1928c2ecf20Sopenharmony_ci ADVERTISE_100HALF|ADVERTISE_100FULL) ; 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case SPEED10_HALF: 1958c2ecf20Sopenharmony_ci tmp |= ADVERTISE_10HALF; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case SPEED10_FULL: 1988c2ecf20Sopenharmony_ci tmp |= ADVERTISE_10FULL; 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci case SPEED100_HALF: 2018c2ecf20Sopenharmony_ci tmp |= ADVERTISE_100HALF; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case SPEED100_FULL: 2048c2ecf20Sopenharmony_ci tmp |= ADVERTISE_100FULL; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if(advert != tmp) 2098c2ecf20Sopenharmony_ci amd8111e_mdio_write(dev, lp->ext_phy_addr, MII_ADVERTISE, tmp); 2108c2ecf20Sopenharmony_ci /* Restart auto negotiation */ 2118c2ecf20Sopenharmony_ci bmcr = amd8111e_mdio_read(dev, lp->ext_phy_addr, MII_BMCR); 2128c2ecf20Sopenharmony_ci bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); 2138c2ecf20Sopenharmony_ci amd8111e_mdio_write(dev, lp->ext_phy_addr, MII_BMCR, bmcr); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* This function will unmap skb->data space and will free 2188c2ecf20Sopenharmony_ci * all transmit and receive skbuffs. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic int amd8111e_free_skbs(struct net_device *dev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 2238c2ecf20Sopenharmony_ci struct sk_buff *rx_skbuff; 2248c2ecf20Sopenharmony_ci int i; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Freeing transmit skbs */ 2278c2ecf20Sopenharmony_ci for(i = 0; i < NUM_TX_BUFFERS; i++){ 2288c2ecf20Sopenharmony_ci if(lp->tx_skbuff[i]){ 2298c2ecf20Sopenharmony_ci dma_unmap_single(&lp->pci_dev->dev, 2308c2ecf20Sopenharmony_ci lp->tx_dma_addr[i], 2318c2ecf20Sopenharmony_ci lp->tx_skbuff[i]->len, DMA_TO_DEVICE); 2328c2ecf20Sopenharmony_ci dev_kfree_skb (lp->tx_skbuff[i]); 2338c2ecf20Sopenharmony_ci lp->tx_skbuff[i] = NULL; 2348c2ecf20Sopenharmony_ci lp->tx_dma_addr[i] = 0; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci /* Freeing previously allocated receive buffers */ 2388c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RX_BUFFERS; i++){ 2398c2ecf20Sopenharmony_ci rx_skbuff = lp->rx_skbuff[i]; 2408c2ecf20Sopenharmony_ci if(rx_skbuff != NULL){ 2418c2ecf20Sopenharmony_ci dma_unmap_single(&lp->pci_dev->dev, 2428c2ecf20Sopenharmony_ci lp->rx_dma_addr[i], 2438c2ecf20Sopenharmony_ci lp->rx_buff_len - 2, DMA_FROM_DEVICE); 2448c2ecf20Sopenharmony_ci dev_kfree_skb(lp->rx_skbuff[i]); 2458c2ecf20Sopenharmony_ci lp->rx_skbuff[i] = NULL; 2468c2ecf20Sopenharmony_ci lp->rx_dma_addr[i] = 0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* This will set the receive buffer length corresponding 2548c2ecf20Sopenharmony_ci * to the mtu size of networkinterface. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic inline void amd8111e_set_rx_buff_len(struct net_device *dev) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 2598c2ecf20Sopenharmony_ci unsigned int mtu = dev->mtu; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (mtu > ETH_DATA_LEN){ 2628c2ecf20Sopenharmony_ci /* MTU + ethernet header + FCS 2638c2ecf20Sopenharmony_ci * + optional VLAN tag + skb reserve space 2 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci lp->rx_buff_len = mtu + ETH_HLEN + 10; 2668c2ecf20Sopenharmony_ci lp->options |= OPTION_JUMBO_ENABLE; 2678c2ecf20Sopenharmony_ci } else{ 2688c2ecf20Sopenharmony_ci lp->rx_buff_len = PKT_BUFF_SZ; 2698c2ecf20Sopenharmony_ci lp->options &= ~OPTION_JUMBO_ENABLE; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* This function will free all the previously allocated buffers, 2748c2ecf20Sopenharmony_ci * determine new receive buffer length and will allocate new receive buffers. 2758c2ecf20Sopenharmony_ci * This function also allocates and initializes both the transmitter 2768c2ecf20Sopenharmony_ci * and receive hardware descriptors. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic int amd8111e_init_ring(struct net_device *dev) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 2818c2ecf20Sopenharmony_ci int i; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci lp->rx_idx = lp->tx_idx = 0; 2848c2ecf20Sopenharmony_ci lp->tx_complete_idx = 0; 2858c2ecf20Sopenharmony_ci lp->tx_ring_idx = 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if(lp->opened) 2898c2ecf20Sopenharmony_ci /* Free previously allocated transmit and receive skbs */ 2908c2ecf20Sopenharmony_ci amd8111e_free_skbs(dev); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci else{ 2938c2ecf20Sopenharmony_ci /* allocate the tx and rx descriptors */ 2948c2ecf20Sopenharmony_ci lp->tx_ring = dma_alloc_coherent(&lp->pci_dev->dev, 2958c2ecf20Sopenharmony_ci sizeof(struct amd8111e_tx_dr) * NUM_TX_RING_DR, 2968c2ecf20Sopenharmony_ci &lp->tx_ring_dma_addr, GFP_ATOMIC); 2978c2ecf20Sopenharmony_ci if (!lp->tx_ring) 2988c2ecf20Sopenharmony_ci goto err_no_mem; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci lp->rx_ring = dma_alloc_coherent(&lp->pci_dev->dev, 3018c2ecf20Sopenharmony_ci sizeof(struct amd8111e_rx_dr) * NUM_RX_RING_DR, 3028c2ecf20Sopenharmony_ci &lp->rx_ring_dma_addr, GFP_ATOMIC); 3038c2ecf20Sopenharmony_ci if (!lp->rx_ring) 3048c2ecf20Sopenharmony_ci goto err_free_tx_ring; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Set new receive buff size */ 3088c2ecf20Sopenharmony_ci amd8111e_set_rx_buff_len(dev); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Allocating receive skbs */ 3118c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RX_BUFFERS; i++) { 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci lp->rx_skbuff[i] = netdev_alloc_skb(dev, lp->rx_buff_len); 3148c2ecf20Sopenharmony_ci if (!lp->rx_skbuff[i]) { 3158c2ecf20Sopenharmony_ci /* Release previos allocated skbs */ 3168c2ecf20Sopenharmony_ci for(--i; i >= 0 ;i--) 3178c2ecf20Sopenharmony_ci dev_kfree_skb(lp->rx_skbuff[i]); 3188c2ecf20Sopenharmony_ci goto err_free_rx_ring; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci skb_reserve(lp->rx_skbuff[i],2); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci /* Initilaizing receive descriptors */ 3238c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RX_BUFFERS; i++) { 3248c2ecf20Sopenharmony_ci lp->rx_dma_addr[i] = dma_map_single(&lp->pci_dev->dev, 3258c2ecf20Sopenharmony_ci lp->rx_skbuff[i]->data, 3268c2ecf20Sopenharmony_ci lp->rx_buff_len - 2, 3278c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci lp->rx_ring[i].buff_phy_addr = cpu_to_le32(lp->rx_dma_addr[i]); 3308c2ecf20Sopenharmony_ci lp->rx_ring[i].buff_count = cpu_to_le16(lp->rx_buff_len-2); 3318c2ecf20Sopenharmony_ci wmb(); 3328c2ecf20Sopenharmony_ci lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Initializing transmit descriptors */ 3368c2ecf20Sopenharmony_ci for (i = 0; i < NUM_TX_RING_DR; i++) { 3378c2ecf20Sopenharmony_ci lp->tx_ring[i].buff_phy_addr = 0; 3388c2ecf20Sopenharmony_ci lp->tx_ring[i].tx_flags = 0; 3398c2ecf20Sopenharmony_ci lp->tx_ring[i].buff_count = 0; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cierr_free_rx_ring: 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dma_free_coherent(&lp->pci_dev->dev, 3478c2ecf20Sopenharmony_ci sizeof(struct amd8111e_rx_dr) * NUM_RX_RING_DR, 3488c2ecf20Sopenharmony_ci lp->rx_ring, lp->rx_ring_dma_addr); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cierr_free_tx_ring: 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci dma_free_coherent(&lp->pci_dev->dev, 3538c2ecf20Sopenharmony_ci sizeof(struct amd8111e_tx_dr) * NUM_TX_RING_DR, 3548c2ecf20Sopenharmony_ci lp->tx_ring, lp->tx_ring_dma_addr); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cierr_no_mem: 3578c2ecf20Sopenharmony_ci return -ENOMEM; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/* This function will set the interrupt coalescing according 3618c2ecf20Sopenharmony_ci * to the input arguments 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_cistatic int amd8111e_set_coalesce(struct net_device *dev, enum coal_mode cmod) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci unsigned int timeout; 3668c2ecf20Sopenharmony_ci unsigned int event_count; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 3698c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 3708c2ecf20Sopenharmony_ci struct amd8111e_coalesce_conf *coal_conf = &lp->coal_conf; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci switch(cmod) 3748c2ecf20Sopenharmony_ci { 3758c2ecf20Sopenharmony_ci case RX_INTR_COAL : 3768c2ecf20Sopenharmony_ci timeout = coal_conf->rx_timeout; 3778c2ecf20Sopenharmony_ci event_count = coal_conf->rx_event_count; 3788c2ecf20Sopenharmony_ci if( timeout > MAX_TIMEOUT || 3798c2ecf20Sopenharmony_ci event_count > MAX_EVENT_COUNT ) 3808c2ecf20Sopenharmony_ci return -EINVAL; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci timeout = timeout * DELAY_TIMER_CONV; 3838c2ecf20Sopenharmony_ci writel(VAL0|STINTEN, mmio+INTEN0); 3848c2ecf20Sopenharmony_ci writel((u32)DLY_INT_A_R0|( event_count<< 16 )|timeout, 3858c2ecf20Sopenharmony_ci mmio+DLY_INT_A); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci case TX_INTR_COAL : 3898c2ecf20Sopenharmony_ci timeout = coal_conf->tx_timeout; 3908c2ecf20Sopenharmony_ci event_count = coal_conf->tx_event_count; 3918c2ecf20Sopenharmony_ci if( timeout > MAX_TIMEOUT || 3928c2ecf20Sopenharmony_ci event_count > MAX_EVENT_COUNT ) 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci timeout = timeout * DELAY_TIMER_CONV; 3978c2ecf20Sopenharmony_ci writel(VAL0|STINTEN,mmio+INTEN0); 3988c2ecf20Sopenharmony_ci writel((u32)DLY_INT_B_T0|( event_count<< 16 )|timeout, 3998c2ecf20Sopenharmony_ci mmio+DLY_INT_B); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci case DISABLE_COAL: 4038c2ecf20Sopenharmony_ci writel(0,mmio+STVAL); 4048c2ecf20Sopenharmony_ci writel(STINTEN, mmio+INTEN0); 4058c2ecf20Sopenharmony_ci writel(0, mmio +DLY_INT_B); 4068c2ecf20Sopenharmony_ci writel(0, mmio+DLY_INT_A); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case ENABLE_COAL: 4098c2ecf20Sopenharmony_ci /* Start the timer */ 4108c2ecf20Sopenharmony_ci writel((u32)SOFT_TIMER_FREQ, mmio+STVAL); /* 0.5 sec */ 4118c2ecf20Sopenharmony_ci writel(VAL0|STINTEN, mmio+INTEN0); 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci default: 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* This function initializes the device registers and starts the device. */ 4228c2ecf20Sopenharmony_cistatic int amd8111e_restart(struct net_device *dev) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 4258c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 4268c2ecf20Sopenharmony_ci int i,reg_val; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* stop the chip */ 4298c2ecf20Sopenharmony_ci writel(RUN, mmio + CMD0); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if(amd8111e_init_ring(dev)) 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* enable the port manager and set auto negotiation always */ 4358c2ecf20Sopenharmony_ci writel((u32) VAL1|EN_PMGR, mmio + CMD3 ); 4368c2ecf20Sopenharmony_ci writel((u32)XPHYANE|XPHYRST , mmio + CTRL2); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci amd8111e_set_ext_phy(dev); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* set control registers */ 4418c2ecf20Sopenharmony_ci reg_val = readl(mmio + CTRL1); 4428c2ecf20Sopenharmony_ci reg_val &= ~XMTSP_MASK; 4438c2ecf20Sopenharmony_ci writel( reg_val| XMTSP_128 | CACHE_ALIGN, mmio + CTRL1 ); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* enable interrupt */ 4468c2ecf20Sopenharmony_ci writel( APINT5EN | APINT4EN | APINT3EN | APINT2EN | APINT1EN | 4478c2ecf20Sopenharmony_ci APINT0EN | MIIPDTINTEN | MCCIINTEN | MCCINTEN | MREINTEN | 4488c2ecf20Sopenharmony_ci SPNDINTEN | MPINTEN | SINTEN | STINTEN, mmio + INTEN0); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* initialize tx and rx ring base addresses */ 4538c2ecf20Sopenharmony_ci writel((u32)lp->tx_ring_dma_addr,mmio + XMT_RING_BASE_ADDR0); 4548c2ecf20Sopenharmony_ci writel((u32)lp->rx_ring_dma_addr,mmio+ RCV_RING_BASE_ADDR0); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci writew((u32)NUM_TX_RING_DR, mmio + XMT_RING_LEN0); 4578c2ecf20Sopenharmony_ci writew((u16)NUM_RX_RING_DR, mmio + RCV_RING_LEN0); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* set default IPG to 96 */ 4608c2ecf20Sopenharmony_ci writew((u32)DEFAULT_IPG,mmio+IPG); 4618c2ecf20Sopenharmony_ci writew((u32)(DEFAULT_IPG-IFS1_DELTA), mmio + IFS1); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if(lp->options & OPTION_JUMBO_ENABLE){ 4648c2ecf20Sopenharmony_ci writel((u32)VAL2|JUMBO, mmio + CMD3); 4658c2ecf20Sopenharmony_ci /* Reset REX_UFLO */ 4668c2ecf20Sopenharmony_ci writel( REX_UFLO, mmio + CMD2); 4678c2ecf20Sopenharmony_ci /* Should not set REX_UFLO for jumbo frames */ 4688c2ecf20Sopenharmony_ci writel( VAL0 | APAD_XMT|REX_RTRY , mmio + CMD2); 4698c2ecf20Sopenharmony_ci }else{ 4708c2ecf20Sopenharmony_ci writel( VAL0 | APAD_XMT | REX_RTRY|REX_UFLO, mmio + CMD2); 4718c2ecf20Sopenharmony_ci writel((u32)JUMBO, mmio + CMD3); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 4758c2ecf20Sopenharmony_ci writel((u32) VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3); 4768c2ecf20Sopenharmony_ci#endif 4778c2ecf20Sopenharmony_ci writel( VAL0 | APAD_XMT | REX_RTRY, mmio + CMD2 ); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Setting the MAC address to the device */ 4808c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 4818c2ecf20Sopenharmony_ci writeb( dev->dev_addr[i], mmio + PADR + i ); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Enable interrupt coalesce */ 4848c2ecf20Sopenharmony_ci if(lp->options & OPTION_INTR_COAL_ENABLE){ 4858c2ecf20Sopenharmony_ci netdev_info(dev, "Interrupt Coalescing Enabled.\n"); 4868c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,ENABLE_COAL); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* set RUN bit to start the chip */ 4908c2ecf20Sopenharmony_ci writel(VAL2 | RDMD0, mmio + CMD0); 4918c2ecf20Sopenharmony_ci writel(VAL0 | INTREN | RUN, mmio + CMD0); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* To avoid PCI posting bug */ 4948c2ecf20Sopenharmony_ci readl(mmio+CMD0); 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* This function clears necessary the device registers. */ 4998c2ecf20Sopenharmony_cistatic void amd8111e_init_hw_default(struct amd8111e_priv *lp) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci unsigned int reg_val; 5028c2ecf20Sopenharmony_ci unsigned int logic_filter[2] ={0,}; 5038c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* stop the chip */ 5078c2ecf20Sopenharmony_ci writel(RUN, mmio + CMD0); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */ 5108c2ecf20Sopenharmony_ci writew( 0x8100 | lp->ext_phy_addr, mmio + AUTOPOLL0); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Clear RCV_RING_BASE_ADDR */ 5138c2ecf20Sopenharmony_ci writel(0, mmio + RCV_RING_BASE_ADDR0); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Clear XMT_RING_BASE_ADDR */ 5168c2ecf20Sopenharmony_ci writel(0, mmio + XMT_RING_BASE_ADDR0); 5178c2ecf20Sopenharmony_ci writel(0, mmio + XMT_RING_BASE_ADDR1); 5188c2ecf20Sopenharmony_ci writel(0, mmio + XMT_RING_BASE_ADDR2); 5198c2ecf20Sopenharmony_ci writel(0, mmio + XMT_RING_BASE_ADDR3); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* Clear CMD0 */ 5228c2ecf20Sopenharmony_ci writel(CMD0_CLEAR,mmio + CMD0); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Clear CMD2 */ 5258c2ecf20Sopenharmony_ci writel(CMD2_CLEAR, mmio +CMD2); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Clear CMD7 */ 5288c2ecf20Sopenharmony_ci writel(CMD7_CLEAR , mmio + CMD7); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Clear DLY_INT_A and DLY_INT_B */ 5318c2ecf20Sopenharmony_ci writel(0x0, mmio + DLY_INT_A); 5328c2ecf20Sopenharmony_ci writel(0x0, mmio + DLY_INT_B); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Clear FLOW_CONTROL */ 5358c2ecf20Sopenharmony_ci writel(0x0, mmio + FLOW_CONTROL); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Clear INT0 write 1 to clear register */ 5388c2ecf20Sopenharmony_ci reg_val = readl(mmio + INT0); 5398c2ecf20Sopenharmony_ci writel(reg_val, mmio + INT0); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* Clear STVAL */ 5428c2ecf20Sopenharmony_ci writel(0x0, mmio + STVAL); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Clear INTEN0 */ 5458c2ecf20Sopenharmony_ci writel( INTEN0_CLEAR, mmio + INTEN0); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Clear LADRF */ 5488c2ecf20Sopenharmony_ci writel(0x0 , mmio + LADRF); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Set SRAM_SIZE & SRAM_BOUNDARY registers */ 5518c2ecf20Sopenharmony_ci writel( 0x80010,mmio + SRAM_SIZE); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Clear RCV_RING0_LEN */ 5548c2ecf20Sopenharmony_ci writel(0x0, mmio + RCV_RING_LEN0); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Clear XMT_RING0/1/2/3_LEN */ 5578c2ecf20Sopenharmony_ci writel(0x0, mmio + XMT_RING_LEN0); 5588c2ecf20Sopenharmony_ci writel(0x0, mmio + XMT_RING_LEN1); 5598c2ecf20Sopenharmony_ci writel(0x0, mmio + XMT_RING_LEN2); 5608c2ecf20Sopenharmony_ci writel(0x0, mmio + XMT_RING_LEN3); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Clear XMT_RING_LIMIT */ 5638c2ecf20Sopenharmony_ci writel(0x0, mmio + XMT_RING_LIMIT); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Clear MIB */ 5668c2ecf20Sopenharmony_ci writew(MIB_CLEAR, mmio + MIB_ADDR); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Clear LARF */ 5698c2ecf20Sopenharmony_ci amd8111e_writeq(*(u64 *)logic_filter, mmio + LADRF); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* SRAM_SIZE register */ 5728c2ecf20Sopenharmony_ci reg_val = readl(mmio + SRAM_SIZE); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if(lp->options & OPTION_JUMBO_ENABLE) 5758c2ecf20Sopenharmony_ci writel( VAL2|JUMBO, mmio + CMD3); 5768c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 5778c2ecf20Sopenharmony_ci writel(VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3 ); 5788c2ecf20Sopenharmony_ci#endif 5798c2ecf20Sopenharmony_ci /* Set default value to CTRL1 Register */ 5808c2ecf20Sopenharmony_ci writel(CTRL1_DEFAULT, mmio + CTRL1); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* To avoid PCI posting bug */ 5838c2ecf20Sopenharmony_ci readl(mmio + CMD2); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* This function disables the interrupt and clears all the pending 5888c2ecf20Sopenharmony_ci * interrupts in INT0 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_cistatic void amd8111e_disable_interrupt(struct amd8111e_priv *lp) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci u32 intr0; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Disable interrupt */ 5958c2ecf20Sopenharmony_ci writel(INTREN, lp->mmio + CMD0); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Clear INT0 */ 5988c2ecf20Sopenharmony_ci intr0 = readl(lp->mmio + INT0); 5998c2ecf20Sopenharmony_ci writel(intr0, lp->mmio + INT0); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* To avoid PCI posting bug */ 6028c2ecf20Sopenharmony_ci readl(lp->mmio + INT0); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci/* This function stops the chip. */ 6078c2ecf20Sopenharmony_cistatic void amd8111e_stop_chip(struct amd8111e_priv *lp) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci writel(RUN, lp->mmio + CMD0); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* To avoid PCI posting bug */ 6128c2ecf20Sopenharmony_ci readl(lp->mmio + CMD0); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/* This function frees the transmiter and receiver descriptor rings. */ 6168c2ecf20Sopenharmony_cistatic void amd8111e_free_ring(struct amd8111e_priv *lp) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci /* Free transmit and receive descriptor rings */ 6198c2ecf20Sopenharmony_ci if(lp->rx_ring){ 6208c2ecf20Sopenharmony_ci dma_free_coherent(&lp->pci_dev->dev, 6218c2ecf20Sopenharmony_ci sizeof(struct amd8111e_rx_dr) * NUM_RX_RING_DR, 6228c2ecf20Sopenharmony_ci lp->rx_ring, lp->rx_ring_dma_addr); 6238c2ecf20Sopenharmony_ci lp->rx_ring = NULL; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if(lp->tx_ring){ 6278c2ecf20Sopenharmony_ci dma_free_coherent(&lp->pci_dev->dev, 6288c2ecf20Sopenharmony_ci sizeof(struct amd8111e_tx_dr) * NUM_TX_RING_DR, 6298c2ecf20Sopenharmony_ci lp->tx_ring, lp->tx_ring_dma_addr); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci lp->tx_ring = NULL; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci/* This function will free all the transmit skbs that are actually 6378c2ecf20Sopenharmony_ci * transmitted by the device. It will check the ownership of the 6388c2ecf20Sopenharmony_ci * skb before freeing the skb. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_cistatic int amd8111e_tx(struct net_device *dev) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 6438c2ecf20Sopenharmony_ci int tx_index; 6448c2ecf20Sopenharmony_ci int status; 6458c2ecf20Sopenharmony_ci /* Complete all the transmit packet */ 6468c2ecf20Sopenharmony_ci while (lp->tx_complete_idx != lp->tx_idx){ 6478c2ecf20Sopenharmony_ci tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK; 6488c2ecf20Sopenharmony_ci status = le16_to_cpu(lp->tx_ring[tx_index].tx_flags); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if(status & OWN_BIT) 6518c2ecf20Sopenharmony_ci break; /* It still hasn't been Txed */ 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].buff_phy_addr = 0; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* We must free the original skb */ 6568c2ecf20Sopenharmony_ci if (lp->tx_skbuff[tx_index]) { 6578c2ecf20Sopenharmony_ci dma_unmap_single(&lp->pci_dev->dev, 6588c2ecf20Sopenharmony_ci lp->tx_dma_addr[tx_index], 6598c2ecf20Sopenharmony_ci lp->tx_skbuff[tx_index]->len, 6608c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6618c2ecf20Sopenharmony_ci dev_consume_skb_irq(lp->tx_skbuff[tx_index]); 6628c2ecf20Sopenharmony_ci lp->tx_skbuff[tx_index] = NULL; 6638c2ecf20Sopenharmony_ci lp->tx_dma_addr[tx_index] = 0; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci lp->tx_complete_idx++; 6668c2ecf20Sopenharmony_ci /*COAL update tx coalescing parameters */ 6678c2ecf20Sopenharmony_ci lp->coal_conf.tx_packets++; 6688c2ecf20Sopenharmony_ci lp->coal_conf.tx_bytes += 6698c2ecf20Sopenharmony_ci le16_to_cpu(lp->tx_ring[tx_index].buff_count); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev) && 6728c2ecf20Sopenharmony_ci lp->tx_complete_idx > lp->tx_idx - NUM_TX_BUFFERS +2){ 6738c2ecf20Sopenharmony_ci /* The ring is no longer full, clear tbusy. */ 6748c2ecf20Sopenharmony_ci /* lp->tx_full = 0; */ 6758c2ecf20Sopenharmony_ci netif_wake_queue (dev); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/* This function handles the driver receive operation in polling mode */ 6828c2ecf20Sopenharmony_cistatic int amd8111e_rx_poll(struct napi_struct *napi, int budget) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = container_of(napi, struct amd8111e_priv, napi); 6858c2ecf20Sopenharmony_ci struct net_device *dev = lp->amd8111e_net_dev; 6868c2ecf20Sopenharmony_ci int rx_index = lp->rx_idx & RX_RING_DR_MOD_MASK; 6878c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 6888c2ecf20Sopenharmony_ci struct sk_buff *skb,*new_skb; 6898c2ecf20Sopenharmony_ci int min_pkt_len, status; 6908c2ecf20Sopenharmony_ci int num_rx_pkt = 0; 6918c2ecf20Sopenharmony_ci short pkt_len; 6928c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 6938c2ecf20Sopenharmony_ci short vtag; 6948c2ecf20Sopenharmony_ci#endif 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci while (num_rx_pkt < budget) { 6978c2ecf20Sopenharmony_ci status = le16_to_cpu(lp->rx_ring[rx_index].rx_flags); 6988c2ecf20Sopenharmony_ci if (status & OWN_BIT) 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* There is a tricky error noted by John Murphy, 7028c2ecf20Sopenharmony_ci * <murf@perftech.com> to Russ Nelson: Even with 7038c2ecf20Sopenharmony_ci * full-sized * buffers it's possible for a 7048c2ecf20Sopenharmony_ci * jabber packet to use two buffers, with only 7058c2ecf20Sopenharmony_ci * the last correctly noting the error. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci if (status & ERR_BIT) { 7088c2ecf20Sopenharmony_ci /* resetting flags */ 7098c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; 7108c2ecf20Sopenharmony_ci goto err_next_pkt; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci /* check for STP and ENP */ 7138c2ecf20Sopenharmony_ci if (!((status & STP_BIT) && (status & ENP_BIT))){ 7148c2ecf20Sopenharmony_ci /* resetting flags */ 7158c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; 7168c2ecf20Sopenharmony_ci goto err_next_pkt; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci pkt_len = le16_to_cpu(lp->rx_ring[rx_index].msg_count) - 4; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 7218c2ecf20Sopenharmony_ci vtag = status & TT_MASK; 7228c2ecf20Sopenharmony_ci /* MAC will strip vlan tag */ 7238c2ecf20Sopenharmony_ci if (vtag != 0) 7248c2ecf20Sopenharmony_ci min_pkt_len = MIN_PKT_LEN - 4; 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci#endif 7278c2ecf20Sopenharmony_ci min_pkt_len = MIN_PKT_LEN; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (pkt_len < min_pkt_len) { 7308c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; 7318c2ecf20Sopenharmony_ci lp->drv_rx_errors++; 7328c2ecf20Sopenharmony_ci goto err_next_pkt; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci new_skb = netdev_alloc_skb(dev, lp->rx_buff_len); 7358c2ecf20Sopenharmony_ci if (!new_skb) { 7368c2ecf20Sopenharmony_ci /* if allocation fail, 7378c2ecf20Sopenharmony_ci * ignore that pkt and go to next one 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; 7408c2ecf20Sopenharmony_ci lp->drv_rx_errors++; 7418c2ecf20Sopenharmony_ci goto err_next_pkt; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci skb_reserve(new_skb, 2); 7458c2ecf20Sopenharmony_ci skb = lp->rx_skbuff[rx_index]; 7468c2ecf20Sopenharmony_ci dma_unmap_single(&lp->pci_dev->dev, lp->rx_dma_addr[rx_index], 7478c2ecf20Sopenharmony_ci lp->rx_buff_len - 2, DMA_FROM_DEVICE); 7488c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); 7498c2ecf20Sopenharmony_ci lp->rx_skbuff[rx_index] = new_skb; 7508c2ecf20Sopenharmony_ci lp->rx_dma_addr[rx_index] = dma_map_single(&lp->pci_dev->dev, 7518c2ecf20Sopenharmony_ci new_skb->data, 7528c2ecf20Sopenharmony_ci lp->rx_buff_len - 2, 7538c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 7588c2ecf20Sopenharmony_ci if (vtag == TT_VLAN_TAGGED){ 7598c2ecf20Sopenharmony_ci u16 vlan_tag = le16_to_cpu(lp->rx_ring[rx_index].tag_ctrl_info); 7608c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci#endif 7638c2ecf20Sopenharmony_ci napi_gro_receive(napi, skb); 7648c2ecf20Sopenharmony_ci /* COAL update rx coalescing parameters */ 7658c2ecf20Sopenharmony_ci lp->coal_conf.rx_packets++; 7668c2ecf20Sopenharmony_ci lp->coal_conf.rx_bytes += pkt_len; 7678c2ecf20Sopenharmony_ci num_rx_pkt++; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cierr_next_pkt: 7708c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].buff_phy_addr 7718c2ecf20Sopenharmony_ci = cpu_to_le32(lp->rx_dma_addr[rx_index]); 7728c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].buff_count = 7738c2ecf20Sopenharmony_ci cpu_to_le16(lp->rx_buff_len-2); 7748c2ecf20Sopenharmony_ci wmb(); 7758c2ecf20Sopenharmony_ci lp->rx_ring[rx_index].rx_flags |= cpu_to_le16(OWN_BIT); 7768c2ecf20Sopenharmony_ci rx_index = (++lp->rx_idx) & RX_RING_DR_MOD_MASK; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (num_rx_pkt < budget && napi_complete_done(napi, num_rx_pkt)) { 7808c2ecf20Sopenharmony_ci unsigned long flags; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Receive descriptor is empty now */ 7838c2ecf20Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 7848c2ecf20Sopenharmony_ci writel(VAL0|RINTEN0, mmio + INTEN0); 7858c2ecf20Sopenharmony_ci writel(VAL2 | RDMD0, mmio + CMD0); 7868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return num_rx_pkt; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/* This function will indicate the link status to the kernel. */ 7938c2ecf20Sopenharmony_cistatic int amd8111e_link_change(struct net_device *dev) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 7968c2ecf20Sopenharmony_ci int status0,speed; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* read the link change */ 7998c2ecf20Sopenharmony_ci status0 = readl(lp->mmio + STAT0); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if(status0 & LINK_STATS){ 8028c2ecf20Sopenharmony_ci if(status0 & AUTONEG_COMPLETE) 8038c2ecf20Sopenharmony_ci lp->link_config.autoneg = AUTONEG_ENABLE; 8048c2ecf20Sopenharmony_ci else 8058c2ecf20Sopenharmony_ci lp->link_config.autoneg = AUTONEG_DISABLE; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if(status0 & FULL_DPLX) 8088c2ecf20Sopenharmony_ci lp->link_config.duplex = DUPLEX_FULL; 8098c2ecf20Sopenharmony_ci else 8108c2ecf20Sopenharmony_ci lp->link_config.duplex = DUPLEX_HALF; 8118c2ecf20Sopenharmony_ci speed = (status0 & SPEED_MASK) >> 7; 8128c2ecf20Sopenharmony_ci if(speed == PHY_SPEED_10) 8138c2ecf20Sopenharmony_ci lp->link_config.speed = SPEED_10; 8148c2ecf20Sopenharmony_ci else if(speed == PHY_SPEED_100) 8158c2ecf20Sopenharmony_ci lp->link_config.speed = SPEED_100; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci netdev_info(dev, "Link is Up. Speed is %s Mbps %s Duplex\n", 8188c2ecf20Sopenharmony_ci (lp->link_config.speed == SPEED_100) ? 8198c2ecf20Sopenharmony_ci "100" : "10", 8208c2ecf20Sopenharmony_ci (lp->link_config.duplex == DUPLEX_FULL) ? 8218c2ecf20Sopenharmony_ci "Full" : "Half"); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci netif_carrier_on(dev); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci else{ 8268c2ecf20Sopenharmony_ci lp->link_config.speed = SPEED_INVALID; 8278c2ecf20Sopenharmony_ci lp->link_config.duplex = DUPLEX_INVALID; 8288c2ecf20Sopenharmony_ci lp->link_config.autoneg = AUTONEG_INVALID; 8298c2ecf20Sopenharmony_ci netdev_info(dev, "Link is Down.\n"); 8308c2ecf20Sopenharmony_ci netif_carrier_off(dev); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/* This function reads the mib counters. */ 8378c2ecf20Sopenharmony_cistatic int amd8111e_read_mib(void __iomem *mmio, u8 MIB_COUNTER) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci unsigned int status; 8408c2ecf20Sopenharmony_ci unsigned int data; 8418c2ecf20Sopenharmony_ci unsigned int repeat = REPEAT_CNT; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci writew( MIB_RD_CMD | MIB_COUNTER, mmio + MIB_ADDR); 8448c2ecf20Sopenharmony_ci do { 8458c2ecf20Sopenharmony_ci status = readw(mmio + MIB_ADDR); 8468c2ecf20Sopenharmony_ci udelay(2); /* controller takes MAX 2 us to get mib data */ 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci while (--repeat && (status & MIB_CMD_ACTIVE)); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci data = readl(mmio + MIB_DATA); 8518c2ecf20Sopenharmony_ci return data; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci/* This function reads the mib registers and returns the hardware statistics. 8558c2ecf20Sopenharmony_ci * It updates previous internal driver statistics with new values. 8568c2ecf20Sopenharmony_ci */ 8578c2ecf20Sopenharmony_cistatic struct net_device_stats *amd8111e_get_stats(struct net_device *dev) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 8608c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 8618c2ecf20Sopenharmony_ci unsigned long flags; 8628c2ecf20Sopenharmony_ci struct net_device_stats *new_stats = &dev->stats; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (!lp->opened) 8658c2ecf20Sopenharmony_ci return new_stats; 8668c2ecf20Sopenharmony_ci spin_lock_irqsave (&lp->lock, flags); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* stats.rx_packets */ 8698c2ecf20Sopenharmony_ci new_stats->rx_packets = amd8111e_read_mib(mmio, rcv_broadcast_pkts)+ 8708c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_multicast_pkts)+ 8718c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_unicast_pkts); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* stats.tx_packets */ 8748c2ecf20Sopenharmony_ci new_stats->tx_packets = amd8111e_read_mib(mmio, xmt_packets); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /*stats.rx_bytes */ 8778c2ecf20Sopenharmony_ci new_stats->rx_bytes = amd8111e_read_mib(mmio, rcv_octets); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* stats.tx_bytes */ 8808c2ecf20Sopenharmony_ci new_stats->tx_bytes = amd8111e_read_mib(mmio, xmt_octets); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* stats.rx_errors */ 8838c2ecf20Sopenharmony_ci /* hw errors + errors driver reported */ 8848c2ecf20Sopenharmony_ci new_stats->rx_errors = amd8111e_read_mib(mmio, rcv_undersize_pkts)+ 8858c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_fragments)+ 8868c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_jabbers)+ 8878c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_alignment_errors)+ 8888c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_fcs_errors)+ 8898c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_miss_pkts)+ 8908c2ecf20Sopenharmony_ci lp->drv_rx_errors; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* stats.tx_errors */ 8938c2ecf20Sopenharmony_ci new_stats->tx_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* stats.rx_dropped*/ 8968c2ecf20Sopenharmony_ci new_stats->rx_dropped = amd8111e_read_mib(mmio, rcv_miss_pkts); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* stats.tx_dropped*/ 8998c2ecf20Sopenharmony_ci new_stats->tx_dropped = amd8111e_read_mib(mmio, xmt_underrun_pkts); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* stats.multicast*/ 9028c2ecf20Sopenharmony_ci new_stats->multicast = amd8111e_read_mib(mmio, rcv_multicast_pkts); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* stats.collisions*/ 9058c2ecf20Sopenharmony_ci new_stats->collisions = amd8111e_read_mib(mmio, xmt_collisions); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* stats.rx_length_errors*/ 9088c2ecf20Sopenharmony_ci new_stats->rx_length_errors = 9098c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_undersize_pkts)+ 9108c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_oversize_pkts); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* stats.rx_over_errors*/ 9138c2ecf20Sopenharmony_ci new_stats->rx_over_errors = amd8111e_read_mib(mmio, rcv_miss_pkts); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* stats.rx_crc_errors*/ 9168c2ecf20Sopenharmony_ci new_stats->rx_crc_errors = amd8111e_read_mib(mmio, rcv_fcs_errors); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* stats.rx_frame_errors*/ 9198c2ecf20Sopenharmony_ci new_stats->rx_frame_errors = 9208c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, rcv_alignment_errors); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* stats.rx_fifo_errors */ 9238c2ecf20Sopenharmony_ci new_stats->rx_fifo_errors = amd8111e_read_mib(mmio, rcv_miss_pkts); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* stats.rx_missed_errors */ 9268c2ecf20Sopenharmony_ci new_stats->rx_missed_errors = amd8111e_read_mib(mmio, rcv_miss_pkts); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci /* stats.tx_aborted_errors*/ 9298c2ecf20Sopenharmony_ci new_stats->tx_aborted_errors = 9308c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, xmt_excessive_collision); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* stats.tx_carrier_errors*/ 9338c2ecf20Sopenharmony_ci new_stats->tx_carrier_errors = 9348c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, xmt_loss_carrier); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci /* stats.tx_fifo_errors*/ 9378c2ecf20Sopenharmony_ci new_stats->tx_fifo_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* stats.tx_window_errors*/ 9408c2ecf20Sopenharmony_ci new_stats->tx_window_errors = 9418c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, xmt_late_collision); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Reset the mibs for collecting new statistics */ 9448c2ecf20Sopenharmony_ci /* writew(MIB_CLEAR, mmio + MIB_ADDR);*/ 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&lp->lock, flags); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return new_stats; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci/* This function recalculate the interrupt coalescing mode on every interrupt 9528c2ecf20Sopenharmony_ci * according to the datarate and the packet rate. 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_cistatic int amd8111e_calc_coalesce(struct net_device *dev) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 9578c2ecf20Sopenharmony_ci struct amd8111e_coalesce_conf *coal_conf = &lp->coal_conf; 9588c2ecf20Sopenharmony_ci int tx_pkt_rate; 9598c2ecf20Sopenharmony_ci int rx_pkt_rate; 9608c2ecf20Sopenharmony_ci int tx_data_rate; 9618c2ecf20Sopenharmony_ci int rx_data_rate; 9628c2ecf20Sopenharmony_ci int rx_pkt_size; 9638c2ecf20Sopenharmony_ci int tx_pkt_size; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci tx_pkt_rate = coal_conf->tx_packets - coal_conf->tx_prev_packets; 9668c2ecf20Sopenharmony_ci coal_conf->tx_prev_packets = coal_conf->tx_packets; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci tx_data_rate = coal_conf->tx_bytes - coal_conf->tx_prev_bytes; 9698c2ecf20Sopenharmony_ci coal_conf->tx_prev_bytes = coal_conf->tx_bytes; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci rx_pkt_rate = coal_conf->rx_packets - coal_conf->rx_prev_packets; 9728c2ecf20Sopenharmony_ci coal_conf->rx_prev_packets = coal_conf->rx_packets; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci rx_data_rate = coal_conf->rx_bytes - coal_conf->rx_prev_bytes; 9758c2ecf20Sopenharmony_ci coal_conf->rx_prev_bytes = coal_conf->rx_bytes; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if(rx_pkt_rate < 800){ 9788c2ecf20Sopenharmony_ci if(coal_conf->rx_coal_type != NO_COALESCE){ 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci coal_conf->rx_timeout = 0x0; 9818c2ecf20Sopenharmony_ci coal_conf->rx_event_count = 0; 9828c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,RX_INTR_COAL); 9838c2ecf20Sopenharmony_ci coal_conf->rx_coal_type = NO_COALESCE; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci else{ 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci rx_pkt_size = rx_data_rate/rx_pkt_rate; 9898c2ecf20Sopenharmony_ci if (rx_pkt_size < 128){ 9908c2ecf20Sopenharmony_ci if(coal_conf->rx_coal_type != NO_COALESCE){ 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci coal_conf->rx_timeout = 0; 9938c2ecf20Sopenharmony_ci coal_conf->rx_event_count = 0; 9948c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,RX_INTR_COAL); 9958c2ecf20Sopenharmony_ci coal_conf->rx_coal_type = NO_COALESCE; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci else if ( (rx_pkt_size >= 128) && (rx_pkt_size < 512) ){ 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if(coal_conf->rx_coal_type != LOW_COALESCE){ 10028c2ecf20Sopenharmony_ci coal_conf->rx_timeout = 1; 10038c2ecf20Sopenharmony_ci coal_conf->rx_event_count = 4; 10048c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,RX_INTR_COAL); 10058c2ecf20Sopenharmony_ci coal_conf->rx_coal_type = LOW_COALESCE; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci else if ((rx_pkt_size >= 512) && (rx_pkt_size < 1024)){ 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if(coal_conf->rx_coal_type != MEDIUM_COALESCE){ 10118c2ecf20Sopenharmony_ci coal_conf->rx_timeout = 1; 10128c2ecf20Sopenharmony_ci coal_conf->rx_event_count = 4; 10138c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,RX_INTR_COAL); 10148c2ecf20Sopenharmony_ci coal_conf->rx_coal_type = MEDIUM_COALESCE; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci else if(rx_pkt_size >= 1024){ 10198c2ecf20Sopenharmony_ci if(coal_conf->rx_coal_type != HIGH_COALESCE){ 10208c2ecf20Sopenharmony_ci coal_conf->rx_timeout = 2; 10218c2ecf20Sopenharmony_ci coal_conf->rx_event_count = 3; 10228c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,RX_INTR_COAL); 10238c2ecf20Sopenharmony_ci coal_conf->rx_coal_type = HIGH_COALESCE; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci /* NOW FOR TX INTR COALESC */ 10288c2ecf20Sopenharmony_ci if(tx_pkt_rate < 800){ 10298c2ecf20Sopenharmony_ci if(coal_conf->tx_coal_type != NO_COALESCE){ 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci coal_conf->tx_timeout = 0x0; 10328c2ecf20Sopenharmony_ci coal_conf->tx_event_count = 0; 10338c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,TX_INTR_COAL); 10348c2ecf20Sopenharmony_ci coal_conf->tx_coal_type = NO_COALESCE; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci else{ 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci tx_pkt_size = tx_data_rate/tx_pkt_rate; 10408c2ecf20Sopenharmony_ci if (tx_pkt_size < 128){ 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if(coal_conf->tx_coal_type != NO_COALESCE){ 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci coal_conf->tx_timeout = 0; 10458c2ecf20Sopenharmony_ci coal_conf->tx_event_count = 0; 10468c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,TX_INTR_COAL); 10478c2ecf20Sopenharmony_ci coal_conf->tx_coal_type = NO_COALESCE; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci else if ( (tx_pkt_size >= 128) && (tx_pkt_size < 512) ){ 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if(coal_conf->tx_coal_type != LOW_COALESCE){ 10548c2ecf20Sopenharmony_ci coal_conf->tx_timeout = 1; 10558c2ecf20Sopenharmony_ci coal_conf->tx_event_count = 2; 10568c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,TX_INTR_COAL); 10578c2ecf20Sopenharmony_ci coal_conf->tx_coal_type = LOW_COALESCE; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci else if ((tx_pkt_size >= 512) && (tx_pkt_size < 1024)){ 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if(coal_conf->tx_coal_type != MEDIUM_COALESCE){ 10648c2ecf20Sopenharmony_ci coal_conf->tx_timeout = 2; 10658c2ecf20Sopenharmony_ci coal_conf->tx_event_count = 5; 10668c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev,TX_INTR_COAL); 10678c2ecf20Sopenharmony_ci coal_conf->tx_coal_type = MEDIUM_COALESCE; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci } else if (tx_pkt_size >= 1024) { 10708c2ecf20Sopenharmony_ci if (coal_conf->tx_coal_type != HIGH_COALESCE) { 10718c2ecf20Sopenharmony_ci coal_conf->tx_timeout = 4; 10728c2ecf20Sopenharmony_ci coal_conf->tx_event_count = 8; 10738c2ecf20Sopenharmony_ci amd8111e_set_coalesce(dev, TX_INTR_COAL); 10748c2ecf20Sopenharmony_ci coal_conf->tx_coal_type = HIGH_COALESCE; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci return 0; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci/* This is device interrupt function. It handles transmit, 10838c2ecf20Sopenharmony_ci * receive,link change and hardware timer interrupts. 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_cistatic irqreturn_t amd8111e_interrupt(int irq, void *dev_id) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_id; 10898c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 10908c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 10918c2ecf20Sopenharmony_ci unsigned int intr0, intren0; 10928c2ecf20Sopenharmony_ci unsigned int handled = 1; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if(unlikely(dev == NULL)) 10958c2ecf20Sopenharmony_ci return IRQ_NONE; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci spin_lock(&lp->lock); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* disabling interrupt */ 11008c2ecf20Sopenharmony_ci writel(INTREN, mmio + CMD0); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* Read interrupt status */ 11038c2ecf20Sopenharmony_ci intr0 = readl(mmio + INT0); 11048c2ecf20Sopenharmony_ci intren0 = readl(mmio + INTEN0); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* Process all the INT event until INTR bit is clear. */ 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (!(intr0 & INTR)){ 11098c2ecf20Sopenharmony_ci handled = 0; 11108c2ecf20Sopenharmony_ci goto err_no_interrupt; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* Current driver processes 4 interrupts : RINT,TINT,LCINT,STINT */ 11148c2ecf20Sopenharmony_ci writel(intr0, mmio + INT0); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* Check if Receive Interrupt has occurred. */ 11178c2ecf20Sopenharmony_ci if (intr0 & RINT0) { 11188c2ecf20Sopenharmony_ci if (napi_schedule_prep(&lp->napi)) { 11198c2ecf20Sopenharmony_ci /* Disable receive interupts */ 11208c2ecf20Sopenharmony_ci writel(RINTEN0, mmio + INTEN0); 11218c2ecf20Sopenharmony_ci /* Schedule a polling routine */ 11228c2ecf20Sopenharmony_ci __napi_schedule(&lp->napi); 11238c2ecf20Sopenharmony_ci } else if (intren0 & RINTEN0) { 11248c2ecf20Sopenharmony_ci netdev_dbg(dev, "************Driver bug! interrupt while in poll\n"); 11258c2ecf20Sopenharmony_ci /* Fix by disable receive interrupts */ 11268c2ecf20Sopenharmony_ci writel(RINTEN0, mmio + INTEN0); 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Check if Transmit Interrupt has occurred. */ 11318c2ecf20Sopenharmony_ci if (intr0 & TINT0) 11328c2ecf20Sopenharmony_ci amd8111e_tx(dev); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* Check if Link Change Interrupt has occurred. */ 11358c2ecf20Sopenharmony_ci if (intr0 & LCINT) 11368c2ecf20Sopenharmony_ci amd8111e_link_change(dev); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci /* Check if Hardware Timer Interrupt has occurred. */ 11398c2ecf20Sopenharmony_ci if (intr0 & STINT) 11408c2ecf20Sopenharmony_ci amd8111e_calc_coalesce(dev); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cierr_no_interrupt: 11438c2ecf20Sopenharmony_ci writel( VAL0 | INTREN,mmio + CMD0); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci spin_unlock(&lp->lock); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 11518c2ecf20Sopenharmony_cistatic void amd8111e_poll(struct net_device *dev) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci unsigned long flags; 11548c2ecf20Sopenharmony_ci local_irq_save(flags); 11558c2ecf20Sopenharmony_ci amd8111e_interrupt(0, dev); 11568c2ecf20Sopenharmony_ci local_irq_restore(flags); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci#endif 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci/* This function closes the network interface and updates 11628c2ecf20Sopenharmony_ci * the statistics so that most recent statistics will be 11638c2ecf20Sopenharmony_ci * available after the interface is down. 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_cistatic int amd8111e_close(struct net_device *dev) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 11688c2ecf20Sopenharmony_ci netif_stop_queue(dev); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci napi_disable(&lp->napi); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci amd8111e_disable_interrupt(lp); 11758c2ecf20Sopenharmony_ci amd8111e_stop_chip(lp); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* Free transmit and receive skbs */ 11788c2ecf20Sopenharmony_ci amd8111e_free_skbs(lp->amd8111e_net_dev); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci netif_carrier_off(lp->amd8111e_net_dev); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* Delete ipg timer */ 11838c2ecf20Sopenharmony_ci if(lp->options & OPTION_DYN_IPG_ENABLE) 11848c2ecf20Sopenharmony_ci del_timer_sync(&lp->ipg_data.ipg_timer); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 11878c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 11888c2ecf20Sopenharmony_ci amd8111e_free_ring(lp); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Update the statistics before closing */ 11918c2ecf20Sopenharmony_ci amd8111e_get_stats(dev); 11928c2ecf20Sopenharmony_ci lp->opened = 0; 11938c2ecf20Sopenharmony_ci return 0; 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci/* This function opens new interface.It requests irq for the device, 11978c2ecf20Sopenharmony_ci * initializes the device,buffers and descriptors, and starts the device. 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_cistatic int amd8111e_open(struct net_device *dev) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if(dev->irq ==0 || request_irq(dev->irq, amd8111e_interrupt, IRQF_SHARED, 12048c2ecf20Sopenharmony_ci dev->name, dev)) 12058c2ecf20Sopenharmony_ci return -EAGAIN; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci napi_enable(&lp->napi); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci amd8111e_init_hw_default(lp); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if(amd8111e_restart(dev)){ 12148c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 12158c2ecf20Sopenharmony_ci napi_disable(&lp->napi); 12168c2ecf20Sopenharmony_ci if (dev->irq) 12178c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 12188c2ecf20Sopenharmony_ci return -ENOMEM; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci /* Start ipg timer */ 12218c2ecf20Sopenharmony_ci if(lp->options & OPTION_DYN_IPG_ENABLE){ 12228c2ecf20Sopenharmony_ci add_timer(&lp->ipg_data.ipg_timer); 12238c2ecf20Sopenharmony_ci netdev_info(dev, "Dynamic IPG Enabled\n"); 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci lp->opened = 1; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci netif_start_queue(dev); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci/* This function checks if there is any transmit descriptors 12368c2ecf20Sopenharmony_ci * available to queue more packet. 12378c2ecf20Sopenharmony_ci */ 12388c2ecf20Sopenharmony_cistatic int amd8111e_tx_queue_avail(struct amd8111e_priv *lp) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci int tx_index = lp->tx_idx & TX_BUFF_MOD_MASK; 12418c2ecf20Sopenharmony_ci if (lp->tx_skbuff[tx_index]) 12428c2ecf20Sopenharmony_ci return -1; 12438c2ecf20Sopenharmony_ci else 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci/* This function will queue the transmit packets to the 12498c2ecf20Sopenharmony_ci * descriptors and will trigger the send operation. It also 12508c2ecf20Sopenharmony_ci * initializes the transmit descriptors with buffer physical address, 12518c2ecf20Sopenharmony_ci * byte count, ownership to hardware etc. 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_cistatic netdev_tx_t amd8111e_start_xmit(struct sk_buff *skb, 12548c2ecf20Sopenharmony_ci struct net_device *dev) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 12578c2ecf20Sopenharmony_ci int tx_index; 12588c2ecf20Sopenharmony_ci unsigned long flags; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci tx_index = lp->tx_idx & TX_RING_DR_MOD_MASK; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].buff_count = cpu_to_le16(skb->len); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci lp->tx_skbuff[tx_index] = skb; 12678c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].tx_flags = 0; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 12708c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 12718c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].tag_ctrl_cmd |= 12728c2ecf20Sopenharmony_ci cpu_to_le16(TCC_VLAN_INSERT); 12738c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].tag_ctrl_info = 12748c2ecf20Sopenharmony_ci cpu_to_le16(skb_vlan_tag_get(skb)); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci#endif 12788c2ecf20Sopenharmony_ci lp->tx_dma_addr[tx_index] = 12798c2ecf20Sopenharmony_ci dma_map_single(&lp->pci_dev->dev, skb->data, skb->len, 12808c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12818c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].buff_phy_addr = 12828c2ecf20Sopenharmony_ci cpu_to_le32(lp->tx_dma_addr[tx_index]); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* Set FCS and LTINT bits */ 12858c2ecf20Sopenharmony_ci wmb(); 12868c2ecf20Sopenharmony_ci lp->tx_ring[tx_index].tx_flags |= 12878c2ecf20Sopenharmony_ci cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT|ADD_FCS_BIT|LTINT_BIT); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci lp->tx_idx++; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* Trigger an immediate send poll. */ 12928c2ecf20Sopenharmony_ci writel( VAL1 | TDMD0, lp->mmio + CMD0); 12938c2ecf20Sopenharmony_ci writel( VAL2 | RDMD0,lp->mmio + CMD0); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if(amd8111e_tx_queue_avail(lp) < 0){ 12968c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 12998c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci/* This function returns all the memory mapped registers of the device. */ 13028c2ecf20Sopenharmony_cistatic void amd8111e_read_regs(struct amd8111e_priv *lp, u32 *buf) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 13058c2ecf20Sopenharmony_ci /* Read only necessary registers */ 13068c2ecf20Sopenharmony_ci buf[0] = readl(mmio + XMT_RING_BASE_ADDR0); 13078c2ecf20Sopenharmony_ci buf[1] = readl(mmio + XMT_RING_LEN0); 13088c2ecf20Sopenharmony_ci buf[2] = readl(mmio + RCV_RING_BASE_ADDR0); 13098c2ecf20Sopenharmony_ci buf[3] = readl(mmio + RCV_RING_LEN0); 13108c2ecf20Sopenharmony_ci buf[4] = readl(mmio + CMD0); 13118c2ecf20Sopenharmony_ci buf[5] = readl(mmio + CMD2); 13128c2ecf20Sopenharmony_ci buf[6] = readl(mmio + CMD3); 13138c2ecf20Sopenharmony_ci buf[7] = readl(mmio + CMD7); 13148c2ecf20Sopenharmony_ci buf[8] = readl(mmio + INT0); 13158c2ecf20Sopenharmony_ci buf[9] = readl(mmio + INTEN0); 13168c2ecf20Sopenharmony_ci buf[10] = readl(mmio + LADRF); 13178c2ecf20Sopenharmony_ci buf[11] = readl(mmio + LADRF+4); 13188c2ecf20Sopenharmony_ci buf[12] = readl(mmio + STAT0); 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci/* This function sets promiscuos mode, all-multi mode or the multicast address 13238c2ecf20Sopenharmony_ci * list to the device. 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_cistatic void amd8111e_set_multicast_list(struct net_device *dev) 13268c2ecf20Sopenharmony_ci{ 13278c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 13288c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 13298c2ecf20Sopenharmony_ci u32 mc_filter[2] ; 13308c2ecf20Sopenharmony_ci int bit_num; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if(dev->flags & IFF_PROMISC){ 13338c2ecf20Sopenharmony_ci writel( VAL2 | PROM, lp->mmio + CMD2); 13348c2ecf20Sopenharmony_ci return; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci else 13378c2ecf20Sopenharmony_ci writel( PROM, lp->mmio + CMD2); 13388c2ecf20Sopenharmony_ci if (dev->flags & IFF_ALLMULTI || 13398c2ecf20Sopenharmony_ci netdev_mc_count(dev) > MAX_FILTER_SIZE) { 13408c2ecf20Sopenharmony_ci /* get all multicast packet */ 13418c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 13428c2ecf20Sopenharmony_ci lp->options |= OPTION_MULTICAST_ENABLE; 13438c2ecf20Sopenharmony_ci amd8111e_writeq(*(u64 *)mc_filter, lp->mmio + LADRF); 13448c2ecf20Sopenharmony_ci return; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci if (netdev_mc_empty(dev)) { 13478c2ecf20Sopenharmony_ci /* get only own packets */ 13488c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0; 13498c2ecf20Sopenharmony_ci lp->options &= ~OPTION_MULTICAST_ENABLE; 13508c2ecf20Sopenharmony_ci amd8111e_writeq(*(u64 *)mc_filter, lp->mmio + LADRF); 13518c2ecf20Sopenharmony_ci /* disable promiscuous mode */ 13528c2ecf20Sopenharmony_ci writel(PROM, lp->mmio + CMD2); 13538c2ecf20Sopenharmony_ci return; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci /* load all the multicast addresses in the logic filter */ 13568c2ecf20Sopenharmony_ci lp->options |= OPTION_MULTICAST_ENABLE; 13578c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0; 13588c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 13598c2ecf20Sopenharmony_ci bit_num = (ether_crc_le(ETH_ALEN, ha->addr) >> 26) & 0x3f; 13608c2ecf20Sopenharmony_ci mc_filter[bit_num >> 5] |= 1 << (bit_num & 31); 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci amd8111e_writeq(*(u64 *)mc_filter, lp->mmio + LADRF); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci /* To eliminate PCI posting bug */ 13658c2ecf20Sopenharmony_ci readl(lp->mmio + CMD2); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic void amd8111e_get_drvinfo(struct net_device *dev, 13708c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 13738c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = lp->pci_dev; 13748c2ecf20Sopenharmony_ci strlcpy(info->driver, MODULE_NAME, sizeof(info->driver)); 13758c2ecf20Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), 13768c2ecf20Sopenharmony_ci "%u", chip_version); 13778c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info)); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic int amd8111e_get_regs_len(struct net_device *dev) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci return AMD8111E_REG_DUMP_LEN; 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cistatic void amd8111e_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf) 13868c2ecf20Sopenharmony_ci{ 13878c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 13888c2ecf20Sopenharmony_ci regs->version = 0; 13898c2ecf20Sopenharmony_ci amd8111e_read_regs(lp, buf); 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cistatic int amd8111e_get_link_ksettings(struct net_device *dev, 13938c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 13968c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 13978c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&lp->mii_if, cmd); 13988c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 13998c2ecf20Sopenharmony_ci return 0; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic int amd8111e_set_link_ksettings(struct net_device *dev, 14038c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 14068c2ecf20Sopenharmony_ci int res; 14078c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 14088c2ecf20Sopenharmony_ci res = mii_ethtool_set_link_ksettings(&lp->mii_if, cmd); 14098c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 14108c2ecf20Sopenharmony_ci return res; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic int amd8111e_nway_reset(struct net_device *dev) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 14168c2ecf20Sopenharmony_ci return mii_nway_restart(&lp->mii_if); 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic u32 amd8111e_get_link(struct net_device *dev) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 14228c2ecf20Sopenharmony_ci return mii_link_ok(&lp->mii_if); 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_cistatic void amd8111e_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 14288c2ecf20Sopenharmony_ci wol_info->supported = WAKE_MAGIC|WAKE_PHY; 14298c2ecf20Sopenharmony_ci if (lp->options & OPTION_WOL_ENABLE) 14308c2ecf20Sopenharmony_ci wol_info->wolopts = WAKE_MAGIC; 14318c2ecf20Sopenharmony_ci} 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic int amd8111e_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 14368c2ecf20Sopenharmony_ci if (wol_info->wolopts & ~(WAKE_MAGIC|WAKE_PHY)) 14378c2ecf20Sopenharmony_ci return -EINVAL; 14388c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 14398c2ecf20Sopenharmony_ci if (wol_info->wolopts & WAKE_MAGIC) 14408c2ecf20Sopenharmony_ci lp->options |= 14418c2ecf20Sopenharmony_ci (OPTION_WOL_ENABLE | OPTION_WAKE_MAGIC_ENABLE); 14428c2ecf20Sopenharmony_ci else if(wol_info->wolopts & WAKE_PHY) 14438c2ecf20Sopenharmony_ci lp->options |= 14448c2ecf20Sopenharmony_ci (OPTION_WOL_ENABLE | OPTION_WAKE_PHY_ENABLE); 14458c2ecf20Sopenharmony_ci else 14468c2ecf20Sopenharmony_ci lp->options &= ~OPTION_WOL_ENABLE; 14478c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 14488c2ecf20Sopenharmony_ci return 0; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic const struct ethtool_ops ops = { 14528c2ecf20Sopenharmony_ci .get_drvinfo = amd8111e_get_drvinfo, 14538c2ecf20Sopenharmony_ci .get_regs_len = amd8111e_get_regs_len, 14548c2ecf20Sopenharmony_ci .get_regs = amd8111e_get_regs, 14558c2ecf20Sopenharmony_ci .nway_reset = amd8111e_nway_reset, 14568c2ecf20Sopenharmony_ci .get_link = amd8111e_get_link, 14578c2ecf20Sopenharmony_ci .get_wol = amd8111e_get_wol, 14588c2ecf20Sopenharmony_ci .set_wol = amd8111e_set_wol, 14598c2ecf20Sopenharmony_ci .get_link_ksettings = amd8111e_get_link_ksettings, 14608c2ecf20Sopenharmony_ci .set_link_ksettings = amd8111e_set_link_ksettings, 14618c2ecf20Sopenharmony_ci}; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci/* This function handles all the ethtool ioctls. It gives driver info, 14648c2ecf20Sopenharmony_ci * gets/sets driver speed, gets memory mapped register values, forces 14658c2ecf20Sopenharmony_ci * auto negotiation, sets/gets WOL options for ethtool application. 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_cistatic int amd8111e_ioctl(struct net_device *dev , struct ifreq *ifr, int cmd) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct mii_ioctl_data *data = if_mii(ifr); 14708c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 14718c2ecf20Sopenharmony_ci int err; 14728c2ecf20Sopenharmony_ci u32 mii_regval; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci switch(cmd) { 14758c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 14768c2ecf20Sopenharmony_ci data->phy_id = lp->ext_phy_addr; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci fallthrough; 14798c2ecf20Sopenharmony_ci case SIOCGMIIREG: 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 14828c2ecf20Sopenharmony_ci err = amd8111e_read_phy(lp, data->phy_id, 14838c2ecf20Sopenharmony_ci data->reg_num & PHY_REG_ADDR_MASK, &mii_regval); 14848c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci data->val_out = mii_regval; 14878c2ecf20Sopenharmony_ci return err; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci case SIOCSMIIREG: 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 14928c2ecf20Sopenharmony_ci err = amd8111e_write_phy(lp, data->phy_id, 14938c2ecf20Sopenharmony_ci data->reg_num & PHY_REG_ADDR_MASK, data->val_in); 14948c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci return err; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci default: 14998c2ecf20Sopenharmony_ci /* do nothing */ 15008c2ecf20Sopenharmony_ci break; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_cistatic int amd8111e_set_mac_address(struct net_device *dev, void *p) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 15078c2ecf20Sopenharmony_ci int i; 15088c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 15118c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 15128c2ecf20Sopenharmony_ci /* Setting the MAC address to the device */ 15138c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 15148c2ecf20Sopenharmony_ci writeb( dev->dev_addr[i], lp->mmio + PADR + i ); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci return 0; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci/* This function changes the mtu of the device. It restarts the device to 15228c2ecf20Sopenharmony_ci * initialize the descriptor with new receive buffers. 15238c2ecf20Sopenharmony_ci */ 15248c2ecf20Sopenharmony_cistatic int amd8111e_change_mtu(struct net_device *dev, int new_mtu) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 15278c2ecf20Sopenharmony_ci int err; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci if (!netif_running(dev)) { 15308c2ecf20Sopenharmony_ci /* new_mtu will be used 15318c2ecf20Sopenharmony_ci * when device starts netxt time 15328c2ecf20Sopenharmony_ci */ 15338c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 15348c2ecf20Sopenharmony_ci return 0; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci /* stop the chip */ 15408c2ecf20Sopenharmony_ci writel(RUN, lp->mmio + CMD0); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci err = amd8111e_restart(dev); 15458c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 15468c2ecf20Sopenharmony_ci if(!err) 15478c2ecf20Sopenharmony_ci netif_start_queue(dev); 15488c2ecf20Sopenharmony_ci return err; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic int amd8111e_enable_magicpkt(struct amd8111e_priv *lp) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci writel( VAL1|MPPLBA, lp->mmio + CMD3); 15548c2ecf20Sopenharmony_ci writel( VAL0|MPEN_SW, lp->mmio + CMD7); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* To eliminate PCI posting bug */ 15578c2ecf20Sopenharmony_ci readl(lp->mmio + CMD7); 15588c2ecf20Sopenharmony_ci return 0; 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic int amd8111e_enable_link_change(struct amd8111e_priv *lp) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* Adapter is already stoped/suspended/interrupt-disabled */ 15658c2ecf20Sopenharmony_ci writel(VAL0|LCMODE_SW,lp->mmio + CMD7); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* To eliminate PCI posting bug */ 15688c2ecf20Sopenharmony_ci readl(lp->mmio + CMD7); 15698c2ecf20Sopenharmony_ci return 0; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci/* This function is called when a packet transmission fails to complete 15738c2ecf20Sopenharmony_ci * within a reasonable period, on the assumption that an interrupt have 15748c2ecf20Sopenharmony_ci * failed or the interface is locked up. This function will reinitialize 15758c2ecf20Sopenharmony_ci * the hardware. 15768c2ecf20Sopenharmony_ci */ 15778c2ecf20Sopenharmony_cistatic void amd8111e_tx_timeout(struct net_device *dev, unsigned int txqueue) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 15808c2ecf20Sopenharmony_ci int err; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci netdev_err(dev, "transmit timed out, resetting\n"); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 15858c2ecf20Sopenharmony_ci err = amd8111e_restart(dev); 15868c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 15878c2ecf20Sopenharmony_ci if(!err) 15888c2ecf20Sopenharmony_ci netif_wake_queue(dev); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic int __maybe_unused amd8111e_suspend(struct device *dev_d) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 15948c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (!netif_running(dev)) 15978c2ecf20Sopenharmony_ci return 0; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci /* disable the interrupt */ 16008c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 16018c2ecf20Sopenharmony_ci amd8111e_disable_interrupt(lp); 16028c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci netif_device_detach(dev); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* stop chip */ 16078c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 16088c2ecf20Sopenharmony_ci if(lp->options & OPTION_DYN_IPG_ENABLE) 16098c2ecf20Sopenharmony_ci del_timer_sync(&lp->ipg_data.ipg_timer); 16108c2ecf20Sopenharmony_ci amd8111e_stop_chip(lp); 16118c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if(lp->options & OPTION_WOL_ENABLE){ 16148c2ecf20Sopenharmony_ci /* enable wol */ 16158c2ecf20Sopenharmony_ci if(lp->options & OPTION_WAKE_MAGIC_ENABLE) 16168c2ecf20Sopenharmony_ci amd8111e_enable_magicpkt(lp); 16178c2ecf20Sopenharmony_ci if(lp->options & OPTION_WAKE_PHY_ENABLE) 16188c2ecf20Sopenharmony_ci amd8111e_enable_link_change(lp); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci device_set_wakeup_enable(dev_d, 1); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci else{ 16248c2ecf20Sopenharmony_ci device_set_wakeup_enable(dev_d, 0); 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci return 0; 16288c2ecf20Sopenharmony_ci} 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_cistatic int __maybe_unused amd8111e_resume(struct device *dev_d) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 16338c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (!netif_running(dev)) 16368c2ecf20Sopenharmony_ci return 0; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci netif_device_attach(dev); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci spin_lock_irq(&lp->lock); 16418c2ecf20Sopenharmony_ci amd8111e_restart(dev); 16428c2ecf20Sopenharmony_ci /* Restart ipg timer */ 16438c2ecf20Sopenharmony_ci if(lp->options & OPTION_DYN_IPG_ENABLE) 16448c2ecf20Sopenharmony_ci mod_timer(&lp->ipg_data.ipg_timer, 16458c2ecf20Sopenharmony_ci jiffies + IPG_CONVERGE_JIFFIES); 16468c2ecf20Sopenharmony_ci spin_unlock_irq(&lp->lock); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci return 0; 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic void amd8111e_config_ipg(struct timer_list *t) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = from_timer(lp, t, ipg_data.ipg_timer); 16548c2ecf20Sopenharmony_ci struct ipg_info *ipg_data = &lp->ipg_data; 16558c2ecf20Sopenharmony_ci void __iomem *mmio = lp->mmio; 16568c2ecf20Sopenharmony_ci unsigned int prev_col_cnt = ipg_data->col_cnt; 16578c2ecf20Sopenharmony_ci unsigned int total_col_cnt; 16588c2ecf20Sopenharmony_ci unsigned int tmp_ipg; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if(lp->link_config.duplex == DUPLEX_FULL){ 16618c2ecf20Sopenharmony_ci ipg_data->ipg = DEFAULT_IPG; 16628c2ecf20Sopenharmony_ci return; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if(ipg_data->ipg_state == SSTATE){ 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci if(ipg_data->timer_tick == IPG_STABLE_TIME){ 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci ipg_data->timer_tick = 0; 16708c2ecf20Sopenharmony_ci ipg_data->ipg = MIN_IPG - IPG_STEP; 16718c2ecf20Sopenharmony_ci ipg_data->current_ipg = MIN_IPG; 16728c2ecf20Sopenharmony_ci ipg_data->diff_col_cnt = 0xFFFFFFFF; 16738c2ecf20Sopenharmony_ci ipg_data->ipg_state = CSTATE; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci else 16768c2ecf20Sopenharmony_ci ipg_data->timer_tick++; 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci if(ipg_data->ipg_state == CSTATE){ 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci /* Get the current collision count */ 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci total_col_cnt = ipg_data->col_cnt = 16848c2ecf20Sopenharmony_ci amd8111e_read_mib(mmio, xmt_collisions); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci if ((total_col_cnt - prev_col_cnt) < 16878c2ecf20Sopenharmony_ci (ipg_data->diff_col_cnt)){ 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci ipg_data->diff_col_cnt = 16908c2ecf20Sopenharmony_ci total_col_cnt - prev_col_cnt ; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci ipg_data->ipg = ipg_data->current_ipg; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci ipg_data->current_ipg += IPG_STEP; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci if (ipg_data->current_ipg <= MAX_IPG) 16988c2ecf20Sopenharmony_ci tmp_ipg = ipg_data->current_ipg; 16998c2ecf20Sopenharmony_ci else{ 17008c2ecf20Sopenharmony_ci tmp_ipg = ipg_data->ipg; 17018c2ecf20Sopenharmony_ci ipg_data->ipg_state = SSTATE; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci writew((u32)tmp_ipg, mmio + IPG); 17048c2ecf20Sopenharmony_ci writew((u32)(tmp_ipg - IFS1_DELTA), mmio + IFS1); 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci mod_timer(&lp->ipg_data.ipg_timer, jiffies + IPG_CONVERGE_JIFFIES); 17078c2ecf20Sopenharmony_ci return; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cistatic void amd8111e_probe_ext_phy(struct net_device *dev) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci struct amd8111e_priv *lp = netdev_priv(dev); 17148c2ecf20Sopenharmony_ci int i; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci for (i = 0x1e; i >= 0; i--) { 17178c2ecf20Sopenharmony_ci u32 id1, id2; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1)) 17208c2ecf20Sopenharmony_ci continue; 17218c2ecf20Sopenharmony_ci if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2)) 17228c2ecf20Sopenharmony_ci continue; 17238c2ecf20Sopenharmony_ci lp->ext_phy_id = (id1 << 16) | id2; 17248c2ecf20Sopenharmony_ci lp->ext_phy_addr = i; 17258c2ecf20Sopenharmony_ci return; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci lp->ext_phy_id = 0; 17288c2ecf20Sopenharmony_ci lp->ext_phy_addr = 1; 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_cistatic const struct net_device_ops amd8111e_netdev_ops = { 17328c2ecf20Sopenharmony_ci .ndo_open = amd8111e_open, 17338c2ecf20Sopenharmony_ci .ndo_stop = amd8111e_close, 17348c2ecf20Sopenharmony_ci .ndo_start_xmit = amd8111e_start_xmit, 17358c2ecf20Sopenharmony_ci .ndo_tx_timeout = amd8111e_tx_timeout, 17368c2ecf20Sopenharmony_ci .ndo_get_stats = amd8111e_get_stats, 17378c2ecf20Sopenharmony_ci .ndo_set_rx_mode = amd8111e_set_multicast_list, 17388c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 17398c2ecf20Sopenharmony_ci .ndo_set_mac_address = amd8111e_set_mac_address, 17408c2ecf20Sopenharmony_ci .ndo_do_ioctl = amd8111e_ioctl, 17418c2ecf20Sopenharmony_ci .ndo_change_mtu = amd8111e_change_mtu, 17428c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 17438c2ecf20Sopenharmony_ci .ndo_poll_controller = amd8111e_poll, 17448c2ecf20Sopenharmony_ci#endif 17458c2ecf20Sopenharmony_ci}; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int amd8111e_probe_one(struct pci_dev *pdev, 17488c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci int err, i; 17518c2ecf20Sopenharmony_ci unsigned long reg_addr,reg_len; 17528c2ecf20Sopenharmony_ci struct amd8111e_priv *lp; 17538c2ecf20Sopenharmony_ci struct net_device *dev; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 17568c2ecf20Sopenharmony_ci if(err){ 17578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable new PCI device\n"); 17588c2ecf20Sopenharmony_ci return err; 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)){ 17628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot find PCI base address\n"); 17638c2ecf20Sopenharmony_ci err = -ENODEV; 17648c2ecf20Sopenharmony_ci goto err_disable_pdev; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, MODULE_NAME); 17688c2ecf20Sopenharmony_ci if(err){ 17698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); 17708c2ecf20Sopenharmony_ci goto err_disable_pdev; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci pci_set_master(pdev); 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci /* Find power-management capability. */ 17768c2ecf20Sopenharmony_ci if (!pdev->pm_cap) { 17778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No Power Management capability\n"); 17788c2ecf20Sopenharmony_ci err = -ENODEV; 17798c2ecf20Sopenharmony_ci goto err_free_reg; 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci /* Initialize DMA */ 17838c2ecf20Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) < 0) { 17848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DMA not supported\n"); 17858c2ecf20Sopenharmony_ci err = -ENODEV; 17868c2ecf20Sopenharmony_ci goto err_free_reg; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci reg_addr = pci_resource_start(pdev, 0); 17908c2ecf20Sopenharmony_ci reg_len = pci_resource_len(pdev, 0); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct amd8111e_priv)); 17938c2ecf20Sopenharmony_ci if (!dev) { 17948c2ecf20Sopenharmony_ci err = -ENOMEM; 17958c2ecf20Sopenharmony_ci goto err_free_reg; 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 18018c2ecf20Sopenharmony_ci dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX ; 18028c2ecf20Sopenharmony_ci#endif 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci lp = netdev_priv(dev); 18058c2ecf20Sopenharmony_ci lp->pci_dev = pdev; 18068c2ecf20Sopenharmony_ci lp->amd8111e_net_dev = dev; 18078c2ecf20Sopenharmony_ci lp->pm_cap = pdev->pm_cap; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci spin_lock_init(&lp->lock); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci lp->mmio = devm_ioremap(&pdev->dev, reg_addr, reg_len); 18128c2ecf20Sopenharmony_ci if (!lp->mmio) { 18138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map device registers\n"); 18148c2ecf20Sopenharmony_ci err = -ENOMEM; 18158c2ecf20Sopenharmony_ci goto err_free_dev; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci /* Initializing MAC address */ 18198c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 18208c2ecf20Sopenharmony_ci dev->dev_addr[i] = readb(lp->mmio + PADR + i); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* Setting user defined parametrs */ 18238c2ecf20Sopenharmony_ci lp->ext_phy_option = speed_duplex[card_idx]; 18248c2ecf20Sopenharmony_ci if(coalesce[card_idx]) 18258c2ecf20Sopenharmony_ci lp->options |= OPTION_INTR_COAL_ENABLE; 18268c2ecf20Sopenharmony_ci if(dynamic_ipg[card_idx++]) 18278c2ecf20Sopenharmony_ci lp->options |= OPTION_DYN_IPG_ENABLE; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* Initialize driver entry points */ 18318c2ecf20Sopenharmony_ci dev->netdev_ops = &amd8111e_netdev_ops; 18328c2ecf20Sopenharmony_ci dev->ethtool_ops = &ops; 18338c2ecf20Sopenharmony_ci dev->irq =pdev->irq; 18348c2ecf20Sopenharmony_ci dev->watchdog_timeo = AMD8111E_TX_TIMEOUT; 18358c2ecf20Sopenharmony_ci dev->min_mtu = AMD8111E_MIN_MTU; 18368c2ecf20Sopenharmony_ci dev->max_mtu = AMD8111E_MAX_MTU; 18378c2ecf20Sopenharmony_ci netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci#if AMD8111E_VLAN_TAG_USED 18408c2ecf20Sopenharmony_ci dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 18418c2ecf20Sopenharmony_ci#endif 18428c2ecf20Sopenharmony_ci /* Probe the external PHY */ 18438c2ecf20Sopenharmony_ci amd8111e_probe_ext_phy(dev); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* setting mii default values */ 18468c2ecf20Sopenharmony_ci lp->mii_if.dev = dev; 18478c2ecf20Sopenharmony_ci lp->mii_if.mdio_read = amd8111e_mdio_read; 18488c2ecf20Sopenharmony_ci lp->mii_if.mdio_write = amd8111e_mdio_write; 18498c2ecf20Sopenharmony_ci lp->mii_if.phy_id = lp->ext_phy_addr; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* Set receive buffer length and set jumbo option*/ 18528c2ecf20Sopenharmony_ci amd8111e_set_rx_buff_len(dev); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci err = register_netdev(dev); 18568c2ecf20Sopenharmony_ci if (err) { 18578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot register net device\n"); 18588c2ecf20Sopenharmony_ci goto err_free_dev; 18598c2ecf20Sopenharmony_ci } 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* Initialize software ipg timer */ 18648c2ecf20Sopenharmony_ci if(lp->options & OPTION_DYN_IPG_ENABLE){ 18658c2ecf20Sopenharmony_ci timer_setup(&lp->ipg_data.ipg_timer, amd8111e_config_ipg, 0); 18668c2ecf20Sopenharmony_ci lp->ipg_data.ipg_timer.expires = jiffies + 18678c2ecf20Sopenharmony_ci IPG_CONVERGE_JIFFIES; 18688c2ecf20Sopenharmony_ci lp->ipg_data.ipg = DEFAULT_IPG; 18698c2ecf20Sopenharmony_ci lp->ipg_data.ipg_state = CSTATE; 18708c2ecf20Sopenharmony_ci } 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci /* display driver and device information */ 18738c2ecf20Sopenharmony_ci chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28; 18748c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "[ Rev %x ] PCI 10/100BaseT Ethernet %pM\n", 18758c2ecf20Sopenharmony_ci chip_version, dev->dev_addr); 18768c2ecf20Sopenharmony_ci if (lp->ext_phy_id) 18778c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Found MII PHY ID 0x%08x at address 0x%02x\n", 18788c2ecf20Sopenharmony_ci lp->ext_phy_id, lp->ext_phy_addr); 18798c2ecf20Sopenharmony_ci else 18808c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Couldn't detect MII PHY, assuming address 0x01\n"); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci return 0; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cierr_free_dev: 18858c2ecf20Sopenharmony_ci free_netdev(dev); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cierr_free_reg: 18888c2ecf20Sopenharmony_ci pci_release_regions(pdev); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cierr_disable_pdev: 18918c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18928c2ecf20Sopenharmony_ci return err; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci} 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_cistatic void amd8111e_remove_one(struct pci_dev *pdev) 18978c2ecf20Sopenharmony_ci{ 18988c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci if (dev) { 19018c2ecf20Sopenharmony_ci unregister_netdev(dev); 19028c2ecf20Sopenharmony_ci free_netdev(dev); 19038c2ecf20Sopenharmony_ci pci_release_regions(pdev); 19048c2ecf20Sopenharmony_ci pci_disable_device(pdev); 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci} 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_cistatic const struct pci_device_id amd8111e_pci_tbl[] = { 19098c2ecf20Sopenharmony_ci { 19108c2ecf20Sopenharmony_ci .vendor = PCI_VENDOR_ID_AMD, 19118c2ecf20Sopenharmony_ci .device = PCI_DEVICE_ID_AMD8111E_7462, 19128c2ecf20Sopenharmony_ci }, 19138c2ecf20Sopenharmony_ci { 19148c2ecf20Sopenharmony_ci .vendor = 0, 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci}; 19178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amd8111e_pci_tbl); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(amd8111e_pm_ops, amd8111e_suspend, amd8111e_resume); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_cistatic struct pci_driver amd8111e_driver = { 19228c2ecf20Sopenharmony_ci .name = MODULE_NAME, 19238c2ecf20Sopenharmony_ci .id_table = amd8111e_pci_tbl, 19248c2ecf20Sopenharmony_ci .probe = amd8111e_probe_one, 19258c2ecf20Sopenharmony_ci .remove = amd8111e_remove_one, 19268c2ecf20Sopenharmony_ci .driver.pm = &amd8111e_pm_ops 19278c2ecf20Sopenharmony_ci}; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cimodule_pci_driver(amd8111e_driver); 1930