18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com> 58c2ecf20Sopenharmony_ci * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Derived from Intel e1000 driver 88c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Contact Information: 118c2ecf20Sopenharmony_ci * Xiong Huang <xiong.huang@atheros.com> 128c2ecf20Sopenharmony_ci * Jie Yang <jie.yang@atheros.com> 138c2ecf20Sopenharmony_ci * Chris Snook <csnook@redhat.com> 148c2ecf20Sopenharmony_ci * Jay Cliburn <jcliburn@gmail.com> 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This version is adapted from the Attansic reference driver. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * TODO: 198c2ecf20Sopenharmony_ci * Add more ethtool functions. 208c2ecf20Sopenharmony_ci * Fix abstruse irq enable/disable condition described here: 218c2ecf20Sopenharmony_ci * http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * NEEDS TESTING: 248c2ecf20Sopenharmony_ci * VLAN 258c2ecf20Sopenharmony_ci * multicast 268c2ecf20Sopenharmony_ci * promiscuous mode 278c2ecf20Sopenharmony_ci * interrupt coalescing 288c2ecf20Sopenharmony_ci * SMP torture testing 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/atomic.h> 328c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/compiler.h> 358c2ecf20Sopenharmony_ci#include <linux/crc32.h> 368c2ecf20Sopenharmony_ci#include <linux/delay.h> 378c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 388c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 398c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 408c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 418c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 428c2ecf20Sopenharmony_ci#include <linux/in.h> 438c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 448c2ecf20Sopenharmony_ci#include <linux/ip.h> 458c2ecf20Sopenharmony_ci#include <linux/irqflags.h> 468c2ecf20Sopenharmony_ci#include <linux/irqreturn.h> 478c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 488c2ecf20Sopenharmony_ci#include <linux/mii.h> 498c2ecf20Sopenharmony_ci#include <linux/module.h> 508c2ecf20Sopenharmony_ci#include <linux/net.h> 518c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 528c2ecf20Sopenharmony_ci#include <linux/pci.h> 538c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 548c2ecf20Sopenharmony_ci#include <linux/pm.h> 558c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 568c2ecf20Sopenharmony_ci#include <linux/slab.h> 578c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 588c2ecf20Sopenharmony_ci#include <linux/string.h> 598c2ecf20Sopenharmony_ci#include <linux/tcp.h> 608c2ecf20Sopenharmony_ci#include <linux/timer.h> 618c2ecf20Sopenharmony_ci#include <linux/types.h> 628c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <net/checksum.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#include "atl1.h" 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, " 698c2ecf20Sopenharmony_ci "Chris Snook <csnook@redhat.com>, " 708c2ecf20Sopenharmony_ci "Jay Cliburn <jcliburn@gmail.com>"); 718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Temporary hack for merging atl1 and atl2 */ 748c2ecf20Sopenharmony_ci#include "atlx.c" 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic const struct ethtool_ops atl1_ethtool_ops; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * This is the only thing that needs to be changed to adjust the 808c2ecf20Sopenharmony_ci * maximum number of ports that the driver can manage. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci#define ATL1_MAX_NIC 4 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define OPTION_UNSET -1 858c2ecf20Sopenharmony_ci#define OPTION_DISABLED 0 868c2ecf20Sopenharmony_ci#define OPTION_ENABLED 1 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Interrupt Moderate Timer in units of 2 us 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * Valid Range: 10-65535 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Default Value: 100 (200us) 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic int int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; 988c2ecf20Sopenharmony_cistatic unsigned int num_int_mod_timer; 998c2ecf20Sopenharmony_cimodule_param_array_named(int_mod_timer, int_mod_timer, int, 1008c2ecf20Sopenharmony_ci &num_int_mod_timer, 0); 1018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer"); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define DEFAULT_INT_MOD_CNT 100 /* 200us */ 1048c2ecf20Sopenharmony_ci#define MAX_INT_MOD_CNT 65000 1058c2ecf20Sopenharmony_ci#define MIN_INT_MOD_CNT 50 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct atl1_option { 1088c2ecf20Sopenharmony_ci enum { enable_option, range_option, list_option } type; 1098c2ecf20Sopenharmony_ci char *name; 1108c2ecf20Sopenharmony_ci char *err; 1118c2ecf20Sopenharmony_ci int def; 1128c2ecf20Sopenharmony_ci union { 1138c2ecf20Sopenharmony_ci struct { /* range_option info */ 1148c2ecf20Sopenharmony_ci int min; 1158c2ecf20Sopenharmony_ci int max; 1168c2ecf20Sopenharmony_ci } r; 1178c2ecf20Sopenharmony_ci struct { /* list_option info */ 1188c2ecf20Sopenharmony_ci int nr; 1198c2ecf20Sopenharmony_ci struct atl1_opt_list { 1208c2ecf20Sopenharmony_ci int i; 1218c2ecf20Sopenharmony_ci char *str; 1228c2ecf20Sopenharmony_ci } *p; 1238c2ecf20Sopenharmony_ci } l; 1248c2ecf20Sopenharmony_ci } arg; 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int atl1_validate_option(int *value, struct atl1_option *opt, 1288c2ecf20Sopenharmony_ci struct pci_dev *pdev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (*value == OPTION_UNSET) { 1318c2ecf20Sopenharmony_ci *value = opt->def; 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci switch (opt->type) { 1368c2ecf20Sopenharmony_ci case enable_option: 1378c2ecf20Sopenharmony_ci switch (*value) { 1388c2ecf20Sopenharmony_ci case OPTION_ENABLED: 1398c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s enabled\n", opt->name); 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci case OPTION_DISABLED: 1428c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s disabled\n", opt->name); 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case range_option: 1478c2ecf20Sopenharmony_ci if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { 1488c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s set to %i\n", opt->name, 1498c2ecf20Sopenharmony_ci *value); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case list_option:{ 1548c2ecf20Sopenharmony_ci int i; 1558c2ecf20Sopenharmony_ci struct atl1_opt_list *ent; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < opt->arg.l.nr; i++) { 1588c2ecf20Sopenharmony_ci ent = &opt->arg.l.p[i]; 1598c2ecf20Sopenharmony_ci if (*value == ent->i) { 1608c2ecf20Sopenharmony_ci if (ent->str[0] != '\0') 1618c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s\n", 1628c2ecf20Sopenharmony_ci ent->str); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci default: 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "invalid %s specified (%i) %s\n", 1748c2ecf20Sopenharmony_ci opt->name, *value, opt->err); 1758c2ecf20Sopenharmony_ci *value = opt->def; 1768c2ecf20Sopenharmony_ci return -1; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/** 1808c2ecf20Sopenharmony_ci * atl1_check_options - Range Checking for Command Line Parameters 1818c2ecf20Sopenharmony_ci * @adapter: board private structure 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * This routine checks all command line parameters for valid user 1848c2ecf20Sopenharmony_ci * input. If an invalid value is given, or if no user specified 1858c2ecf20Sopenharmony_ci * value exists, a default value is used. The final value is stored 1868c2ecf20Sopenharmony_ci * in a variable in the adapter structure. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic void atl1_check_options(struct atl1_adapter *adapter) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 1918c2ecf20Sopenharmony_ci int bd = adapter->bd_number; 1928c2ecf20Sopenharmony_ci if (bd >= ATL1_MAX_NIC) { 1938c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "no configuration for board#%i\n", bd); 1948c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "using defaults for all values\n"); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci { /* Interrupt Moderate Timer */ 1978c2ecf20Sopenharmony_ci struct atl1_option opt = { 1988c2ecf20Sopenharmony_ci .type = range_option, 1998c2ecf20Sopenharmony_ci .name = "Interrupt Moderator Timer", 2008c2ecf20Sopenharmony_ci .err = "using default of " 2018c2ecf20Sopenharmony_ci __MODULE_STRING(DEFAULT_INT_MOD_CNT), 2028c2ecf20Sopenharmony_ci .def = DEFAULT_INT_MOD_CNT, 2038c2ecf20Sopenharmony_ci .arg = {.r = {.min = MIN_INT_MOD_CNT, 2048c2ecf20Sopenharmony_ci .max = MAX_INT_MOD_CNT} } 2058c2ecf20Sopenharmony_ci }; 2068c2ecf20Sopenharmony_ci int val; 2078c2ecf20Sopenharmony_ci if (num_int_mod_timer > bd) { 2088c2ecf20Sopenharmony_ci val = int_mod_timer[bd]; 2098c2ecf20Sopenharmony_ci atl1_validate_option(&val, &opt, pdev); 2108c2ecf20Sopenharmony_ci adapter->imt = (u16) val; 2118c2ecf20Sopenharmony_ci } else 2128c2ecf20Sopenharmony_ci adapter->imt = (u16) (opt.def); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * atl1_pci_tbl - PCI Device ID Table 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic const struct pci_device_id atl1_pci_tbl[] = { 2208c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1)}, 2218c2ecf20Sopenharmony_ci /* required last entry */ 2228c2ecf20Sopenharmony_ci {0,} 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, atl1_pci_tbl); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const u32 atl1_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | 2278c2ecf20Sopenharmony_ci NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int debug = -1; 2308c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 2318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Message level (0=none,...,16=all)"); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * Reset the transmit and receive units; mask and clear all interrupts. 2358c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 2368c2ecf20Sopenharmony_ci * return : 0 or idle status (if error) 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistatic s32 atl1_reset_hw(struct atl1_hw *hw) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct pci_dev *pdev = hw->back->pdev; 2418c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = hw->back; 2428c2ecf20Sopenharmony_ci u32 icr; 2438c2ecf20Sopenharmony_ci int i; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * Clear Interrupt mask to stop board from generating 2478c2ecf20Sopenharmony_ci * interrupts & Clear any pending interrupt events 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * atlx_irq_disable(adapter); 2518c2ecf20Sopenharmony_ci * iowrite32(0xffffffff, hw->hw_addr + REG_ISR); 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * Issue Soft Reset to the MAC. This will reset the chip's 2568c2ecf20Sopenharmony_ci * transmit, receive, DMA. It will not effect 2578c2ecf20Sopenharmony_ci * the current PCI configuration. The global reset bit is self- 2588c2ecf20Sopenharmony_ci * clearing, and should clear within a microsecond. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci iowrite32(MASTER_CTRL_SOFT_RST, hw->hw_addr + REG_MASTER_CTRL); 2618c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_MASTER_CTRL); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci iowrite16(1, hw->hw_addr + REG_PHY_ENABLE); 2648c2ecf20Sopenharmony_ci ioread16(hw->hw_addr + REG_PHY_ENABLE); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* delay about 1ms */ 2678c2ecf20Sopenharmony_ci msleep(1); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Wait at least 10ms for All module to be Idle */ 2708c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 2718c2ecf20Sopenharmony_ci icr = ioread32(hw->hw_addr + REG_IDLE_STATUS); 2728c2ecf20Sopenharmony_ci if (!icr) 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci /* delay 1 ms */ 2758c2ecf20Sopenharmony_ci msleep(1); 2768c2ecf20Sopenharmony_ci /* FIXME: still the right way to do this? */ 2778c2ecf20Sopenharmony_ci cpu_relax(); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (icr) { 2818c2ecf20Sopenharmony_ci if (netif_msg_hw(adapter)) 2828c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "ICR = 0x%x\n", icr); 2838c2ecf20Sopenharmony_ci return icr; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* function about EEPROM 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * check_eeprom_exist 2928c2ecf20Sopenharmony_ci * return 0 if eeprom exist 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic int atl1_check_eeprom_exist(struct atl1_hw *hw) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci u32 value; 2978c2ecf20Sopenharmony_ci value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); 2988c2ecf20Sopenharmony_ci if (value & SPI_FLASH_CTRL_EN_VPD) { 2998c2ecf20Sopenharmony_ci value &= ~SPI_FLASH_CTRL_EN_VPD; 3008c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST); 3048c2ecf20Sopenharmony_ci return ((value & 0xFF00) == 0x6C00) ? 0 : 1; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 *p_value) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci int i; 3108c2ecf20Sopenharmony_ci u32 control; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (offset & 3) 3138c2ecf20Sopenharmony_ci /* address do not align */ 3148c2ecf20Sopenharmony_ci return false; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci iowrite32(0, hw->hw_addr + REG_VPD_DATA); 3178c2ecf20Sopenharmony_ci control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; 3188c2ecf20Sopenharmony_ci iowrite32(control, hw->hw_addr + REG_VPD_CAP); 3198c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_VPD_CAP); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3228c2ecf20Sopenharmony_ci msleep(2); 3238c2ecf20Sopenharmony_ci control = ioread32(hw->hw_addr + REG_VPD_CAP); 3248c2ecf20Sopenharmony_ci if (control & VPD_CAP_VPD_FLAG) 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci if (control & VPD_CAP_VPD_FLAG) { 3288c2ecf20Sopenharmony_ci *p_value = ioread32(hw->hw_addr + REG_VPD_DATA); 3298c2ecf20Sopenharmony_ci return true; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci /* timeout */ 3328c2ecf20Sopenharmony_ci return false; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* 3368c2ecf20Sopenharmony_ci * Reads the value from a PHY register 3378c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 3388c2ecf20Sopenharmony_ci * reg_addr - address of the PHY register to read 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_cistatic s32 atl1_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci u32 val; 3438c2ecf20Sopenharmony_ci int i; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci val = ((u32) (reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | 3468c2ecf20Sopenharmony_ci MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 << 3478c2ecf20Sopenharmony_ci MDIO_CLK_SEL_SHIFT; 3488c2ecf20Sopenharmony_ci iowrite32(val, hw->hw_addr + REG_MDIO_CTRL); 3498c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_MDIO_CTRL); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < MDIO_WAIT_TIMES; i++) { 3528c2ecf20Sopenharmony_ci udelay(2); 3538c2ecf20Sopenharmony_ci val = ioread32(hw->hw_addr + REG_MDIO_CTRL); 3548c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) { 3588c2ecf20Sopenharmony_ci *phy_data = (u16) val; 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci return ATLX_ERR_PHY; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CS_SETUP 2 3658c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CLK_HI 2 3668c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CLK_LO 2 3678c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CS_HOLD 2 3688c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CS_HI 3 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 *buf) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int i; 3738c2ecf20Sopenharmony_ci u32 value; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci iowrite32(0, hw->hw_addr + REG_SPI_DATA); 3768c2ecf20Sopenharmony_ci iowrite32(addr, hw->hw_addr + REG_SPI_ADDR); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci value = SPI_FLASH_CTRL_WAIT_READY | 3798c2ecf20Sopenharmony_ci (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << 3808c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI & 3818c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CLK_HI_MASK) << 3828c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO & 3838c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CLK_LO_MASK) << 3848c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD & 3858c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_HOLD_MASK) << 3868c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI & 3878c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_HI_MASK) << 3888c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_HI_SHIFT | (1 & SPI_FLASH_CTRL_INS_MASK) << 3898c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_INS_SHIFT; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci value |= SPI_FLASH_CTRL_START; 3948c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); 3958c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3988c2ecf20Sopenharmony_ci msleep(1); 3998c2ecf20Sopenharmony_ci value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); 4008c2ecf20Sopenharmony_ci if (!(value & SPI_FLASH_CTRL_START)) 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (value & SPI_FLASH_CTRL_START) 4058c2ecf20Sopenharmony_ci return false; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci *buf = ioread32(hw->hw_addr + REG_SPI_DATA); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return true; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/* 4138c2ecf20Sopenharmony_ci * get_permanent_address 4148c2ecf20Sopenharmony_ci * return 0 if get valid mac address, 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic int atl1_get_permanent_address(struct atl1_hw *hw) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci u32 addr[2]; 4198c2ecf20Sopenharmony_ci u32 i, control; 4208c2ecf20Sopenharmony_ci u16 reg; 4218c2ecf20Sopenharmony_ci u8 eth_addr[ETH_ALEN]; 4228c2ecf20Sopenharmony_ci bool key_valid; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (is_valid_ether_addr(hw->perm_mac_addr)) 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* init */ 4288c2ecf20Sopenharmony_ci addr[0] = addr[1] = 0; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (!atl1_check_eeprom_exist(hw)) { 4318c2ecf20Sopenharmony_ci reg = 0; 4328c2ecf20Sopenharmony_ci key_valid = false; 4338c2ecf20Sopenharmony_ci /* Read out all EEPROM content */ 4348c2ecf20Sopenharmony_ci i = 0; 4358c2ecf20Sopenharmony_ci while (1) { 4368c2ecf20Sopenharmony_ci if (atl1_read_eeprom(hw, i + 0x100, &control)) { 4378c2ecf20Sopenharmony_ci if (key_valid) { 4388c2ecf20Sopenharmony_ci if (reg == REG_MAC_STA_ADDR) 4398c2ecf20Sopenharmony_ci addr[0] = control; 4408c2ecf20Sopenharmony_ci else if (reg == (REG_MAC_STA_ADDR + 4)) 4418c2ecf20Sopenharmony_ci addr[1] = control; 4428c2ecf20Sopenharmony_ci key_valid = false; 4438c2ecf20Sopenharmony_ci } else if ((control & 0xff) == 0x5A) { 4448c2ecf20Sopenharmony_ci key_valid = true; 4458c2ecf20Sopenharmony_ci reg = (u16) (control >> 16); 4468c2ecf20Sopenharmony_ci } else 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci } else 4498c2ecf20Sopenharmony_ci /* read error */ 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci i += 4; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci *(u32 *) ð_addr[2] = swab32(addr[0]); 4558c2ecf20Sopenharmony_ci *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); 4568c2ecf20Sopenharmony_ci if (is_valid_ether_addr(eth_addr)) { 4578c2ecf20Sopenharmony_ci memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* see if SPI FLAGS exist ? */ 4638c2ecf20Sopenharmony_ci addr[0] = addr[1] = 0; 4648c2ecf20Sopenharmony_ci reg = 0; 4658c2ecf20Sopenharmony_ci key_valid = false; 4668c2ecf20Sopenharmony_ci i = 0; 4678c2ecf20Sopenharmony_ci while (1) { 4688c2ecf20Sopenharmony_ci if (atl1_spi_read(hw, i + 0x1f000, &control)) { 4698c2ecf20Sopenharmony_ci if (key_valid) { 4708c2ecf20Sopenharmony_ci if (reg == REG_MAC_STA_ADDR) 4718c2ecf20Sopenharmony_ci addr[0] = control; 4728c2ecf20Sopenharmony_ci else if (reg == (REG_MAC_STA_ADDR + 4)) 4738c2ecf20Sopenharmony_ci addr[1] = control; 4748c2ecf20Sopenharmony_ci key_valid = false; 4758c2ecf20Sopenharmony_ci } else if ((control & 0xff) == 0x5A) { 4768c2ecf20Sopenharmony_ci key_valid = true; 4778c2ecf20Sopenharmony_ci reg = (u16) (control >> 16); 4788c2ecf20Sopenharmony_ci } else 4798c2ecf20Sopenharmony_ci /* data end */ 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci } else 4828c2ecf20Sopenharmony_ci /* read error */ 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci i += 4; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci *(u32 *) ð_addr[2] = swab32(addr[0]); 4888c2ecf20Sopenharmony_ci *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); 4898c2ecf20Sopenharmony_ci if (is_valid_ether_addr(eth_addr)) { 4908c2ecf20Sopenharmony_ci memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* 4958c2ecf20Sopenharmony_ci * On some motherboards, the MAC address is written by the 4968c2ecf20Sopenharmony_ci * BIOS directly to the MAC register during POST, and is 4978c2ecf20Sopenharmony_ci * not stored in eeprom. If all else thus far has failed 4988c2ecf20Sopenharmony_ci * to fetch the permanent MAC address, try reading it directly. 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci addr[0] = ioread32(hw->hw_addr + REG_MAC_STA_ADDR); 5018c2ecf20Sopenharmony_ci addr[1] = ioread16(hw->hw_addr + (REG_MAC_STA_ADDR + 4)); 5028c2ecf20Sopenharmony_ci *(u32 *) ð_addr[2] = swab32(addr[0]); 5038c2ecf20Sopenharmony_ci *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); 5048c2ecf20Sopenharmony_ci if (is_valid_ether_addr(eth_addr)) { 5058c2ecf20Sopenharmony_ci memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return 1; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/* 5138c2ecf20Sopenharmony_ci * Reads the adapter's MAC address from the EEPROM 5148c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_cistatic s32 atl1_read_mac_addr(struct atl1_hw *hw) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci s32 ret = 0; 5198c2ecf20Sopenharmony_ci u16 i; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (atl1_get_permanent_address(hw)) { 5228c2ecf20Sopenharmony_ci eth_random_addr(hw->perm_mac_addr); 5238c2ecf20Sopenharmony_ci ret = 1; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 5278c2ecf20Sopenharmony_ci hw->mac_addr[i] = hw->perm_mac_addr[i]; 5288c2ecf20Sopenharmony_ci return ret; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* 5328c2ecf20Sopenharmony_ci * Hashes an address to determine its location in the multicast table 5338c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 5348c2ecf20Sopenharmony_ci * mc_addr - the multicast address to hash 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * atl1_hash_mc_addr 5378c2ecf20Sopenharmony_ci * purpose 5388c2ecf20Sopenharmony_ci * set hash value for a multicast address 5398c2ecf20Sopenharmony_ci * hash calcu processing : 5408c2ecf20Sopenharmony_ci * 1. calcu 32bit CRC for multicast address 5418c2ecf20Sopenharmony_ci * 2. reverse crc with MSB to LSB 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_cistatic u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci u32 crc32, value = 0; 5468c2ecf20Sopenharmony_ci int i; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci crc32 = ether_crc_le(6, mc_addr); 5498c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) 5508c2ecf20Sopenharmony_ci value |= (((crc32 >> i) & 1) << (31 - i)); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return value; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci/* 5568c2ecf20Sopenharmony_ci * Sets the bit in the multicast table corresponding to the hash value. 5578c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 5588c2ecf20Sopenharmony_ci * hash_value - Multicast address hash value 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_cistatic void atl1_hash_set(struct atl1_hw *hw, u32 hash_value) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci u32 hash_bit, hash_reg; 5638c2ecf20Sopenharmony_ci u32 mta; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* 5668c2ecf20Sopenharmony_ci * The HASH Table is a register array of 2 32-bit registers. 5678c2ecf20Sopenharmony_ci * It is treated like an array of 64 bits. We want to set 5688c2ecf20Sopenharmony_ci * bit BitArray[hash_value]. So we figure out what register 5698c2ecf20Sopenharmony_ci * the bit is in, read it, OR in the new bit, then write 5708c2ecf20Sopenharmony_ci * back the new value. The register is determined by the 5718c2ecf20Sopenharmony_ci * upper 7 bits of the hash value and the bit within that 5728c2ecf20Sopenharmony_ci * register are determined by the lower 5 bits of the value. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci hash_reg = (hash_value >> 31) & 0x1; 5758c2ecf20Sopenharmony_ci hash_bit = (hash_value >> 26) & 0x1F; 5768c2ecf20Sopenharmony_ci mta = ioread32((hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); 5778c2ecf20Sopenharmony_ci mta |= (1 << hash_bit); 5788c2ecf20Sopenharmony_ci iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/* 5828c2ecf20Sopenharmony_ci * Writes a value to a PHY register 5838c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 5848c2ecf20Sopenharmony_ci * reg_addr - address of the PHY register to write 5858c2ecf20Sopenharmony_ci * data - data to write to the PHY 5868c2ecf20Sopenharmony_ci */ 5878c2ecf20Sopenharmony_cistatic s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci int i; 5908c2ecf20Sopenharmony_ci u32 val; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci val = ((u32) (phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | 5938c2ecf20Sopenharmony_ci (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | 5948c2ecf20Sopenharmony_ci MDIO_SUP_PREAMBLE | 5958c2ecf20Sopenharmony_ci MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; 5968c2ecf20Sopenharmony_ci iowrite32(val, hw->hw_addr + REG_MDIO_CTRL); 5978c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_MDIO_CTRL); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci for (i = 0; i < MDIO_WAIT_TIMES; i++) { 6008c2ecf20Sopenharmony_ci udelay(2); 6018c2ecf20Sopenharmony_ci val = ioread32(hw->hw_addr + REG_MDIO_CTRL); 6028c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return ATLX_ERR_PHY; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/* 6138c2ecf20Sopenharmony_ci * Make L001's PHY out of Power Saving State (bug) 6148c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 6158c2ecf20Sopenharmony_ci * when power on, L001's PHY always on Power saving State 6168c2ecf20Sopenharmony_ci * (Gigabit Link forbidden) 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_cistatic s32 atl1_phy_leave_power_saving(struct atl1_hw *hw) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci s32 ret; 6218c2ecf20Sopenharmony_ci ret = atl1_write_phy_reg(hw, 29, 0x0029); 6228c2ecf20Sopenharmony_ci if (ret) 6238c2ecf20Sopenharmony_ci return ret; 6248c2ecf20Sopenharmony_ci return atl1_write_phy_reg(hw, 30, 0); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* 6288c2ecf20Sopenharmony_ci * Resets the PHY and make all config validate 6298c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 6308c2ecf20Sopenharmony_ci * 6318c2ecf20Sopenharmony_ci * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_cistatic s32 atl1_phy_reset(struct atl1_hw *hw) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct pci_dev *pdev = hw->back->pdev; 6368c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = hw->back; 6378c2ecf20Sopenharmony_ci s32 ret_val; 6388c2ecf20Sopenharmony_ci u16 phy_data; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 6418c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) 6428c2ecf20Sopenharmony_ci phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; 6438c2ecf20Sopenharmony_ci else { 6448c2ecf20Sopenharmony_ci switch (hw->media_type) { 6458c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 6468c2ecf20Sopenharmony_ci phy_data = 6478c2ecf20Sopenharmony_ci MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | 6488c2ecf20Sopenharmony_ci MII_CR_RESET; 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 6518c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_100 | MII_CR_RESET; 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 6548c2ecf20Sopenharmony_ci phy_data = 6558c2ecf20Sopenharmony_ci MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci default: 6588c2ecf20Sopenharmony_ci /* MEDIA_TYPE_10M_HALF: */ 6598c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_10 | MII_CR_RESET; 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret_val = atl1_write_phy_reg(hw, MII_BMCR, phy_data); 6658c2ecf20Sopenharmony_ci if (ret_val) { 6668c2ecf20Sopenharmony_ci u32 val; 6678c2ecf20Sopenharmony_ci int i; 6688c2ecf20Sopenharmony_ci /* pcie serdes link may be down! */ 6698c2ecf20Sopenharmony_ci if (netif_msg_hw(adapter)) 6708c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "pcie phy link down\n"); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci for (i = 0; i < 25; i++) { 6738c2ecf20Sopenharmony_ci msleep(1); 6748c2ecf20Sopenharmony_ci val = ioread32(hw->hw_addr + REG_MDIO_CTRL); 6758c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if ((val & (MDIO_START | MDIO_BUSY)) != 0) { 6808c2ecf20Sopenharmony_ci if (netif_msg_hw(adapter)) 6818c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 6828c2ecf20Sopenharmony_ci "pcie link down at least 25ms\n"); 6838c2ecf20Sopenharmony_ci return ret_val; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* 6908c2ecf20Sopenharmony_ci * Configures PHY autoneg and flow control advertisement settings 6918c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_cistatic s32 atl1_phy_setup_autoneg_adv(struct atl1_hw *hw) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci s32 ret_val; 6968c2ecf20Sopenharmony_ci s16 mii_autoneg_adv_reg; 6978c2ecf20Sopenharmony_ci s16 mii_1000t_ctrl_reg; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* Read the MII Auto-Neg Advertisement Register (Address 4). */ 7008c2ecf20Sopenharmony_ci mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Read the MII 1000Base-T Control Register (Address 9). */ 7038c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg = MII_ATLX_CR_1000T_DEFAULT_CAP_MASK; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * First we clear all the 10/100 mb speed bits in the Auto-Neg 7078c2ecf20Sopenharmony_ci * Advertisement Register (Address 4) and the 1000 mb speed bits in 7088c2ecf20Sopenharmony_ci * the 1000Base-T Control Register (Address 9). 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; 7118c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg &= ~MII_ATLX_CR_1000T_SPEED_MASK; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* 7148c2ecf20Sopenharmony_ci * Need to parse media_type and set up 7158c2ecf20Sopenharmony_ci * the appropriate PHY registers. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci switch (hw->media_type) { 7188c2ecf20Sopenharmony_ci case MEDIA_TYPE_AUTO_SENSOR: 7198c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS | 7208c2ecf20Sopenharmony_ci MII_AR_10T_FD_CAPS | 7218c2ecf20Sopenharmony_ci MII_AR_100TX_HD_CAPS | 7228c2ecf20Sopenharmony_ci MII_AR_100TX_FD_CAPS); 7238c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS; 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci case MEDIA_TYPE_1000M_FULL: 7278c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS; 7288c2ecf20Sopenharmony_ci break; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 7318c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 7358c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; 7368c2ecf20Sopenharmony_ci break; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 7398c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; 7408c2ecf20Sopenharmony_ci break; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci default: 7438c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* flow control fixed to enable all */ 7488c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; 7518c2ecf20Sopenharmony_ci hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret_val = atl1_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); 7548c2ecf20Sopenharmony_ci if (ret_val) 7558c2ecf20Sopenharmony_ci return ret_val; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci ret_val = atl1_write_phy_reg(hw, MII_ATLX_CR, mii_1000t_ctrl_reg); 7588c2ecf20Sopenharmony_ci if (ret_val) 7598c2ecf20Sopenharmony_ci return ret_val; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/* 7658c2ecf20Sopenharmony_ci * Configures link settings. 7668c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 7678c2ecf20Sopenharmony_ci * Assumes the hardware has previously been reset and the 7688c2ecf20Sopenharmony_ci * transmitter and receiver are not enabled. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic s32 atl1_setup_link(struct atl1_hw *hw) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct pci_dev *pdev = hw->back->pdev; 7738c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = hw->back; 7748c2ecf20Sopenharmony_ci s32 ret_val; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* 7778c2ecf20Sopenharmony_ci * Options: 7788c2ecf20Sopenharmony_ci * PHY will advertise value(s) parsed from 7798c2ecf20Sopenharmony_ci * autoneg_advertised and fc 7808c2ecf20Sopenharmony_ci * no matter what autoneg is , We will not wait link result. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci ret_val = atl1_phy_setup_autoneg_adv(hw); 7838c2ecf20Sopenharmony_ci if (ret_val) { 7848c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 7858c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, 7868c2ecf20Sopenharmony_ci "error setting up autonegotiation\n"); 7878c2ecf20Sopenharmony_ci return ret_val; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci /* SW.Reset , En-Auto-Neg if needed */ 7908c2ecf20Sopenharmony_ci ret_val = atl1_phy_reset(hw); 7918c2ecf20Sopenharmony_ci if (ret_val) { 7928c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 7938c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "error resetting phy\n"); 7948c2ecf20Sopenharmony_ci return ret_val; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci hw->phy_configured = true; 7978c2ecf20Sopenharmony_ci return ret_val; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic void atl1_init_flash_opcode(struct atl1_hw *hw) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci if (hw->flash_vendor >= ARRAY_SIZE(flash_table)) 8038c2ecf20Sopenharmony_ci /* Atmel */ 8048c2ecf20Sopenharmony_ci hw->flash_vendor = 0; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Init OP table */ 8078c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_program, 8088c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_PROGRAM); 8098c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_sector_erase, 8108c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_SC_ERASE); 8118c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_chip_erase, 8128c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_CHIP_ERASE); 8138c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_rdid, 8148c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_RDID); 8158c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_wren, 8168c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_WREN); 8178c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_rdsr, 8188c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_RDSR); 8198c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_wrsr, 8208c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_WRSR); 8218c2ecf20Sopenharmony_ci iowrite8(flash_table[hw->flash_vendor].cmd_read, 8228c2ecf20Sopenharmony_ci hw->hw_addr + REG_SPI_FLASH_OP_READ); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci/* 8268c2ecf20Sopenharmony_ci * Performs basic configuration of the adapter. 8278c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 8288c2ecf20Sopenharmony_ci * Assumes that the controller has previously been reset and is in a 8298c2ecf20Sopenharmony_ci * post-reset uninitialized state. Initializes multicast table, 8308c2ecf20Sopenharmony_ci * and Calls routines to setup link 8318c2ecf20Sopenharmony_ci * Leaves the transmit and receive units disabled and uninitialized. 8328c2ecf20Sopenharmony_ci */ 8338c2ecf20Sopenharmony_cistatic s32 atl1_init_hw(struct atl1_hw *hw) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci u32 ret_val = 0; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* Zero out the Multicast HASH table */ 8388c2ecf20Sopenharmony_ci iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE); 8398c2ecf20Sopenharmony_ci /* clear the old settings from the multicast hash table */ 8408c2ecf20Sopenharmony_ci iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci atl1_init_flash_opcode(hw); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (!hw->phy_configured) { 8458c2ecf20Sopenharmony_ci /* enable GPHY LinkChange Interrupt */ 8468c2ecf20Sopenharmony_ci ret_val = atl1_write_phy_reg(hw, 18, 0xC00); 8478c2ecf20Sopenharmony_ci if (ret_val) 8488c2ecf20Sopenharmony_ci return ret_val; 8498c2ecf20Sopenharmony_ci /* make PHY out of power-saving state */ 8508c2ecf20Sopenharmony_ci ret_val = atl1_phy_leave_power_saving(hw); 8518c2ecf20Sopenharmony_ci if (ret_val) 8528c2ecf20Sopenharmony_ci return ret_val; 8538c2ecf20Sopenharmony_ci /* Call a subroutine to configure the link */ 8548c2ecf20Sopenharmony_ci ret_val = atl1_setup_link(hw); 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci return ret_val; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* 8608c2ecf20Sopenharmony_ci * Detects the current speed and duplex settings of the hardware. 8618c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 8628c2ecf20Sopenharmony_ci * speed - Speed of the connection 8638c2ecf20Sopenharmony_ci * duplex - Duplex setting of the connection 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_cistatic s32 atl1_get_speed_and_duplex(struct atl1_hw *hw, u16 *speed, u16 *duplex) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct pci_dev *pdev = hw->back->pdev; 8688c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = hw->back; 8698c2ecf20Sopenharmony_ci s32 ret_val; 8708c2ecf20Sopenharmony_ci u16 phy_data; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* ; --- Read PHY Specific Status Register (17) */ 8738c2ecf20Sopenharmony_ci ret_val = atl1_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data); 8748c2ecf20Sopenharmony_ci if (ret_val) 8758c2ecf20Sopenharmony_ci return ret_val; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED)) 8788c2ecf20Sopenharmony_ci return ATLX_ERR_PHY_RES; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci switch (phy_data & MII_ATLX_PSSR_SPEED) { 8818c2ecf20Sopenharmony_ci case MII_ATLX_PSSR_1000MBS: 8828c2ecf20Sopenharmony_ci *speed = SPEED_1000; 8838c2ecf20Sopenharmony_ci break; 8848c2ecf20Sopenharmony_ci case MII_ATLX_PSSR_100MBS: 8858c2ecf20Sopenharmony_ci *speed = SPEED_100; 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci case MII_ATLX_PSSR_10MBS: 8888c2ecf20Sopenharmony_ci *speed = SPEED_10; 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci default: 8918c2ecf20Sopenharmony_ci if (netif_msg_hw(adapter)) 8928c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "error getting speed\n"); 8938c2ecf20Sopenharmony_ci return ATLX_ERR_PHY_SPEED; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci if (phy_data & MII_ATLX_PSSR_DPLX) 8968c2ecf20Sopenharmony_ci *duplex = FULL_DUPLEX; 8978c2ecf20Sopenharmony_ci else 8988c2ecf20Sopenharmony_ci *duplex = HALF_DUPLEX; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic void atl1_set_mac_addr(struct atl1_hw *hw) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci u32 value; 9068c2ecf20Sopenharmony_ci /* 9078c2ecf20Sopenharmony_ci * 00-0B-6A-F6-00-DC 9088c2ecf20Sopenharmony_ci * 0: 6AF600DC 1: 000B 9098c2ecf20Sopenharmony_ci * low dword 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_ci value = (((u32) hw->mac_addr[2]) << 24) | 9128c2ecf20Sopenharmony_ci (((u32) hw->mac_addr[3]) << 16) | 9138c2ecf20Sopenharmony_ci (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5])); 9148c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR); 9158c2ecf20Sopenharmony_ci /* high dword */ 9168c2ecf20Sopenharmony_ci value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1])); 9178c2ecf20Sopenharmony_ci iowrite32(value, (hw->hw_addr + REG_MAC_STA_ADDR) + (1 << 2)); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/** 9218c2ecf20Sopenharmony_ci * atl1_sw_init - Initialize general software structures (struct atl1_adapter) 9228c2ecf20Sopenharmony_ci * @adapter: board private structure to initialize 9238c2ecf20Sopenharmony_ci * 9248c2ecf20Sopenharmony_ci * atl1_sw_init initializes the Adapter private data structure. 9258c2ecf20Sopenharmony_ci * Fields are initialized based on PCI device information and 9268c2ecf20Sopenharmony_ci * OS network device settings (MTU size). 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_cistatic int atl1_sw_init(struct atl1_adapter *adapter) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 9318c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 9348c2ecf20Sopenharmony_ci hw->min_frame_size = ETH_ZLEN + ETH_FCS_LEN; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci adapter->wol = 0; 9378c2ecf20Sopenharmony_ci device_set_wakeup_enable(&adapter->pdev->dev, false); 9388c2ecf20Sopenharmony_ci adapter->rx_buffer_len = (hw->max_frame_size + 7) & ~7; 9398c2ecf20Sopenharmony_ci adapter->ict = 50000; /* 100ms */ 9408c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; /* hardware init */ 9418c2ecf20Sopenharmony_ci adapter->link_duplex = FULL_DUPLEX; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci hw->phy_configured = false; 9448c2ecf20Sopenharmony_ci hw->preamble_len = 7; 9458c2ecf20Sopenharmony_ci hw->ipgt = 0x60; 9468c2ecf20Sopenharmony_ci hw->min_ifg = 0x50; 9478c2ecf20Sopenharmony_ci hw->ipgr1 = 0x40; 9488c2ecf20Sopenharmony_ci hw->ipgr2 = 0x60; 9498c2ecf20Sopenharmony_ci hw->max_retry = 0xf; 9508c2ecf20Sopenharmony_ci hw->lcol = 0x37; 9518c2ecf20Sopenharmony_ci hw->jam_ipg = 7; 9528c2ecf20Sopenharmony_ci hw->rfd_burst = 8; 9538c2ecf20Sopenharmony_ci hw->rrd_burst = 8; 9548c2ecf20Sopenharmony_ci hw->rfd_fetch_gap = 1; 9558c2ecf20Sopenharmony_ci hw->rx_jumbo_th = adapter->rx_buffer_len / 8; 9568c2ecf20Sopenharmony_ci hw->rx_jumbo_lkah = 1; 9578c2ecf20Sopenharmony_ci hw->rrd_ret_timer = 16; 9588c2ecf20Sopenharmony_ci hw->tpd_burst = 4; 9598c2ecf20Sopenharmony_ci hw->tpd_fetch_th = 16; 9608c2ecf20Sopenharmony_ci hw->txf_burst = 0x100; 9618c2ecf20Sopenharmony_ci hw->tx_jumbo_task_th = (hw->max_frame_size + 7) >> 3; 9628c2ecf20Sopenharmony_ci hw->tpd_fetch_gap = 1; 9638c2ecf20Sopenharmony_ci hw->rcb_value = atl1_rcb_64; 9648c2ecf20Sopenharmony_ci hw->dma_ord = atl1_dma_ord_enh; 9658c2ecf20Sopenharmony_ci hw->dmar_block = atl1_dma_req_256; 9668c2ecf20Sopenharmony_ci hw->dmaw_block = atl1_dma_req_256; 9678c2ecf20Sopenharmony_ci hw->cmb_rrd = 4; 9688c2ecf20Sopenharmony_ci hw->cmb_tpd = 4; 9698c2ecf20Sopenharmony_ci hw->cmb_rx_timer = 1; /* about 2us */ 9708c2ecf20Sopenharmony_ci hw->cmb_tx_timer = 1; /* about 2us */ 9718c2ecf20Sopenharmony_ci hw->smb_timer = 100000; /* about 200ms */ 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci spin_lock_init(&adapter->lock); 9748c2ecf20Sopenharmony_ci spin_lock_init(&adapter->mb_lock); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *netdev, int phy_id, int reg_num) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 9828c2ecf20Sopenharmony_ci u16 result; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci atl1_read_phy_reg(&adapter->hw, reg_num & 0x1f, &result); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci return result; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *netdev, int phy_id, int reg_num, 9908c2ecf20Sopenharmony_ci int val) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci atl1_write_phy_reg(&adapter->hw, reg_num, val); 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 10008c2ecf20Sopenharmony_ci unsigned long flags; 10018c2ecf20Sopenharmony_ci int retval; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 10048c2ecf20Sopenharmony_ci return -EINVAL; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->lock, flags); 10078c2ecf20Sopenharmony_ci retval = generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL); 10088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->lock, flags); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci return retval; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/** 10148c2ecf20Sopenharmony_ci * atl1_setup_mem_resources - allocate Tx / RX descriptor resources 10158c2ecf20Sopenharmony_ci * @adapter: board private structure 10168c2ecf20Sopenharmony_ci * 10178c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_cistatic s32 atl1_setup_ring_resources(struct atl1_adapter *adapter) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 10228c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 10238c2ecf20Sopenharmony_ci struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; 10248c2ecf20Sopenharmony_ci struct atl1_ring_header *ring_header = &adapter->ring_header; 10258c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 10268c2ecf20Sopenharmony_ci int size; 10278c2ecf20Sopenharmony_ci u8 offset = 0; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci size = sizeof(struct atl1_buffer) * (tpd_ring->count + rfd_ring->count); 10308c2ecf20Sopenharmony_ci tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); 10318c2ecf20Sopenharmony_ci if (unlikely(!tpd_ring->buffer_info)) { 10328c2ecf20Sopenharmony_ci if (netif_msg_drv(adapter)) 10338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "kzalloc failed , size = D%d\n", 10348c2ecf20Sopenharmony_ci size); 10358c2ecf20Sopenharmony_ci goto err_nomem; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci rfd_ring->buffer_info = 10388c2ecf20Sopenharmony_ci (tpd_ring->buffer_info + tpd_ring->count); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* 10418c2ecf20Sopenharmony_ci * real ring DMA buffer 10428c2ecf20Sopenharmony_ci * each ring/block may need up to 8 bytes for alignment, hence the 10438c2ecf20Sopenharmony_ci * additional 40 bytes tacked onto the end. 10448c2ecf20Sopenharmony_ci */ 10458c2ecf20Sopenharmony_ci ring_header->size = 10468c2ecf20Sopenharmony_ci sizeof(struct tx_packet_desc) * tpd_ring->count 10478c2ecf20Sopenharmony_ci + sizeof(struct rx_free_desc) * rfd_ring->count 10488c2ecf20Sopenharmony_ci + sizeof(struct rx_return_desc) * rrd_ring->count 10498c2ecf20Sopenharmony_ci + sizeof(struct coals_msg_block) 10508c2ecf20Sopenharmony_ci + sizeof(struct stats_msg_block) 10518c2ecf20Sopenharmony_ci + 40; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci ring_header->desc = dma_alloc_coherent(&pdev->dev, ring_header->size, 10548c2ecf20Sopenharmony_ci &ring_header->dma, GFP_KERNEL); 10558c2ecf20Sopenharmony_ci if (unlikely(!ring_header->desc)) { 10568c2ecf20Sopenharmony_ci if (netif_msg_drv(adapter)) 10578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "dma_alloc_coherent failed\n"); 10588c2ecf20Sopenharmony_ci goto err_nomem; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* init TPD ring */ 10628c2ecf20Sopenharmony_ci tpd_ring->dma = ring_header->dma; 10638c2ecf20Sopenharmony_ci offset = (tpd_ring->dma & 0x7) ? (8 - (ring_header->dma & 0x7)) : 0; 10648c2ecf20Sopenharmony_ci tpd_ring->dma += offset; 10658c2ecf20Sopenharmony_ci tpd_ring->desc = (u8 *) ring_header->desc + offset; 10668c2ecf20Sopenharmony_ci tpd_ring->size = sizeof(struct tx_packet_desc) * tpd_ring->count; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* init RFD ring */ 10698c2ecf20Sopenharmony_ci rfd_ring->dma = tpd_ring->dma + tpd_ring->size; 10708c2ecf20Sopenharmony_ci offset = (rfd_ring->dma & 0x7) ? (8 - (rfd_ring->dma & 0x7)) : 0; 10718c2ecf20Sopenharmony_ci rfd_ring->dma += offset; 10728c2ecf20Sopenharmony_ci rfd_ring->desc = (u8 *) tpd_ring->desc + (tpd_ring->size + offset); 10738c2ecf20Sopenharmony_ci rfd_ring->size = sizeof(struct rx_free_desc) * rfd_ring->count; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* init RRD ring */ 10778c2ecf20Sopenharmony_ci rrd_ring->dma = rfd_ring->dma + rfd_ring->size; 10788c2ecf20Sopenharmony_ci offset = (rrd_ring->dma & 0x7) ? (8 - (rrd_ring->dma & 0x7)) : 0; 10798c2ecf20Sopenharmony_ci rrd_ring->dma += offset; 10808c2ecf20Sopenharmony_ci rrd_ring->desc = (u8 *) rfd_ring->desc + (rfd_ring->size + offset); 10818c2ecf20Sopenharmony_ci rrd_ring->size = sizeof(struct rx_return_desc) * rrd_ring->count; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* init CMB */ 10858c2ecf20Sopenharmony_ci adapter->cmb.dma = rrd_ring->dma + rrd_ring->size; 10868c2ecf20Sopenharmony_ci offset = (adapter->cmb.dma & 0x7) ? (8 - (adapter->cmb.dma & 0x7)) : 0; 10878c2ecf20Sopenharmony_ci adapter->cmb.dma += offset; 10888c2ecf20Sopenharmony_ci adapter->cmb.cmb = (struct coals_msg_block *) 10898c2ecf20Sopenharmony_ci ((u8 *) rrd_ring->desc + (rrd_ring->size + offset)); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* init SMB */ 10928c2ecf20Sopenharmony_ci adapter->smb.dma = adapter->cmb.dma + sizeof(struct coals_msg_block); 10938c2ecf20Sopenharmony_ci offset = (adapter->smb.dma & 0x7) ? (8 - (adapter->smb.dma & 0x7)) : 0; 10948c2ecf20Sopenharmony_ci adapter->smb.dma += offset; 10958c2ecf20Sopenharmony_ci adapter->smb.smb = (struct stats_msg_block *) 10968c2ecf20Sopenharmony_ci ((u8 *) adapter->cmb.cmb + 10978c2ecf20Sopenharmony_ci (sizeof(struct coals_msg_block) + offset)); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cierr_nomem: 11028c2ecf20Sopenharmony_ci kfree(tpd_ring->buffer_info); 11038c2ecf20Sopenharmony_ci return -ENOMEM; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void atl1_init_ring_ptrs(struct atl1_adapter *adapter) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 11098c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 11108c2ecf20Sopenharmony_ci struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci atomic_set(&tpd_ring->next_to_use, 0); 11138c2ecf20Sopenharmony_ci atomic_set(&tpd_ring->next_to_clean, 0); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci rfd_ring->next_to_clean = 0; 11168c2ecf20Sopenharmony_ci atomic_set(&rfd_ring->next_to_use, 0); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci rrd_ring->next_to_use = 0; 11198c2ecf20Sopenharmony_ci atomic_set(&rrd_ring->next_to_clean, 0); 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci/** 11238c2ecf20Sopenharmony_ci * atl1_clean_rx_ring - Free RFD Buffers 11248c2ecf20Sopenharmony_ci * @adapter: board private structure 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_cistatic void atl1_clean_rx_ring(struct atl1_adapter *adapter) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 11298c2ecf20Sopenharmony_ci struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; 11308c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info; 11318c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 11328c2ecf20Sopenharmony_ci unsigned long size; 11338c2ecf20Sopenharmony_ci unsigned int i; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* Free all the Rx ring sk_buffs */ 11368c2ecf20Sopenharmony_ci for (i = 0; i < rfd_ring->count; i++) { 11378c2ecf20Sopenharmony_ci buffer_info = &rfd_ring->buffer_info[i]; 11388c2ecf20Sopenharmony_ci if (buffer_info->dma) { 11398c2ecf20Sopenharmony_ci dma_unmap_page(&pdev->dev, buffer_info->dma, 11408c2ecf20Sopenharmony_ci buffer_info->length, DMA_FROM_DEVICE); 11418c2ecf20Sopenharmony_ci buffer_info->dma = 0; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci if (buffer_info->skb) { 11448c2ecf20Sopenharmony_ci dev_kfree_skb(buffer_info->skb); 11458c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci size = sizeof(struct atl1_buffer) * rfd_ring->count; 11508c2ecf20Sopenharmony_ci memset(rfd_ring->buffer_info, 0, size); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* Zero out the descriptor ring */ 11538c2ecf20Sopenharmony_ci memset(rfd_ring->desc, 0, rfd_ring->size); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci rfd_ring->next_to_clean = 0; 11568c2ecf20Sopenharmony_ci atomic_set(&rfd_ring->next_to_use, 0); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci rrd_ring->next_to_use = 0; 11598c2ecf20Sopenharmony_ci atomic_set(&rrd_ring->next_to_clean, 0); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci/** 11638c2ecf20Sopenharmony_ci * atl1_clean_tx_ring - Free Tx Buffers 11648c2ecf20Sopenharmony_ci * @adapter: board private structure 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_cistatic void atl1_clean_tx_ring(struct atl1_adapter *adapter) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 11698c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info; 11708c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 11718c2ecf20Sopenharmony_ci unsigned long size; 11728c2ecf20Sopenharmony_ci unsigned int i; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* Free all the Tx ring sk_buffs */ 11758c2ecf20Sopenharmony_ci for (i = 0; i < tpd_ring->count; i++) { 11768c2ecf20Sopenharmony_ci buffer_info = &tpd_ring->buffer_info[i]; 11778c2ecf20Sopenharmony_ci if (buffer_info->dma) { 11788c2ecf20Sopenharmony_ci dma_unmap_page(&pdev->dev, buffer_info->dma, 11798c2ecf20Sopenharmony_ci buffer_info->length, DMA_TO_DEVICE); 11808c2ecf20Sopenharmony_ci buffer_info->dma = 0; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci for (i = 0; i < tpd_ring->count; i++) { 11858c2ecf20Sopenharmony_ci buffer_info = &tpd_ring->buffer_info[i]; 11868c2ecf20Sopenharmony_ci if (buffer_info->skb) { 11878c2ecf20Sopenharmony_ci dev_kfree_skb_any(buffer_info->skb); 11888c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci size = sizeof(struct atl1_buffer) * tpd_ring->count; 11938c2ecf20Sopenharmony_ci memset(tpd_ring->buffer_info, 0, size); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* Zero out the descriptor ring */ 11968c2ecf20Sopenharmony_ci memset(tpd_ring->desc, 0, tpd_ring->size); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci atomic_set(&tpd_ring->next_to_use, 0); 11998c2ecf20Sopenharmony_ci atomic_set(&tpd_ring->next_to_clean, 0); 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci/** 12038c2ecf20Sopenharmony_ci * atl1_free_ring_resources - Free Tx / RX descriptor Resources 12048c2ecf20Sopenharmony_ci * @adapter: board private structure 12058c2ecf20Sopenharmony_ci * 12068c2ecf20Sopenharmony_ci * Free all transmit software resources 12078c2ecf20Sopenharmony_ci */ 12088c2ecf20Sopenharmony_cistatic void atl1_free_ring_resources(struct atl1_adapter *adapter) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 12118c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 12128c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 12138c2ecf20Sopenharmony_ci struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; 12148c2ecf20Sopenharmony_ci struct atl1_ring_header *ring_header = &adapter->ring_header; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci atl1_clean_tx_ring(adapter); 12178c2ecf20Sopenharmony_ci atl1_clean_rx_ring(adapter); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci kfree(tpd_ring->buffer_info); 12208c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, ring_header->size, ring_header->desc, 12218c2ecf20Sopenharmony_ci ring_header->dma); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci tpd_ring->buffer_info = NULL; 12248c2ecf20Sopenharmony_ci tpd_ring->desc = NULL; 12258c2ecf20Sopenharmony_ci tpd_ring->dma = 0; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci rfd_ring->buffer_info = NULL; 12288c2ecf20Sopenharmony_ci rfd_ring->desc = NULL; 12298c2ecf20Sopenharmony_ci rfd_ring->dma = 0; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci rrd_ring->desc = NULL; 12328c2ecf20Sopenharmony_ci rrd_ring->dma = 0; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci adapter->cmb.dma = 0; 12358c2ecf20Sopenharmony_ci adapter->cmb.cmb = NULL; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci adapter->smb.dma = 0; 12388c2ecf20Sopenharmony_ci adapter->smb.smb = NULL; 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic void atl1_setup_mac_ctrl(struct atl1_adapter *adapter) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci u32 value; 12448c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 12458c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 12468c2ecf20Sopenharmony_ci /* Config MAC CTRL Register */ 12478c2ecf20Sopenharmony_ci value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN; 12488c2ecf20Sopenharmony_ci /* duplex */ 12498c2ecf20Sopenharmony_ci if (FULL_DUPLEX == adapter->link_duplex) 12508c2ecf20Sopenharmony_ci value |= MAC_CTRL_DUPLX; 12518c2ecf20Sopenharmony_ci /* speed */ 12528c2ecf20Sopenharmony_ci value |= ((u32) ((SPEED_1000 == adapter->link_speed) ? 12538c2ecf20Sopenharmony_ci MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << 12548c2ecf20Sopenharmony_ci MAC_CTRL_SPEED_SHIFT); 12558c2ecf20Sopenharmony_ci /* flow control */ 12568c2ecf20Sopenharmony_ci value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); 12578c2ecf20Sopenharmony_ci /* PAD & CRC */ 12588c2ecf20Sopenharmony_ci value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); 12598c2ecf20Sopenharmony_ci /* preamble length */ 12608c2ecf20Sopenharmony_ci value |= (((u32) adapter->hw.preamble_len 12618c2ecf20Sopenharmony_ci & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); 12628c2ecf20Sopenharmony_ci /* vlan */ 12638c2ecf20Sopenharmony_ci __atlx_vlan_mode(netdev->features, &value); 12648c2ecf20Sopenharmony_ci /* rx checksum 12658c2ecf20Sopenharmony_ci if (adapter->rx_csum) 12668c2ecf20Sopenharmony_ci value |= MAC_CTRL_RX_CHKSUM_EN; 12678c2ecf20Sopenharmony_ci */ 12688c2ecf20Sopenharmony_ci /* filter mode */ 12698c2ecf20Sopenharmony_ci value |= MAC_CTRL_BC_EN; 12708c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 12718c2ecf20Sopenharmony_ci value |= MAC_CTRL_PROMIS_EN; 12728c2ecf20Sopenharmony_ci else if (netdev->flags & IFF_ALLMULTI) 12738c2ecf20Sopenharmony_ci value |= MAC_CTRL_MC_ALL_EN; 12748c2ecf20Sopenharmony_ci /* value |= MAC_CTRL_LOOPBACK; */ 12758c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_MAC_CTRL); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic u32 atl1_check_link(struct atl1_adapter *adapter) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 12818c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 12828c2ecf20Sopenharmony_ci u32 ret_val; 12838c2ecf20Sopenharmony_ci u16 speed, duplex, phy_data; 12848c2ecf20Sopenharmony_ci int reconfig = 0; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci /* MII_BMSR must read twice */ 12878c2ecf20Sopenharmony_ci atl1_read_phy_reg(hw, MII_BMSR, &phy_data); 12888c2ecf20Sopenharmony_ci atl1_read_phy_reg(hw, MII_BMSR, &phy_data); 12898c2ecf20Sopenharmony_ci if (!(phy_data & BMSR_LSTATUS)) { 12908c2ecf20Sopenharmony_ci /* link down */ 12918c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { 12928c2ecf20Sopenharmony_ci /* old link state: Up */ 12938c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 12948c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "link is down\n"); 12958c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 12968c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci return 0; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Link Up */ 13028c2ecf20Sopenharmony_ci ret_val = atl1_get_speed_and_duplex(hw, &speed, &duplex); 13038c2ecf20Sopenharmony_ci if (ret_val) 13048c2ecf20Sopenharmony_ci return ret_val; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci switch (hw->media_type) { 13078c2ecf20Sopenharmony_ci case MEDIA_TYPE_1000M_FULL: 13088c2ecf20Sopenharmony_ci if (speed != SPEED_1000 || duplex != FULL_DUPLEX) 13098c2ecf20Sopenharmony_ci reconfig = 1; 13108c2ecf20Sopenharmony_ci break; 13118c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 13128c2ecf20Sopenharmony_ci if (speed != SPEED_100 || duplex != FULL_DUPLEX) 13138c2ecf20Sopenharmony_ci reconfig = 1; 13148c2ecf20Sopenharmony_ci break; 13158c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 13168c2ecf20Sopenharmony_ci if (speed != SPEED_100 || duplex != HALF_DUPLEX) 13178c2ecf20Sopenharmony_ci reconfig = 1; 13188c2ecf20Sopenharmony_ci break; 13198c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 13208c2ecf20Sopenharmony_ci if (speed != SPEED_10 || duplex != FULL_DUPLEX) 13218c2ecf20Sopenharmony_ci reconfig = 1; 13228c2ecf20Sopenharmony_ci break; 13238c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_HALF: 13248c2ecf20Sopenharmony_ci if (speed != SPEED_10 || duplex != HALF_DUPLEX) 13258c2ecf20Sopenharmony_ci reconfig = 1; 13268c2ecf20Sopenharmony_ci break; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* link result is our setting */ 13308c2ecf20Sopenharmony_ci if (!reconfig) { 13318c2ecf20Sopenharmony_ci if (adapter->link_speed != speed || 13328c2ecf20Sopenharmony_ci adapter->link_duplex != duplex) { 13338c2ecf20Sopenharmony_ci adapter->link_speed = speed; 13348c2ecf20Sopenharmony_ci adapter->link_duplex = duplex; 13358c2ecf20Sopenharmony_ci atl1_setup_mac_ctrl(adapter); 13368c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 13378c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 13388c2ecf20Sopenharmony_ci "%s link is up %d Mbps %s\n", 13398c2ecf20Sopenharmony_ci netdev->name, adapter->link_speed, 13408c2ecf20Sopenharmony_ci adapter->link_duplex == FULL_DUPLEX ? 13418c2ecf20Sopenharmony_ci "full duplex" : "half duplex"); 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci if (!netif_carrier_ok(netdev)) { 13448c2ecf20Sopenharmony_ci /* Link down -> Up */ 13458c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci return 0; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci /* change original link status */ 13518c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { 13528c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 13538c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 13548c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (hw->media_type != MEDIA_TYPE_AUTO_SENSOR && 13588c2ecf20Sopenharmony_ci hw->media_type != MEDIA_TYPE_1000M_FULL) { 13598c2ecf20Sopenharmony_ci switch (hw->media_type) { 13608c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 13618c2ecf20Sopenharmony_ci phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | 13628c2ecf20Sopenharmony_ci MII_CR_RESET; 13638c2ecf20Sopenharmony_ci break; 13648c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 13658c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_100 | MII_CR_RESET; 13668c2ecf20Sopenharmony_ci break; 13678c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 13688c2ecf20Sopenharmony_ci phy_data = 13698c2ecf20Sopenharmony_ci MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; 13708c2ecf20Sopenharmony_ci break; 13718c2ecf20Sopenharmony_ci default: 13728c2ecf20Sopenharmony_ci /* MEDIA_TYPE_10M_HALF: */ 13738c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_10 | MII_CR_RESET; 13748c2ecf20Sopenharmony_ci break; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci atl1_write_phy_reg(hw, MII_BMCR, phy_data); 13778c2ecf20Sopenharmony_ci return 0; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* auto-neg, insert timer to re-config phy */ 13818c2ecf20Sopenharmony_ci if (!adapter->phy_timer_pending) { 13828c2ecf20Sopenharmony_ci adapter->phy_timer_pending = true; 13838c2ecf20Sopenharmony_ci mod_timer(&adapter->phy_config_timer, 13848c2ecf20Sopenharmony_ci round_jiffies(jiffies + 3 * HZ)); 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void set_flow_ctrl_old(struct atl1_adapter *adapter) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci u32 hi, lo, value; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci /* RFD Flow Control */ 13958c2ecf20Sopenharmony_ci value = adapter->rfd_ring.count; 13968c2ecf20Sopenharmony_ci hi = value / 16; 13978c2ecf20Sopenharmony_ci if (hi < 2) 13988c2ecf20Sopenharmony_ci hi = 2; 13998c2ecf20Sopenharmony_ci lo = value * 7 / 8; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | 14028c2ecf20Sopenharmony_ci ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); 14038c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RXF_PAUSE_THRESH); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* RRD Flow Control */ 14068c2ecf20Sopenharmony_ci value = adapter->rrd_ring.count; 14078c2ecf20Sopenharmony_ci lo = value / 16; 14088c2ecf20Sopenharmony_ci hi = value * 7 / 8; 14098c2ecf20Sopenharmony_ci if (lo < 2) 14108c2ecf20Sopenharmony_ci lo = 2; 14118c2ecf20Sopenharmony_ci value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) | 14128c2ecf20Sopenharmony_ci ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT); 14138c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RRD_PAUSE_THRESH); 14148c2ecf20Sopenharmony_ci} 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_cistatic void set_flow_ctrl_new(struct atl1_hw *hw) 14178c2ecf20Sopenharmony_ci{ 14188c2ecf20Sopenharmony_ci u32 hi, lo, value; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci /* RXF Flow Control */ 14218c2ecf20Sopenharmony_ci value = ioread32(hw->hw_addr + REG_SRAM_RXF_LEN); 14228c2ecf20Sopenharmony_ci lo = value / 16; 14238c2ecf20Sopenharmony_ci if (lo < 192) 14248c2ecf20Sopenharmony_ci lo = 192; 14258c2ecf20Sopenharmony_ci hi = value * 7 / 8; 14268c2ecf20Sopenharmony_ci if (hi < lo) 14278c2ecf20Sopenharmony_ci hi = lo + 16; 14288c2ecf20Sopenharmony_ci value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | 14298c2ecf20Sopenharmony_ci ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); 14308c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_RXQ_RXF_PAUSE_THRESH); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci /* RRD Flow Control */ 14338c2ecf20Sopenharmony_ci value = ioread32(hw->hw_addr + REG_SRAM_RRD_LEN); 14348c2ecf20Sopenharmony_ci lo = value / 8; 14358c2ecf20Sopenharmony_ci hi = value * 7 / 8; 14368c2ecf20Sopenharmony_ci if (lo < 2) 14378c2ecf20Sopenharmony_ci lo = 2; 14388c2ecf20Sopenharmony_ci if (hi < lo) 14398c2ecf20Sopenharmony_ci hi = lo + 3; 14408c2ecf20Sopenharmony_ci value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) | 14418c2ecf20Sopenharmony_ci ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT); 14428c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_RXQ_RRD_PAUSE_THRESH); 14438c2ecf20Sopenharmony_ci} 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci/** 14468c2ecf20Sopenharmony_ci * atl1_configure - Configure Transmit&Receive Unit after Reset 14478c2ecf20Sopenharmony_ci * @adapter: board private structure 14488c2ecf20Sopenharmony_ci * 14498c2ecf20Sopenharmony_ci * Configure the Tx /Rx unit of the MAC after a reset. 14508c2ecf20Sopenharmony_ci */ 14518c2ecf20Sopenharmony_cistatic u32 atl1_configure(struct atl1_adapter *adapter) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 14548c2ecf20Sopenharmony_ci u32 value; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci /* clear interrupt status */ 14578c2ecf20Sopenharmony_ci iowrite32(0xffffffff, adapter->hw.hw_addr + REG_ISR); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci /* set MAC Address */ 14608c2ecf20Sopenharmony_ci value = (((u32) hw->mac_addr[2]) << 24) | 14618c2ecf20Sopenharmony_ci (((u32) hw->mac_addr[3]) << 16) | 14628c2ecf20Sopenharmony_ci (((u32) hw->mac_addr[4]) << 8) | 14638c2ecf20Sopenharmony_ci (((u32) hw->mac_addr[5])); 14648c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR); 14658c2ecf20Sopenharmony_ci value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1])); 14668c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + (REG_MAC_STA_ADDR + 4)); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci /* tx / rx ring */ 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* HI base address */ 14718c2ecf20Sopenharmony_ci iowrite32((u32) ((adapter->tpd_ring.dma & 0xffffffff00000000ULL) >> 32), 14728c2ecf20Sopenharmony_ci hw->hw_addr + REG_DESC_BASE_ADDR_HI); 14738c2ecf20Sopenharmony_ci /* LO base address */ 14748c2ecf20Sopenharmony_ci iowrite32((u32) (adapter->rfd_ring.dma & 0x00000000ffffffffULL), 14758c2ecf20Sopenharmony_ci hw->hw_addr + REG_DESC_RFD_ADDR_LO); 14768c2ecf20Sopenharmony_ci iowrite32((u32) (adapter->rrd_ring.dma & 0x00000000ffffffffULL), 14778c2ecf20Sopenharmony_ci hw->hw_addr + REG_DESC_RRD_ADDR_LO); 14788c2ecf20Sopenharmony_ci iowrite32((u32) (adapter->tpd_ring.dma & 0x00000000ffffffffULL), 14798c2ecf20Sopenharmony_ci hw->hw_addr + REG_DESC_TPD_ADDR_LO); 14808c2ecf20Sopenharmony_ci iowrite32((u32) (adapter->cmb.dma & 0x00000000ffffffffULL), 14818c2ecf20Sopenharmony_ci hw->hw_addr + REG_DESC_CMB_ADDR_LO); 14828c2ecf20Sopenharmony_ci iowrite32((u32) (adapter->smb.dma & 0x00000000ffffffffULL), 14838c2ecf20Sopenharmony_ci hw->hw_addr + REG_DESC_SMB_ADDR_LO); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci /* element count */ 14868c2ecf20Sopenharmony_ci value = adapter->rrd_ring.count; 14878c2ecf20Sopenharmony_ci value <<= 16; 14888c2ecf20Sopenharmony_ci value += adapter->rfd_ring.count; 14898c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_DESC_RFD_RRD_RING_SIZE); 14908c2ecf20Sopenharmony_ci iowrite32(adapter->tpd_ring.count, hw->hw_addr + 14918c2ecf20Sopenharmony_ci REG_DESC_TPD_RING_SIZE); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci /* Load Ptr */ 14948c2ecf20Sopenharmony_ci iowrite32(1, hw->hw_addr + REG_LOAD_PTR); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* config Mailbox */ 14978c2ecf20Sopenharmony_ci value = ((atomic_read(&adapter->tpd_ring.next_to_use) 14988c2ecf20Sopenharmony_ci & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT) | 14998c2ecf20Sopenharmony_ci ((atomic_read(&adapter->rrd_ring.next_to_clean) 15008c2ecf20Sopenharmony_ci & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) | 15018c2ecf20Sopenharmony_ci ((atomic_read(&adapter->rfd_ring.next_to_use) 15028c2ecf20Sopenharmony_ci & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT); 15038c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_MAILBOX); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci /* config IPG/IFG */ 15068c2ecf20Sopenharmony_ci value = (((u32) hw->ipgt & MAC_IPG_IFG_IPGT_MASK) 15078c2ecf20Sopenharmony_ci << MAC_IPG_IFG_IPGT_SHIFT) | 15088c2ecf20Sopenharmony_ci (((u32) hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) 15098c2ecf20Sopenharmony_ci << MAC_IPG_IFG_MIFG_SHIFT) | 15108c2ecf20Sopenharmony_ci (((u32) hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) 15118c2ecf20Sopenharmony_ci << MAC_IPG_IFG_IPGR1_SHIFT) | 15128c2ecf20Sopenharmony_ci (((u32) hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) 15138c2ecf20Sopenharmony_ci << MAC_IPG_IFG_IPGR2_SHIFT); 15148c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_MAC_IPG_IFG); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* config Half-Duplex Control */ 15178c2ecf20Sopenharmony_ci value = ((u32) hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | 15188c2ecf20Sopenharmony_ci (((u32) hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) 15198c2ecf20Sopenharmony_ci << MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | 15208c2ecf20Sopenharmony_ci MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | 15218c2ecf20Sopenharmony_ci (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | 15228c2ecf20Sopenharmony_ci (((u32) hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) 15238c2ecf20Sopenharmony_ci << MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); 15248c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_MAC_HALF_DUPLX_CTRL); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* set Interrupt Moderator Timer */ 15278c2ecf20Sopenharmony_ci iowrite16(adapter->imt, hw->hw_addr + REG_IRQ_MODU_TIMER_INIT); 15288c2ecf20Sopenharmony_ci iowrite32(MASTER_CTRL_ITIMER_EN, hw->hw_addr + REG_MASTER_CTRL); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* set Interrupt Clear Timer */ 15318c2ecf20Sopenharmony_ci iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci /* set max frame size hw will accept */ 15348c2ecf20Sopenharmony_ci iowrite32(hw->max_frame_size, hw->hw_addr + REG_MTU); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* jumbo size & rrd retirement timer */ 15378c2ecf20Sopenharmony_ci value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) 15388c2ecf20Sopenharmony_ci << RXQ_JMBOSZ_TH_SHIFT) | 15398c2ecf20Sopenharmony_ci (((u32) hw->rx_jumbo_lkah & RXQ_JMBO_LKAH_MASK) 15408c2ecf20Sopenharmony_ci << RXQ_JMBO_LKAH_SHIFT) | 15418c2ecf20Sopenharmony_ci (((u32) hw->rrd_ret_timer & RXQ_RRD_TIMER_MASK) 15428c2ecf20Sopenharmony_ci << RXQ_RRD_TIMER_SHIFT); 15438c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_RXQ_JMBOSZ_RRDTIM); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* Flow Control */ 15468c2ecf20Sopenharmony_ci switch (hw->dev_rev) { 15478c2ecf20Sopenharmony_ci case 0x8001: 15488c2ecf20Sopenharmony_ci case 0x9001: 15498c2ecf20Sopenharmony_ci case 0x9002: 15508c2ecf20Sopenharmony_ci case 0x9003: 15518c2ecf20Sopenharmony_ci set_flow_ctrl_old(adapter); 15528c2ecf20Sopenharmony_ci break; 15538c2ecf20Sopenharmony_ci default: 15548c2ecf20Sopenharmony_ci set_flow_ctrl_new(hw); 15558c2ecf20Sopenharmony_ci break; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* config TXQ */ 15598c2ecf20Sopenharmony_ci value = (((u32) hw->tpd_burst & TXQ_CTRL_TPD_BURST_NUM_MASK) 15608c2ecf20Sopenharmony_ci << TXQ_CTRL_TPD_BURST_NUM_SHIFT) | 15618c2ecf20Sopenharmony_ci (((u32) hw->txf_burst & TXQ_CTRL_TXF_BURST_NUM_MASK) 15628c2ecf20Sopenharmony_ci << TXQ_CTRL_TXF_BURST_NUM_SHIFT) | 15638c2ecf20Sopenharmony_ci (((u32) hw->tpd_fetch_th & TXQ_CTRL_TPD_FETCH_TH_MASK) 15648c2ecf20Sopenharmony_ci << TXQ_CTRL_TPD_FETCH_TH_SHIFT) | TXQ_CTRL_ENH_MODE | 15658c2ecf20Sopenharmony_ci TXQ_CTRL_EN; 15668c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_TXQ_CTRL); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci /* min tpd fetch gap & tx jumbo packet size threshold for taskoffload */ 15698c2ecf20Sopenharmony_ci value = (((u32) hw->tx_jumbo_task_th & TX_JUMBO_TASK_TH_MASK) 15708c2ecf20Sopenharmony_ci << TX_JUMBO_TASK_TH_SHIFT) | 15718c2ecf20Sopenharmony_ci (((u32) hw->tpd_fetch_gap & TX_TPD_MIN_IPG_MASK) 15728c2ecf20Sopenharmony_ci << TX_TPD_MIN_IPG_SHIFT); 15738c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_TX_JUMBO_TASK_TH_TPD_IPG); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci /* config RXQ */ 15768c2ecf20Sopenharmony_ci value = (((u32) hw->rfd_burst & RXQ_CTRL_RFD_BURST_NUM_MASK) 15778c2ecf20Sopenharmony_ci << RXQ_CTRL_RFD_BURST_NUM_SHIFT) | 15788c2ecf20Sopenharmony_ci (((u32) hw->rrd_burst & RXQ_CTRL_RRD_BURST_THRESH_MASK) 15798c2ecf20Sopenharmony_ci << RXQ_CTRL_RRD_BURST_THRESH_SHIFT) | 15808c2ecf20Sopenharmony_ci (((u32) hw->rfd_fetch_gap & RXQ_CTRL_RFD_PREF_MIN_IPG_MASK) 15818c2ecf20Sopenharmony_ci << RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT) | RXQ_CTRL_CUT_THRU_EN | 15828c2ecf20Sopenharmony_ci RXQ_CTRL_EN; 15838c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_RXQ_CTRL); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* config DMA Engine */ 15868c2ecf20Sopenharmony_ci value = ((((u32) hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) 15878c2ecf20Sopenharmony_ci << DMA_CTRL_DMAR_BURST_LEN_SHIFT) | 15888c2ecf20Sopenharmony_ci ((((u32) hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) 15898c2ecf20Sopenharmony_ci << DMA_CTRL_DMAW_BURST_LEN_SHIFT) | DMA_CTRL_DMAR_EN | 15908c2ecf20Sopenharmony_ci DMA_CTRL_DMAW_EN; 15918c2ecf20Sopenharmony_ci value |= (u32) hw->dma_ord; 15928c2ecf20Sopenharmony_ci if (atl1_rcb_128 == hw->rcb_value) 15938c2ecf20Sopenharmony_ci value |= DMA_CTRL_RCB_VALUE; 15948c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_DMA_CTRL); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* config CMB / SMB */ 15978c2ecf20Sopenharmony_ci value = (hw->cmb_tpd > adapter->tpd_ring.count) ? 15988c2ecf20Sopenharmony_ci hw->cmb_tpd : adapter->tpd_ring.count; 15998c2ecf20Sopenharmony_ci value <<= 16; 16008c2ecf20Sopenharmony_ci value |= hw->cmb_rrd; 16018c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TH); 16028c2ecf20Sopenharmony_ci value = hw->cmb_rx_timer | ((u32) hw->cmb_tx_timer << 16); 16038c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TIMER); 16048c2ecf20Sopenharmony_ci iowrite32(hw->smb_timer, hw->hw_addr + REG_SMB_TIMER); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* --- enable CMB / SMB */ 16078c2ecf20Sopenharmony_ci value = CSMB_CTRL_CMB_EN | CSMB_CTRL_SMB_EN; 16088c2ecf20Sopenharmony_ci iowrite32(value, hw->hw_addr + REG_CSMB_CTRL); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci value = ioread32(adapter->hw.hw_addr + REG_ISR); 16118c2ecf20Sopenharmony_ci if (unlikely((value & ISR_PHY_LINKDOWN) != 0)) 16128c2ecf20Sopenharmony_ci value = 1; /* config failed */ 16138c2ecf20Sopenharmony_ci else 16148c2ecf20Sopenharmony_ci value = 0; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci /* clear all interrupt status */ 16178c2ecf20Sopenharmony_ci iowrite32(0x3fffffff, adapter->hw.hw_addr + REG_ISR); 16188c2ecf20Sopenharmony_ci iowrite32(0, adapter->hw.hw_addr + REG_ISR); 16198c2ecf20Sopenharmony_ci return value; 16208c2ecf20Sopenharmony_ci} 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci/* 16238c2ecf20Sopenharmony_ci * atl1_pcie_patch - Patch for PCIE module 16248c2ecf20Sopenharmony_ci */ 16258c2ecf20Sopenharmony_cistatic void atl1_pcie_patch(struct atl1_adapter *adapter) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci u32 value; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* much vendor magic here */ 16308c2ecf20Sopenharmony_ci value = 0x6500; 16318c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + 0x12FC); 16328c2ecf20Sopenharmony_ci /* pcie flow control mode change */ 16338c2ecf20Sopenharmony_ci value = ioread32(adapter->hw.hw_addr + 0x1008); 16348c2ecf20Sopenharmony_ci value |= 0x8000; 16358c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + 0x1008); 16368c2ecf20Sopenharmony_ci} 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci/* 16398c2ecf20Sopenharmony_ci * When ACPI resume on some VIA MotherBoard, the Interrupt Disable bit/0x400 16408c2ecf20Sopenharmony_ci * on PCI Command register is disable. 16418c2ecf20Sopenharmony_ci * The function enable this bit. 16428c2ecf20Sopenharmony_ci * Brackett, 2006/03/15 16438c2ecf20Sopenharmony_ci */ 16448c2ecf20Sopenharmony_cistatic void atl1_via_workaround(struct atl1_adapter *adapter) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci unsigned long value; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci value = ioread16(adapter->hw.hw_addr + PCI_COMMAND); 16498c2ecf20Sopenharmony_ci if (value & PCI_COMMAND_INTX_DISABLE) 16508c2ecf20Sopenharmony_ci value &= ~PCI_COMMAND_INTX_DISABLE; 16518c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + PCI_COMMAND); 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_cistatic void atl1_inc_smb(struct atl1_adapter *adapter) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 16578c2ecf20Sopenharmony_ci struct stats_msg_block *smb = adapter->smb.smb; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci u64 new_rx_errors = smb->rx_frag + 16608c2ecf20Sopenharmony_ci smb->rx_fcs_err + 16618c2ecf20Sopenharmony_ci smb->rx_len_err + 16628c2ecf20Sopenharmony_ci smb->rx_sz_ov + 16638c2ecf20Sopenharmony_ci smb->rx_rxf_ov + 16648c2ecf20Sopenharmony_ci smb->rx_rrd_ov + 16658c2ecf20Sopenharmony_ci smb->rx_align_err; 16668c2ecf20Sopenharmony_ci u64 new_tx_errors = smb->tx_late_col + 16678c2ecf20Sopenharmony_ci smb->tx_abort_col + 16688c2ecf20Sopenharmony_ci smb->tx_underrun + 16698c2ecf20Sopenharmony_ci smb->tx_trunc; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* Fill out the OS statistics structure */ 16728c2ecf20Sopenharmony_ci adapter->soft_stats.rx_packets += smb->rx_ok + new_rx_errors; 16738c2ecf20Sopenharmony_ci adapter->soft_stats.tx_packets += smb->tx_ok + new_tx_errors; 16748c2ecf20Sopenharmony_ci adapter->soft_stats.rx_bytes += smb->rx_byte_cnt; 16758c2ecf20Sopenharmony_ci adapter->soft_stats.tx_bytes += smb->tx_byte_cnt; 16768c2ecf20Sopenharmony_ci adapter->soft_stats.multicast += smb->rx_mcast; 16778c2ecf20Sopenharmony_ci adapter->soft_stats.collisions += smb->tx_1_col + 16788c2ecf20Sopenharmony_ci smb->tx_2_col + 16798c2ecf20Sopenharmony_ci smb->tx_late_col + 16808c2ecf20Sopenharmony_ci smb->tx_abort_col; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci /* Rx Errors */ 16838c2ecf20Sopenharmony_ci adapter->soft_stats.rx_errors += new_rx_errors; 16848c2ecf20Sopenharmony_ci adapter->soft_stats.rx_fifo_errors += smb->rx_rxf_ov; 16858c2ecf20Sopenharmony_ci adapter->soft_stats.rx_length_errors += smb->rx_len_err; 16868c2ecf20Sopenharmony_ci adapter->soft_stats.rx_crc_errors += smb->rx_fcs_err; 16878c2ecf20Sopenharmony_ci adapter->soft_stats.rx_frame_errors += smb->rx_align_err; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci adapter->soft_stats.rx_pause += smb->rx_pause; 16908c2ecf20Sopenharmony_ci adapter->soft_stats.rx_rrd_ov += smb->rx_rrd_ov; 16918c2ecf20Sopenharmony_ci adapter->soft_stats.rx_trunc += smb->rx_sz_ov; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci /* Tx Errors */ 16948c2ecf20Sopenharmony_ci adapter->soft_stats.tx_errors += new_tx_errors; 16958c2ecf20Sopenharmony_ci adapter->soft_stats.tx_fifo_errors += smb->tx_underrun; 16968c2ecf20Sopenharmony_ci adapter->soft_stats.tx_aborted_errors += smb->tx_abort_col; 16978c2ecf20Sopenharmony_ci adapter->soft_stats.tx_window_errors += smb->tx_late_col; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci adapter->soft_stats.excecol += smb->tx_abort_col; 17008c2ecf20Sopenharmony_ci adapter->soft_stats.deffer += smb->tx_defer; 17018c2ecf20Sopenharmony_ci adapter->soft_stats.scc += smb->tx_1_col; 17028c2ecf20Sopenharmony_ci adapter->soft_stats.mcc += smb->tx_2_col; 17038c2ecf20Sopenharmony_ci adapter->soft_stats.latecol += smb->tx_late_col; 17048c2ecf20Sopenharmony_ci adapter->soft_stats.tx_underrun += smb->tx_underrun; 17058c2ecf20Sopenharmony_ci adapter->soft_stats.tx_trunc += smb->tx_trunc; 17068c2ecf20Sopenharmony_ci adapter->soft_stats.tx_pause += smb->tx_pause; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci netdev->stats.rx_bytes = adapter->soft_stats.rx_bytes; 17098c2ecf20Sopenharmony_ci netdev->stats.tx_bytes = adapter->soft_stats.tx_bytes; 17108c2ecf20Sopenharmony_ci netdev->stats.multicast = adapter->soft_stats.multicast; 17118c2ecf20Sopenharmony_ci netdev->stats.collisions = adapter->soft_stats.collisions; 17128c2ecf20Sopenharmony_ci netdev->stats.rx_errors = adapter->soft_stats.rx_errors; 17138c2ecf20Sopenharmony_ci netdev->stats.rx_length_errors = 17148c2ecf20Sopenharmony_ci adapter->soft_stats.rx_length_errors; 17158c2ecf20Sopenharmony_ci netdev->stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors; 17168c2ecf20Sopenharmony_ci netdev->stats.rx_frame_errors = 17178c2ecf20Sopenharmony_ci adapter->soft_stats.rx_frame_errors; 17188c2ecf20Sopenharmony_ci netdev->stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors; 17198c2ecf20Sopenharmony_ci netdev->stats.rx_dropped = adapter->soft_stats.rx_rrd_ov; 17208c2ecf20Sopenharmony_ci netdev->stats.tx_errors = adapter->soft_stats.tx_errors; 17218c2ecf20Sopenharmony_ci netdev->stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors; 17228c2ecf20Sopenharmony_ci netdev->stats.tx_aborted_errors = 17238c2ecf20Sopenharmony_ci adapter->soft_stats.tx_aborted_errors; 17248c2ecf20Sopenharmony_ci netdev->stats.tx_window_errors = 17258c2ecf20Sopenharmony_ci adapter->soft_stats.tx_window_errors; 17268c2ecf20Sopenharmony_ci netdev->stats.tx_carrier_errors = 17278c2ecf20Sopenharmony_ci adapter->soft_stats.tx_carrier_errors; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci netdev->stats.rx_packets = adapter->soft_stats.rx_packets; 17308c2ecf20Sopenharmony_ci netdev->stats.tx_packets = adapter->soft_stats.tx_packets; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic void atl1_update_mailbox(struct atl1_adapter *adapter) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci unsigned long flags; 17368c2ecf20Sopenharmony_ci u32 tpd_next_to_use; 17378c2ecf20Sopenharmony_ci u32 rfd_next_to_use; 17388c2ecf20Sopenharmony_ci u32 rrd_next_to_clean; 17398c2ecf20Sopenharmony_ci u32 value; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->mb_lock, flags); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use); 17448c2ecf20Sopenharmony_ci rfd_next_to_use = atomic_read(&adapter->rfd_ring.next_to_use); 17458c2ecf20Sopenharmony_ci rrd_next_to_clean = atomic_read(&adapter->rrd_ring.next_to_clean); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) << 17488c2ecf20Sopenharmony_ci MB_RFD_PROD_INDX_SHIFT) | 17498c2ecf20Sopenharmony_ci ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) << 17508c2ecf20Sopenharmony_ci MB_RRD_CONS_INDX_SHIFT) | 17518c2ecf20Sopenharmony_ci ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) << 17528c2ecf20Sopenharmony_ci MB_TPD_PROD_INDX_SHIFT); 17538c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->mb_lock, flags); 17568c2ecf20Sopenharmony_ci} 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_cistatic void atl1_clean_alloc_flag(struct atl1_adapter *adapter, 17598c2ecf20Sopenharmony_ci struct rx_return_desc *rrd, u16 offset) 17608c2ecf20Sopenharmony_ci{ 17618c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci while (rfd_ring->next_to_clean != (rrd->buf_indx + offset)) { 17648c2ecf20Sopenharmony_ci rfd_ring->buffer_info[rfd_ring->next_to_clean].alloced = 0; 17658c2ecf20Sopenharmony_ci if (++rfd_ring->next_to_clean == rfd_ring->count) { 17668c2ecf20Sopenharmony_ci rfd_ring->next_to_clean = 0; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci } 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_cistatic void atl1_update_rfd_index(struct atl1_adapter *adapter, 17728c2ecf20Sopenharmony_ci struct rx_return_desc *rrd) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci u16 num_buf; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci num_buf = (rrd->xsz.xsum_sz.pkt_size + adapter->rx_buffer_len - 1) / 17778c2ecf20Sopenharmony_ci adapter->rx_buffer_len; 17788c2ecf20Sopenharmony_ci if (rrd->num_buf == num_buf) 17798c2ecf20Sopenharmony_ci /* clean alloc flag for bad rrd */ 17808c2ecf20Sopenharmony_ci atl1_clean_alloc_flag(adapter, rrd, num_buf); 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_cistatic void atl1_rx_checksum(struct atl1_adapter *adapter, 17848c2ecf20Sopenharmony_ci struct rx_return_desc *rrd, struct sk_buff *skb) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci /* 17898c2ecf20Sopenharmony_ci * The L1 hardware contains a bug that erroneously sets the 17908c2ecf20Sopenharmony_ci * PACKET_FLAG_ERR and ERR_FLAG_L4_CHKSUM bits whenever a 17918c2ecf20Sopenharmony_ci * fragmented IP packet is received, even though the packet 17928c2ecf20Sopenharmony_ci * is perfectly valid and its checksum is correct. There's 17938c2ecf20Sopenharmony_ci * no way to distinguish between one of these good packets 17948c2ecf20Sopenharmony_ci * and a packet that actually contains a TCP/UDP checksum 17958c2ecf20Sopenharmony_ci * error, so all we can do is allow it to be handed up to 17968c2ecf20Sopenharmony_ci * the higher layers and let it be sorted out there. 17978c2ecf20Sopenharmony_ci */ 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) { 18028c2ecf20Sopenharmony_ci if (rrd->err_flg & (ERR_FLAG_CRC | ERR_FLAG_TRUNC | 18038c2ecf20Sopenharmony_ci ERR_FLAG_CODE | ERR_FLAG_OV)) { 18048c2ecf20Sopenharmony_ci adapter->hw_csum_err++; 18058c2ecf20Sopenharmony_ci if (netif_msg_rx_err(adapter)) 18068c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &pdev->dev, 18078c2ecf20Sopenharmony_ci "rx checksum error\n"); 18088c2ecf20Sopenharmony_ci return; 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* not IPv4 */ 18138c2ecf20Sopenharmony_ci if (!(rrd->pkt_flg & PACKET_FLAG_IPV4)) 18148c2ecf20Sopenharmony_ci /* checksum is invalid, but it's not an IPv4 pkt, so ok */ 18158c2ecf20Sopenharmony_ci return; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci /* IPv4 packet */ 18188c2ecf20Sopenharmony_ci if (likely(!(rrd->err_flg & 18198c2ecf20Sopenharmony_ci (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM)))) { 18208c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 18218c2ecf20Sopenharmony_ci adapter->hw_csum_good++; 18228c2ecf20Sopenharmony_ci return; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci/** 18278c2ecf20Sopenharmony_ci * atl1_alloc_rx_buffers - Replace used receive buffers 18288c2ecf20Sopenharmony_ci * @adapter: address of board private structure 18298c2ecf20Sopenharmony_ci */ 18308c2ecf20Sopenharmony_cistatic u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) 18318c2ecf20Sopenharmony_ci{ 18328c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 18338c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 18348c2ecf20Sopenharmony_ci struct page *page; 18358c2ecf20Sopenharmony_ci unsigned long offset; 18368c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info, *next_info; 18378c2ecf20Sopenharmony_ci struct sk_buff *skb; 18388c2ecf20Sopenharmony_ci u16 num_alloc = 0; 18398c2ecf20Sopenharmony_ci u16 rfd_next_to_use, next_next; 18408c2ecf20Sopenharmony_ci struct rx_free_desc *rfd_desc; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci next_next = rfd_next_to_use = atomic_read(&rfd_ring->next_to_use); 18438c2ecf20Sopenharmony_ci if (++next_next == rfd_ring->count) 18448c2ecf20Sopenharmony_ci next_next = 0; 18458c2ecf20Sopenharmony_ci buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; 18468c2ecf20Sopenharmony_ci next_info = &rfd_ring->buffer_info[next_next]; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci while (!buffer_info->alloced && !next_info->alloced) { 18498c2ecf20Sopenharmony_ci if (buffer_info->skb) { 18508c2ecf20Sopenharmony_ci buffer_info->alloced = 1; 18518c2ecf20Sopenharmony_ci goto next; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(adapter->netdev, 18578c2ecf20Sopenharmony_ci adapter->rx_buffer_len); 18588c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 18598c2ecf20Sopenharmony_ci /* Better luck next round */ 18608c2ecf20Sopenharmony_ci adapter->soft_stats.rx_dropped++; 18618c2ecf20Sopenharmony_ci break; 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci buffer_info->alloced = 1; 18658c2ecf20Sopenharmony_ci buffer_info->skb = skb; 18668c2ecf20Sopenharmony_ci buffer_info->length = (u16) adapter->rx_buffer_len; 18678c2ecf20Sopenharmony_ci page = virt_to_page(skb->data); 18688c2ecf20Sopenharmony_ci offset = offset_in_page(skb->data); 18698c2ecf20Sopenharmony_ci buffer_info->dma = dma_map_page(&pdev->dev, page, offset, 18708c2ecf20Sopenharmony_ci adapter->rx_buffer_len, 18718c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 18728c2ecf20Sopenharmony_ci rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); 18738c2ecf20Sopenharmony_ci rfd_desc->buf_len = cpu_to_le16(adapter->rx_buffer_len); 18748c2ecf20Sopenharmony_ci rfd_desc->coalese = 0; 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_cinext: 18778c2ecf20Sopenharmony_ci rfd_next_to_use = next_next; 18788c2ecf20Sopenharmony_ci if (unlikely(++next_next == rfd_ring->count)) 18798c2ecf20Sopenharmony_ci next_next = 0; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; 18828c2ecf20Sopenharmony_ci next_info = &rfd_ring->buffer_info[next_next]; 18838c2ecf20Sopenharmony_ci num_alloc++; 18848c2ecf20Sopenharmony_ci } 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci if (num_alloc) { 18878c2ecf20Sopenharmony_ci /* 18888c2ecf20Sopenharmony_ci * Force memory writes to complete before letting h/w 18898c2ecf20Sopenharmony_ci * know there are new descriptors to fetch. (Only 18908c2ecf20Sopenharmony_ci * applicable for weak-ordered memory model archs, 18918c2ecf20Sopenharmony_ci * such as IA-64). 18928c2ecf20Sopenharmony_ci */ 18938c2ecf20Sopenharmony_ci wmb(); 18948c2ecf20Sopenharmony_ci atomic_set(&rfd_ring->next_to_use, (int)rfd_next_to_use); 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci return num_alloc; 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_cistatic int atl1_intr_rx(struct atl1_adapter *adapter, int budget) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci int i, count; 19028c2ecf20Sopenharmony_ci u16 length; 19038c2ecf20Sopenharmony_ci u16 rrd_next_to_clean; 19048c2ecf20Sopenharmony_ci u32 value; 19058c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; 19068c2ecf20Sopenharmony_ci struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; 19078c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info; 19088c2ecf20Sopenharmony_ci struct rx_return_desc *rrd; 19098c2ecf20Sopenharmony_ci struct sk_buff *skb; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci count = 0; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci rrd_next_to_clean = atomic_read(&rrd_ring->next_to_clean); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci while (count < budget) { 19168c2ecf20Sopenharmony_ci rrd = ATL1_RRD_DESC(rrd_ring, rrd_next_to_clean); 19178c2ecf20Sopenharmony_ci i = 1; 19188c2ecf20Sopenharmony_ci if (likely(rrd->xsz.valid)) { /* packet valid */ 19198c2ecf20Sopenharmony_cichk_rrd: 19208c2ecf20Sopenharmony_ci /* check rrd status */ 19218c2ecf20Sopenharmony_ci if (likely(rrd->num_buf == 1)) 19228c2ecf20Sopenharmony_ci goto rrd_ok; 19238c2ecf20Sopenharmony_ci else if (netif_msg_rx_err(adapter)) { 19248c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19258c2ecf20Sopenharmony_ci "unexpected RRD buffer count\n"); 19268c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19278c2ecf20Sopenharmony_ci "rx_buf_len = %d\n", 19288c2ecf20Sopenharmony_ci adapter->rx_buffer_len); 19298c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19308c2ecf20Sopenharmony_ci "RRD num_buf = %d\n", 19318c2ecf20Sopenharmony_ci rrd->num_buf); 19328c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19338c2ecf20Sopenharmony_ci "RRD pkt_len = %d\n", 19348c2ecf20Sopenharmony_ci rrd->xsz.xsum_sz.pkt_size); 19358c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19368c2ecf20Sopenharmony_ci "RRD pkt_flg = 0x%08X\n", 19378c2ecf20Sopenharmony_ci rrd->pkt_flg); 19388c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19398c2ecf20Sopenharmony_ci "RRD err_flg = 0x%08X\n", 19408c2ecf20Sopenharmony_ci rrd->err_flg); 19418c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19428c2ecf20Sopenharmony_ci "RRD vlan_tag = 0x%08X\n", 19438c2ecf20Sopenharmony_ci rrd->vlan_tag); 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* rrd seems to be bad */ 19478c2ecf20Sopenharmony_ci if (unlikely(i-- > 0)) { 19488c2ecf20Sopenharmony_ci /* rrd may not be DMAed completely */ 19498c2ecf20Sopenharmony_ci udelay(1); 19508c2ecf20Sopenharmony_ci goto chk_rrd; 19518c2ecf20Sopenharmony_ci } 19528c2ecf20Sopenharmony_ci /* bad rrd */ 19538c2ecf20Sopenharmony_ci if (netif_msg_rx_err(adapter)) 19548c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 19558c2ecf20Sopenharmony_ci "bad RRD\n"); 19568c2ecf20Sopenharmony_ci /* see if update RFD index */ 19578c2ecf20Sopenharmony_ci if (rrd->num_buf > 1) 19588c2ecf20Sopenharmony_ci atl1_update_rfd_index(adapter, rrd); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* update rrd */ 19618c2ecf20Sopenharmony_ci rrd->xsz.valid = 0; 19628c2ecf20Sopenharmony_ci if (++rrd_next_to_clean == rrd_ring->count) 19638c2ecf20Sopenharmony_ci rrd_next_to_clean = 0; 19648c2ecf20Sopenharmony_ci count++; 19658c2ecf20Sopenharmony_ci continue; 19668c2ecf20Sopenharmony_ci } else { /* current rrd still not be updated */ 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci break; 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_cirrd_ok: 19718c2ecf20Sopenharmony_ci /* clean alloc flag for bad rrd */ 19728c2ecf20Sopenharmony_ci atl1_clean_alloc_flag(adapter, rrd, 0); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci buffer_info = &rfd_ring->buffer_info[rrd->buf_indx]; 19758c2ecf20Sopenharmony_ci if (++rfd_ring->next_to_clean == rfd_ring->count) 19768c2ecf20Sopenharmony_ci rfd_ring->next_to_clean = 0; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* update rrd next to clean */ 19798c2ecf20Sopenharmony_ci if (++rrd_next_to_clean == rrd_ring->count) 19808c2ecf20Sopenharmony_ci rrd_next_to_clean = 0; 19818c2ecf20Sopenharmony_ci count++; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) { 19848c2ecf20Sopenharmony_ci if (!(rrd->err_flg & 19858c2ecf20Sopenharmony_ci (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM 19868c2ecf20Sopenharmony_ci | ERR_FLAG_LEN))) { 19878c2ecf20Sopenharmony_ci /* packet error, don't need upstream */ 19888c2ecf20Sopenharmony_ci buffer_info->alloced = 0; 19898c2ecf20Sopenharmony_ci rrd->xsz.valid = 0; 19908c2ecf20Sopenharmony_ci continue; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci /* Good Receive */ 19958c2ecf20Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, buffer_info->dma, 19968c2ecf20Sopenharmony_ci buffer_info->length, DMA_FROM_DEVICE); 19978c2ecf20Sopenharmony_ci buffer_info->dma = 0; 19988c2ecf20Sopenharmony_ci skb = buffer_info->skb; 19998c2ecf20Sopenharmony_ci length = le16_to_cpu(rrd->xsz.xsum_sz.pkt_size); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci skb_put(skb, length - ETH_FCS_LEN); 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* Receive Checksum Offload */ 20048c2ecf20Sopenharmony_ci atl1_rx_checksum(adapter, rrd, skb); 20058c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, adapter->netdev); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (rrd->pkt_flg & PACKET_FLAG_VLAN_INS) { 20088c2ecf20Sopenharmony_ci u16 vlan_tag = (rrd->vlan_tag >> 4) | 20098c2ecf20Sopenharmony_ci ((rrd->vlan_tag & 7) << 13) | 20108c2ecf20Sopenharmony_ci ((rrd->vlan_tag & 8) << 9); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); 20138c2ecf20Sopenharmony_ci } 20148c2ecf20Sopenharmony_ci netif_receive_skb(skb); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* let protocol layer free skb */ 20178c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 20188c2ecf20Sopenharmony_ci buffer_info->alloced = 0; 20198c2ecf20Sopenharmony_ci rrd->xsz.valid = 0; 20208c2ecf20Sopenharmony_ci } 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci atomic_set(&rrd_ring->next_to_clean, rrd_next_to_clean); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci atl1_alloc_rx_buffers(adapter); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci /* update mailbox ? */ 20278c2ecf20Sopenharmony_ci if (count) { 20288c2ecf20Sopenharmony_ci u32 tpd_next_to_use; 20298c2ecf20Sopenharmony_ci u32 rfd_next_to_use; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci spin_lock(&adapter->mb_lock); 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use); 20348c2ecf20Sopenharmony_ci rfd_next_to_use = 20358c2ecf20Sopenharmony_ci atomic_read(&adapter->rfd_ring.next_to_use); 20368c2ecf20Sopenharmony_ci rrd_next_to_clean = 20378c2ecf20Sopenharmony_ci atomic_read(&adapter->rrd_ring.next_to_clean); 20388c2ecf20Sopenharmony_ci value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) << 20398c2ecf20Sopenharmony_ci MB_RFD_PROD_INDX_SHIFT) | 20408c2ecf20Sopenharmony_ci ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) << 20418c2ecf20Sopenharmony_ci MB_RRD_CONS_INDX_SHIFT) | 20428c2ecf20Sopenharmony_ci ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) << 20438c2ecf20Sopenharmony_ci MB_TPD_PROD_INDX_SHIFT); 20448c2ecf20Sopenharmony_ci iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX); 20458c2ecf20Sopenharmony_ci spin_unlock(&adapter->mb_lock); 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci return count; 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_cistatic int atl1_intr_tx(struct atl1_adapter *adapter) 20528c2ecf20Sopenharmony_ci{ 20538c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 20548c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info; 20558c2ecf20Sopenharmony_ci u16 sw_tpd_next_to_clean; 20568c2ecf20Sopenharmony_ci u16 cmb_tpd_next_to_clean; 20578c2ecf20Sopenharmony_ci int count = 0; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean); 20608c2ecf20Sopenharmony_ci cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) { 20638c2ecf20Sopenharmony_ci buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean]; 20648c2ecf20Sopenharmony_ci if (buffer_info->dma) { 20658c2ecf20Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, buffer_info->dma, 20668c2ecf20Sopenharmony_ci buffer_info->length, DMA_TO_DEVICE); 20678c2ecf20Sopenharmony_ci buffer_info->dma = 0; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci if (buffer_info->skb) { 20718c2ecf20Sopenharmony_ci dev_consume_skb_irq(buffer_info->skb); 20728c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 20738c2ecf20Sopenharmony_ci } 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci if (++sw_tpd_next_to_clean == tpd_ring->count) 20768c2ecf20Sopenharmony_ci sw_tpd_next_to_clean = 0; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci count++; 20798c2ecf20Sopenharmony_ci } 20808c2ecf20Sopenharmony_ci atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci if (netif_queue_stopped(adapter->netdev) && 20838c2ecf20Sopenharmony_ci netif_carrier_ok(adapter->netdev)) 20848c2ecf20Sopenharmony_ci netif_wake_queue(adapter->netdev); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci return count; 20878c2ecf20Sopenharmony_ci} 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_cistatic u16 atl1_tpd_avail(struct atl1_tpd_ring *tpd_ring) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); 20928c2ecf20Sopenharmony_ci u16 next_to_use = atomic_read(&tpd_ring->next_to_use); 20938c2ecf20Sopenharmony_ci return (next_to_clean > next_to_use) ? 20948c2ecf20Sopenharmony_ci next_to_clean - next_to_use - 1 : 20958c2ecf20Sopenharmony_ci tpd_ring->count + next_to_clean - next_to_use - 1; 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_cistatic int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb, 20998c2ecf20Sopenharmony_ci struct tx_packet_desc *ptpd) 21008c2ecf20Sopenharmony_ci{ 21018c2ecf20Sopenharmony_ci u8 hdr_len, ip_off; 21028c2ecf20Sopenharmony_ci u32 real_len; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->gso_size) { 21058c2ecf20Sopenharmony_ci int err; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci err = skb_cow_head(skb, 0); 21088c2ecf20Sopenharmony_ci if (err < 0) 21098c2ecf20Sopenharmony_ci return err; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 21128c2ecf20Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci real_len = (((unsigned char *)iph - skb->data) + 21158c2ecf20Sopenharmony_ci ntohs(iph->tot_len)); 21168c2ecf20Sopenharmony_ci if (real_len < skb->len) 21178c2ecf20Sopenharmony_ci pskb_trim(skb, real_len); 21188c2ecf20Sopenharmony_ci hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); 21198c2ecf20Sopenharmony_ci if (skb->len == hdr_len) { 21208c2ecf20Sopenharmony_ci iph->check = 0; 21218c2ecf20Sopenharmony_ci tcp_hdr(skb)->check = 21228c2ecf20Sopenharmony_ci ~csum_tcpudp_magic(iph->saddr, 21238c2ecf20Sopenharmony_ci iph->daddr, tcp_hdrlen(skb), 21248c2ecf20Sopenharmony_ci IPPROTO_TCP, 0); 21258c2ecf20Sopenharmony_ci ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) << 21268c2ecf20Sopenharmony_ci TPD_IPHL_SHIFT; 21278c2ecf20Sopenharmony_ci ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) & 21288c2ecf20Sopenharmony_ci TPD_TCPHDRLEN_MASK) << 21298c2ecf20Sopenharmony_ci TPD_TCPHDRLEN_SHIFT; 21308c2ecf20Sopenharmony_ci ptpd->word3 |= 1 << TPD_IP_CSUM_SHIFT; 21318c2ecf20Sopenharmony_ci ptpd->word3 |= 1 << TPD_TCP_CSUM_SHIFT; 21328c2ecf20Sopenharmony_ci return 1; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci iph->check = 0; 21368c2ecf20Sopenharmony_ci tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, 21378c2ecf20Sopenharmony_ci iph->daddr, 0, IPPROTO_TCP, 0); 21388c2ecf20Sopenharmony_ci ip_off = (unsigned char *)iph - 21398c2ecf20Sopenharmony_ci (unsigned char *) skb_network_header(skb); 21408c2ecf20Sopenharmony_ci if (ip_off == 8) /* 802.3-SNAP frame */ 21418c2ecf20Sopenharmony_ci ptpd->word3 |= 1 << TPD_ETHTYPE_SHIFT; 21428c2ecf20Sopenharmony_ci else if (ip_off != 0) 21438c2ecf20Sopenharmony_ci return -2; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) << 21468c2ecf20Sopenharmony_ci TPD_IPHL_SHIFT; 21478c2ecf20Sopenharmony_ci ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) & 21488c2ecf20Sopenharmony_ci TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT; 21498c2ecf20Sopenharmony_ci ptpd->word3 |= (skb_shinfo(skb)->gso_size & 21508c2ecf20Sopenharmony_ci TPD_MSS_MASK) << TPD_MSS_SHIFT; 21518c2ecf20Sopenharmony_ci ptpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; 21528c2ecf20Sopenharmony_ci return 3; 21538c2ecf20Sopenharmony_ci } 21548c2ecf20Sopenharmony_ci } 21558c2ecf20Sopenharmony_ci return 0; 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb, 21598c2ecf20Sopenharmony_ci struct tx_packet_desc *ptpd) 21608c2ecf20Sopenharmony_ci{ 21618c2ecf20Sopenharmony_ci u8 css, cso; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { 21648c2ecf20Sopenharmony_ci css = skb_checksum_start_offset(skb); 21658c2ecf20Sopenharmony_ci cso = css + (u8) skb->csum_offset; 21668c2ecf20Sopenharmony_ci if (unlikely(css & 0x1)) { 21678c2ecf20Sopenharmony_ci /* L1 hardware requires an even number here */ 21688c2ecf20Sopenharmony_ci if (netif_msg_tx_err(adapter)) 21698c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 21708c2ecf20Sopenharmony_ci "payload offset not an even number\n"); 21718c2ecf20Sopenharmony_ci return -1; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci ptpd->word3 |= (css & TPD_PLOADOFFSET_MASK) << 21748c2ecf20Sopenharmony_ci TPD_PLOADOFFSET_SHIFT; 21758c2ecf20Sopenharmony_ci ptpd->word3 |= (cso & TPD_CCSUMOFFSET_MASK) << 21768c2ecf20Sopenharmony_ci TPD_CCSUMOFFSET_SHIFT; 21778c2ecf20Sopenharmony_ci ptpd->word3 |= 1 << TPD_CUST_CSUM_EN_SHIFT; 21788c2ecf20Sopenharmony_ci return true; 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci return 0; 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cistatic void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, 21848c2ecf20Sopenharmony_ci struct tx_packet_desc *ptpd) 21858c2ecf20Sopenharmony_ci{ 21868c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 21878c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info; 21888c2ecf20Sopenharmony_ci u16 buf_len = skb->len; 21898c2ecf20Sopenharmony_ci struct page *page; 21908c2ecf20Sopenharmony_ci unsigned long offset; 21918c2ecf20Sopenharmony_ci unsigned int nr_frags; 21928c2ecf20Sopenharmony_ci unsigned int f; 21938c2ecf20Sopenharmony_ci int retval; 21948c2ecf20Sopenharmony_ci u16 next_to_use; 21958c2ecf20Sopenharmony_ci u16 data_len; 21968c2ecf20Sopenharmony_ci u8 hdr_len; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci buf_len -= skb->data_len; 21998c2ecf20Sopenharmony_ci nr_frags = skb_shinfo(skb)->nr_frags; 22008c2ecf20Sopenharmony_ci next_to_use = atomic_read(&tpd_ring->next_to_use); 22018c2ecf20Sopenharmony_ci buffer_info = &tpd_ring->buffer_info[next_to_use]; 22028c2ecf20Sopenharmony_ci BUG_ON(buffer_info->skb); 22038c2ecf20Sopenharmony_ci /* put skb in last TPD */ 22048c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci retval = (ptpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK; 22078c2ecf20Sopenharmony_ci if (retval) { 22088c2ecf20Sopenharmony_ci /* TSO */ 22098c2ecf20Sopenharmony_ci hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 22108c2ecf20Sopenharmony_ci buffer_info->length = hdr_len; 22118c2ecf20Sopenharmony_ci page = virt_to_page(skb->data); 22128c2ecf20Sopenharmony_ci offset = offset_in_page(skb->data); 22138c2ecf20Sopenharmony_ci buffer_info->dma = dma_map_page(&adapter->pdev->dev, page, 22148c2ecf20Sopenharmony_ci offset, hdr_len, 22158c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (++next_to_use == tpd_ring->count) 22188c2ecf20Sopenharmony_ci next_to_use = 0; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci if (buf_len > hdr_len) { 22218c2ecf20Sopenharmony_ci int i, nseg; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci data_len = buf_len - hdr_len; 22248c2ecf20Sopenharmony_ci nseg = (data_len + ATL1_MAX_TX_BUF_LEN - 1) / 22258c2ecf20Sopenharmony_ci ATL1_MAX_TX_BUF_LEN; 22268c2ecf20Sopenharmony_ci for (i = 0; i < nseg; i++) { 22278c2ecf20Sopenharmony_ci buffer_info = 22288c2ecf20Sopenharmony_ci &tpd_ring->buffer_info[next_to_use]; 22298c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 22308c2ecf20Sopenharmony_ci buffer_info->length = 22318c2ecf20Sopenharmony_ci (ATL1_MAX_TX_BUF_LEN >= 22328c2ecf20Sopenharmony_ci data_len) ? ATL1_MAX_TX_BUF_LEN : data_len; 22338c2ecf20Sopenharmony_ci data_len -= buffer_info->length; 22348c2ecf20Sopenharmony_ci page = virt_to_page(skb->data + 22358c2ecf20Sopenharmony_ci (hdr_len + i * ATL1_MAX_TX_BUF_LEN)); 22368c2ecf20Sopenharmony_ci offset = offset_in_page(skb->data + 22378c2ecf20Sopenharmony_ci (hdr_len + i * ATL1_MAX_TX_BUF_LEN)); 22388c2ecf20Sopenharmony_ci buffer_info->dma = dma_map_page(&adapter->pdev->dev, 22398c2ecf20Sopenharmony_ci page, offset, 22408c2ecf20Sopenharmony_ci buffer_info->length, 22418c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 22428c2ecf20Sopenharmony_ci if (++next_to_use == tpd_ring->count) 22438c2ecf20Sopenharmony_ci next_to_use = 0; 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci } else { 22478c2ecf20Sopenharmony_ci /* not TSO */ 22488c2ecf20Sopenharmony_ci buffer_info->length = buf_len; 22498c2ecf20Sopenharmony_ci page = virt_to_page(skb->data); 22508c2ecf20Sopenharmony_ci offset = offset_in_page(skb->data); 22518c2ecf20Sopenharmony_ci buffer_info->dma = dma_map_page(&adapter->pdev->dev, page, 22528c2ecf20Sopenharmony_ci offset, buf_len, 22538c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 22548c2ecf20Sopenharmony_ci if (++next_to_use == tpd_ring->count) 22558c2ecf20Sopenharmony_ci next_to_use = 0; 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci for (f = 0; f < nr_frags; f++) { 22598c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 22608c2ecf20Sopenharmony_ci u16 i, nseg; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci buf_len = skb_frag_size(frag); 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci nseg = (buf_len + ATL1_MAX_TX_BUF_LEN - 1) / 22658c2ecf20Sopenharmony_ci ATL1_MAX_TX_BUF_LEN; 22668c2ecf20Sopenharmony_ci for (i = 0; i < nseg; i++) { 22678c2ecf20Sopenharmony_ci buffer_info = &tpd_ring->buffer_info[next_to_use]; 22688c2ecf20Sopenharmony_ci BUG_ON(buffer_info->skb); 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci buffer_info->skb = NULL; 22718c2ecf20Sopenharmony_ci buffer_info->length = (buf_len > ATL1_MAX_TX_BUF_LEN) ? 22728c2ecf20Sopenharmony_ci ATL1_MAX_TX_BUF_LEN : buf_len; 22738c2ecf20Sopenharmony_ci buf_len -= buffer_info->length; 22748c2ecf20Sopenharmony_ci buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev, 22758c2ecf20Sopenharmony_ci frag, i * ATL1_MAX_TX_BUF_LEN, 22768c2ecf20Sopenharmony_ci buffer_info->length, DMA_TO_DEVICE); 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci if (++next_to_use == tpd_ring->count) 22798c2ecf20Sopenharmony_ci next_to_use = 0; 22808c2ecf20Sopenharmony_ci } 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci /* last tpd's buffer-info */ 22848c2ecf20Sopenharmony_ci buffer_info->skb = skb; 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic void atl1_tx_queue(struct atl1_adapter *adapter, u16 count, 22888c2ecf20Sopenharmony_ci struct tx_packet_desc *ptpd) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 22918c2ecf20Sopenharmony_ci struct atl1_buffer *buffer_info; 22928c2ecf20Sopenharmony_ci struct tx_packet_desc *tpd; 22938c2ecf20Sopenharmony_ci u16 j; 22948c2ecf20Sopenharmony_ci u32 val; 22958c2ecf20Sopenharmony_ci u16 next_to_use = (u16) atomic_read(&tpd_ring->next_to_use); 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci for (j = 0; j < count; j++) { 22988c2ecf20Sopenharmony_ci buffer_info = &tpd_ring->buffer_info[next_to_use]; 22998c2ecf20Sopenharmony_ci tpd = ATL1_TPD_DESC(&adapter->tpd_ring, next_to_use); 23008c2ecf20Sopenharmony_ci if (tpd != ptpd) 23018c2ecf20Sopenharmony_ci memcpy(tpd, ptpd, sizeof(struct tx_packet_desc)); 23028c2ecf20Sopenharmony_ci tpd->buffer_addr = cpu_to_le64(buffer_info->dma); 23038c2ecf20Sopenharmony_ci tpd->word2 &= ~(TPD_BUFLEN_MASK << TPD_BUFLEN_SHIFT); 23048c2ecf20Sopenharmony_ci tpd->word2 |= (cpu_to_le16(buffer_info->length) & 23058c2ecf20Sopenharmony_ci TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci /* 23088c2ecf20Sopenharmony_ci * if this is the first packet in a TSO chain, set 23098c2ecf20Sopenharmony_ci * TPD_HDRFLAG, otherwise, clear it. 23108c2ecf20Sopenharmony_ci */ 23118c2ecf20Sopenharmony_ci val = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & 23128c2ecf20Sopenharmony_ci TPD_SEGMENT_EN_MASK; 23138c2ecf20Sopenharmony_ci if (val) { 23148c2ecf20Sopenharmony_ci if (!j) 23158c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT; 23168c2ecf20Sopenharmony_ci else 23178c2ecf20Sopenharmony_ci tpd->word3 &= ~(1 << TPD_HDRFLAG_SHIFT); 23188c2ecf20Sopenharmony_ci } 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci if (j == (count - 1)) 23218c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_EOP_SHIFT; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci if (++next_to_use == tpd_ring->count) 23248c2ecf20Sopenharmony_ci next_to_use = 0; 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci /* 23278c2ecf20Sopenharmony_ci * Force memory writes to complete before letting h/w 23288c2ecf20Sopenharmony_ci * know there are new descriptors to fetch. (Only 23298c2ecf20Sopenharmony_ci * applicable for weak-ordered memory model archs, 23308c2ecf20Sopenharmony_ci * such as IA-64). 23318c2ecf20Sopenharmony_ci */ 23328c2ecf20Sopenharmony_ci wmb(); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci atomic_set(&tpd_ring->next_to_use, next_to_use); 23358c2ecf20Sopenharmony_ci} 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_cistatic netdev_tx_t atl1_xmit_frame(struct sk_buff *skb, 23388c2ecf20Sopenharmony_ci struct net_device *netdev) 23398c2ecf20Sopenharmony_ci{ 23408c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 23418c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; 23428c2ecf20Sopenharmony_ci int len; 23438c2ecf20Sopenharmony_ci int tso; 23448c2ecf20Sopenharmony_ci int count = 1; 23458c2ecf20Sopenharmony_ci int ret_val; 23468c2ecf20Sopenharmony_ci struct tx_packet_desc *ptpd; 23478c2ecf20Sopenharmony_ci u16 vlan_tag; 23488c2ecf20Sopenharmony_ci unsigned int nr_frags = 0; 23498c2ecf20Sopenharmony_ci unsigned int mss = 0; 23508c2ecf20Sopenharmony_ci unsigned int f; 23518c2ecf20Sopenharmony_ci unsigned int proto_hdr_len; 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci len = skb_headlen(skb); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (unlikely(skb->len <= 0)) { 23568c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 23578c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci nr_frags = skb_shinfo(skb)->nr_frags; 23618c2ecf20Sopenharmony_ci for (f = 0; f < nr_frags; f++) { 23628c2ecf20Sopenharmony_ci unsigned int f_size = skb_frag_size(&skb_shinfo(skb)->frags[f]); 23638c2ecf20Sopenharmony_ci count += (f_size + ATL1_MAX_TX_BUF_LEN - 1) / 23648c2ecf20Sopenharmony_ci ATL1_MAX_TX_BUF_LEN; 23658c2ecf20Sopenharmony_ci } 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci mss = skb_shinfo(skb)->gso_size; 23688c2ecf20Sopenharmony_ci if (mss) { 23698c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 23708c2ecf20Sopenharmony_ci proto_hdr_len = (skb_transport_offset(skb) + 23718c2ecf20Sopenharmony_ci tcp_hdrlen(skb)); 23728c2ecf20Sopenharmony_ci if (unlikely(proto_hdr_len > len)) { 23738c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 23748c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 23758c2ecf20Sopenharmony_ci } 23768c2ecf20Sopenharmony_ci /* need additional TPD ? */ 23778c2ecf20Sopenharmony_ci if (proto_hdr_len != len) 23788c2ecf20Sopenharmony_ci count += (len - proto_hdr_len + 23798c2ecf20Sopenharmony_ci ATL1_MAX_TX_BUF_LEN - 1) / 23808c2ecf20Sopenharmony_ci ATL1_MAX_TX_BUF_LEN; 23818c2ecf20Sopenharmony_ci } 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci if (atl1_tpd_avail(&adapter->tpd_ring) < count) { 23858c2ecf20Sopenharmony_ci /* not enough descriptors */ 23868c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 23878c2ecf20Sopenharmony_ci if (netif_msg_tx_queued(adapter)) 23888c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 23898c2ecf20Sopenharmony_ci "tx busy\n"); 23908c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci ptpd = ATL1_TPD_DESC(tpd_ring, 23948c2ecf20Sopenharmony_ci (u16) atomic_read(&tpd_ring->next_to_use)); 23958c2ecf20Sopenharmony_ci memset(ptpd, 0, sizeof(struct tx_packet_desc)); 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 23988c2ecf20Sopenharmony_ci vlan_tag = skb_vlan_tag_get(skb); 23998c2ecf20Sopenharmony_ci vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) | 24008c2ecf20Sopenharmony_ci ((vlan_tag >> 9) & 0x8); 24018c2ecf20Sopenharmony_ci ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; 24028c2ecf20Sopenharmony_ci ptpd->word2 |= (vlan_tag & TPD_VLANTAG_MASK) << 24038c2ecf20Sopenharmony_ci TPD_VLANTAG_SHIFT; 24048c2ecf20Sopenharmony_ci } 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci tso = atl1_tso(adapter, skb, ptpd); 24078c2ecf20Sopenharmony_ci if (tso < 0) { 24088c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 24098c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 24108c2ecf20Sopenharmony_ci } 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci if (!tso) { 24138c2ecf20Sopenharmony_ci ret_val = atl1_tx_csum(adapter, skb, ptpd); 24148c2ecf20Sopenharmony_ci if (ret_val < 0) { 24158c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 24168c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 24178c2ecf20Sopenharmony_ci } 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci atl1_tx_map(adapter, skb, ptpd); 24218c2ecf20Sopenharmony_ci atl1_tx_queue(adapter, count, ptpd); 24228c2ecf20Sopenharmony_ci atl1_update_mailbox(adapter); 24238c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 24248c2ecf20Sopenharmony_ci} 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_cistatic int atl1_rings_clean(struct napi_struct *napi, int budget) 24278c2ecf20Sopenharmony_ci{ 24288c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = container_of(napi, struct atl1_adapter, napi); 24298c2ecf20Sopenharmony_ci int work_done = atl1_intr_rx(adapter, budget); 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci if (atl1_intr_tx(adapter)) 24328c2ecf20Sopenharmony_ci work_done = budget; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci /* Let's come again to process some more packets */ 24358c2ecf20Sopenharmony_ci if (work_done >= budget) 24368c2ecf20Sopenharmony_ci return work_done; 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 24398c2ecf20Sopenharmony_ci /* re-enable Interrupt */ 24408c2ecf20Sopenharmony_ci if (likely(adapter->int_enabled)) 24418c2ecf20Sopenharmony_ci atlx_imr_set(adapter, IMR_NORMAL_MASK); 24428c2ecf20Sopenharmony_ci return work_done; 24438c2ecf20Sopenharmony_ci} 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_cistatic inline int atl1_sched_rings_clean(struct atl1_adapter* adapter) 24468c2ecf20Sopenharmony_ci{ 24478c2ecf20Sopenharmony_ci if (!napi_schedule_prep(&adapter->napi)) 24488c2ecf20Sopenharmony_ci /* It is possible in case even the RX/TX ints are disabled via IMR 24498c2ecf20Sopenharmony_ci * register the ISR bits are set anyway (but do not produce IRQ). 24508c2ecf20Sopenharmony_ci * To handle such situation the napi functions used to check is 24518c2ecf20Sopenharmony_ci * something scheduled or not. 24528c2ecf20Sopenharmony_ci */ 24538c2ecf20Sopenharmony_ci return 0; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci __napi_schedule(&adapter->napi); 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci /* 24588c2ecf20Sopenharmony_ci * Disable RX/TX ints via IMR register if it is 24598c2ecf20Sopenharmony_ci * allowed. NAPI handler must reenable them in same 24608c2ecf20Sopenharmony_ci * way. 24618c2ecf20Sopenharmony_ci */ 24628c2ecf20Sopenharmony_ci if (!adapter->int_enabled) 24638c2ecf20Sopenharmony_ci return 1; 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci atlx_imr_set(adapter, IMR_NORXTX_MASK); 24668c2ecf20Sopenharmony_ci return 1; 24678c2ecf20Sopenharmony_ci} 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci/** 24708c2ecf20Sopenharmony_ci * atl1_intr - Interrupt Handler 24718c2ecf20Sopenharmony_ci * @irq: interrupt number 24728c2ecf20Sopenharmony_ci * @data: pointer to a network interface device structure 24738c2ecf20Sopenharmony_ci */ 24748c2ecf20Sopenharmony_cistatic irqreturn_t atl1_intr(int irq, void *data) 24758c2ecf20Sopenharmony_ci{ 24768c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(data); 24778c2ecf20Sopenharmony_ci u32 status; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci status = adapter->cmb.cmb->int_stats; 24808c2ecf20Sopenharmony_ci if (!status) 24818c2ecf20Sopenharmony_ci return IRQ_NONE; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci /* clear CMB interrupt status at once, 24848c2ecf20Sopenharmony_ci * but leave rx/tx interrupt status in case it should be dropped 24858c2ecf20Sopenharmony_ci * only if rx/tx processing queued. In other case interrupt 24868c2ecf20Sopenharmony_ci * can be lost. 24878c2ecf20Sopenharmony_ci */ 24888c2ecf20Sopenharmony_ci adapter->cmb.cmb->int_stats = status & (ISR_CMB_TX | ISR_CMB_RX); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci if (status & ISR_GPHY) /* clear phy status */ 24918c2ecf20Sopenharmony_ci atlx_clear_phy_int(adapter); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci /* clear ISR status, and Enable CMB DMA/Disable Interrupt */ 24948c2ecf20Sopenharmony_ci iowrite32(status | ISR_DIS_INT, adapter->hw.hw_addr + REG_ISR); 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci /* check if SMB intr */ 24978c2ecf20Sopenharmony_ci if (status & ISR_SMB) 24988c2ecf20Sopenharmony_ci atl1_inc_smb(adapter); 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci /* check if PCIE PHY Link down */ 25018c2ecf20Sopenharmony_ci if (status & ISR_PHY_LINKDOWN) { 25028c2ecf20Sopenharmony_ci if (netif_msg_intr(adapter)) 25038c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 25048c2ecf20Sopenharmony_ci "pcie phy link down %x\n", status); 25058c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { /* reset MAC */ 25068c2ecf20Sopenharmony_ci atlx_irq_disable(adapter); 25078c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_dev_task); 25088c2ecf20Sopenharmony_ci return IRQ_HANDLED; 25098c2ecf20Sopenharmony_ci } 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci /* check if DMA read/write error ? */ 25138c2ecf20Sopenharmony_ci if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { 25148c2ecf20Sopenharmony_ci if (netif_msg_intr(adapter)) 25158c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &adapter->pdev->dev, 25168c2ecf20Sopenharmony_ci "pcie DMA r/w error (status = 0x%x)\n", 25178c2ecf20Sopenharmony_ci status); 25188c2ecf20Sopenharmony_ci atlx_irq_disable(adapter); 25198c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_dev_task); 25208c2ecf20Sopenharmony_ci return IRQ_HANDLED; 25218c2ecf20Sopenharmony_ci } 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci /* link event */ 25248c2ecf20Sopenharmony_ci if (status & ISR_GPHY) { 25258c2ecf20Sopenharmony_ci adapter->soft_stats.tx_carrier_errors++; 25268c2ecf20Sopenharmony_ci atl1_check_for_link(adapter); 25278c2ecf20Sopenharmony_ci } 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci /* transmit or receive event */ 25308c2ecf20Sopenharmony_ci if (status & (ISR_CMB_TX | ISR_CMB_RX) && 25318c2ecf20Sopenharmony_ci atl1_sched_rings_clean(adapter)) 25328c2ecf20Sopenharmony_ci adapter->cmb.cmb->int_stats = adapter->cmb.cmb->int_stats & 25338c2ecf20Sopenharmony_ci ~(ISR_CMB_TX | ISR_CMB_RX); 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci /* rx exception */ 25368c2ecf20Sopenharmony_ci if (unlikely(status & (ISR_RXF_OV | ISR_RFD_UNRUN | 25378c2ecf20Sopenharmony_ci ISR_RRD_OV | ISR_HOST_RFD_UNRUN | 25388c2ecf20Sopenharmony_ci ISR_HOST_RRD_OV))) { 25398c2ecf20Sopenharmony_ci if (netif_msg_intr(adapter)) 25408c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, 25418c2ecf20Sopenharmony_ci &adapter->pdev->dev, 25428c2ecf20Sopenharmony_ci "rx exception, ISR = 0x%x\n", 25438c2ecf20Sopenharmony_ci status); 25448c2ecf20Sopenharmony_ci atl1_sched_rings_clean(adapter); 25458c2ecf20Sopenharmony_ci } 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci /* re-enable Interrupt */ 25488c2ecf20Sopenharmony_ci iowrite32(ISR_DIS_SMB | ISR_DIS_DMA, adapter->hw.hw_addr + REG_ISR); 25498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 25508c2ecf20Sopenharmony_ci} 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci/** 25548c2ecf20Sopenharmony_ci * atl1_phy_config - Timer Call-back 25558c2ecf20Sopenharmony_ci * @t: timer_list containing pointer to netdev cast into an unsigned long 25568c2ecf20Sopenharmony_ci */ 25578c2ecf20Sopenharmony_cistatic void atl1_phy_config(struct timer_list *t) 25588c2ecf20Sopenharmony_ci{ 25598c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = from_timer(adapter, t, 25608c2ecf20Sopenharmony_ci phy_config_timer); 25618c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 25628c2ecf20Sopenharmony_ci unsigned long flags; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->lock, flags); 25658c2ecf20Sopenharmony_ci adapter->phy_timer_pending = false; 25668c2ecf20Sopenharmony_ci atl1_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); 25678c2ecf20Sopenharmony_ci atl1_write_phy_reg(hw, MII_ATLX_CR, hw->mii_1000t_ctrl_reg); 25688c2ecf20Sopenharmony_ci atl1_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN); 25698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->lock, flags); 25708c2ecf20Sopenharmony_ci} 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci/* 25738c2ecf20Sopenharmony_ci * Orphaned vendor comment left intact here: 25748c2ecf20Sopenharmony_ci * <vendor comment> 25758c2ecf20Sopenharmony_ci * If TPD Buffer size equal to 0, PCIE DMAR_TO_INT 25768c2ecf20Sopenharmony_ci * will assert. We do soft reset <0x1400=1> according 25778c2ecf20Sopenharmony_ci * with the SPEC. BUT, it seemes that PCIE or DMA 25788c2ecf20Sopenharmony_ci * state-machine will not be reset. DMAR_TO_INT will 25798c2ecf20Sopenharmony_ci * assert again and again. 25808c2ecf20Sopenharmony_ci * </vendor comment> 25818c2ecf20Sopenharmony_ci */ 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_cistatic int atl1_reset(struct atl1_adapter *adapter) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci int ret; 25868c2ecf20Sopenharmony_ci ret = atl1_reset_hw(&adapter->hw); 25878c2ecf20Sopenharmony_ci if (ret) 25888c2ecf20Sopenharmony_ci return ret; 25898c2ecf20Sopenharmony_ci return atl1_init_hw(&adapter->hw); 25908c2ecf20Sopenharmony_ci} 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_cistatic s32 atl1_up(struct atl1_adapter *adapter) 25938c2ecf20Sopenharmony_ci{ 25948c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 25958c2ecf20Sopenharmony_ci int err; 25968c2ecf20Sopenharmony_ci int irq_flags = 0; 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci /* hardware has been reset, we need to reload some things */ 25998c2ecf20Sopenharmony_ci atlx_set_multi(netdev); 26008c2ecf20Sopenharmony_ci atl1_init_ring_ptrs(adapter); 26018c2ecf20Sopenharmony_ci atlx_restore_vlan(adapter); 26028c2ecf20Sopenharmony_ci err = atl1_alloc_rx_buffers(adapter); 26038c2ecf20Sopenharmony_ci if (unlikely(!err)) 26048c2ecf20Sopenharmony_ci /* no RX BUFFER allocated */ 26058c2ecf20Sopenharmony_ci return -ENOMEM; 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci if (unlikely(atl1_configure(adapter))) { 26088c2ecf20Sopenharmony_ci err = -EIO; 26098c2ecf20Sopenharmony_ci goto err_up; 26108c2ecf20Sopenharmony_ci } 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci err = pci_enable_msi(adapter->pdev); 26138c2ecf20Sopenharmony_ci if (err) { 26148c2ecf20Sopenharmony_ci if (netif_msg_ifup(adapter)) 26158c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 26168c2ecf20Sopenharmony_ci "Unable to enable MSI: %d\n", err); 26178c2ecf20Sopenharmony_ci irq_flags |= IRQF_SHARED; 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci err = request_irq(adapter->pdev->irq, atl1_intr, irq_flags, 26218c2ecf20Sopenharmony_ci netdev->name, netdev); 26228c2ecf20Sopenharmony_ci if (unlikely(err)) 26238c2ecf20Sopenharmony_ci goto err_up; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci napi_enable(&adapter->napi); 26268c2ecf20Sopenharmony_ci atlx_irq_enable(adapter); 26278c2ecf20Sopenharmony_ci atl1_check_link(adapter); 26288c2ecf20Sopenharmony_ci netif_start_queue(netdev); 26298c2ecf20Sopenharmony_ci return 0; 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_cierr_up: 26328c2ecf20Sopenharmony_ci pci_disable_msi(adapter->pdev); 26338c2ecf20Sopenharmony_ci /* free rx_buffers */ 26348c2ecf20Sopenharmony_ci atl1_clean_rx_ring(adapter); 26358c2ecf20Sopenharmony_ci return err; 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_cistatic void atl1_down(struct atl1_adapter *adapter) 26398c2ecf20Sopenharmony_ci{ 26408c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci napi_disable(&adapter->napi); 26438c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 26448c2ecf20Sopenharmony_ci del_timer_sync(&adapter->phy_config_timer); 26458c2ecf20Sopenharmony_ci adapter->phy_timer_pending = false; 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci atlx_irq_disable(adapter); 26488c2ecf20Sopenharmony_ci free_irq(adapter->pdev->irq, netdev); 26498c2ecf20Sopenharmony_ci pci_disable_msi(adapter->pdev); 26508c2ecf20Sopenharmony_ci atl1_reset_hw(&adapter->hw); 26518c2ecf20Sopenharmony_ci adapter->cmb.cmb->int_stats = 0; 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 26548c2ecf20Sopenharmony_ci adapter->link_duplex = -1; 26558c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci atl1_clean_tx_ring(adapter); 26588c2ecf20Sopenharmony_ci atl1_clean_rx_ring(adapter); 26598c2ecf20Sopenharmony_ci} 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_cistatic void atl1_reset_dev_task(struct work_struct *work) 26628c2ecf20Sopenharmony_ci{ 26638c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = 26648c2ecf20Sopenharmony_ci container_of(work, struct atl1_adapter, reset_dev_task); 26658c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci netif_device_detach(netdev); 26688c2ecf20Sopenharmony_ci atl1_down(adapter); 26698c2ecf20Sopenharmony_ci atl1_up(adapter); 26708c2ecf20Sopenharmony_ci netif_device_attach(netdev); 26718c2ecf20Sopenharmony_ci} 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci/** 26748c2ecf20Sopenharmony_ci * atl1_change_mtu - Change the Maximum Transfer Unit 26758c2ecf20Sopenharmony_ci * @netdev: network interface device structure 26768c2ecf20Sopenharmony_ci * @new_mtu: new value for maximum frame size 26778c2ecf20Sopenharmony_ci * 26788c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 26798c2ecf20Sopenharmony_ci */ 26808c2ecf20Sopenharmony_cistatic int atl1_change_mtu(struct net_device *netdev, int new_mtu) 26818c2ecf20Sopenharmony_ci{ 26828c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 26838c2ecf20Sopenharmony_ci int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci adapter->hw.max_frame_size = max_frame; 26868c2ecf20Sopenharmony_ci adapter->hw.tx_jumbo_task_th = (max_frame + 7) >> 3; 26878c2ecf20Sopenharmony_ci adapter->rx_buffer_len = (max_frame + 7) & ~7; 26888c2ecf20Sopenharmony_ci adapter->hw.rx_jumbo_th = adapter->rx_buffer_len / 8; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci netdev->mtu = new_mtu; 26918c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 26928c2ecf20Sopenharmony_ci atl1_down(adapter); 26938c2ecf20Sopenharmony_ci atl1_up(adapter); 26948c2ecf20Sopenharmony_ci } 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci return 0; 26978c2ecf20Sopenharmony_ci} 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci/** 27008c2ecf20Sopenharmony_ci * atl1_open - Called when a network interface is made active 27018c2ecf20Sopenharmony_ci * @netdev: network interface device structure 27028c2ecf20Sopenharmony_ci * 27038c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure 27048c2ecf20Sopenharmony_ci * 27058c2ecf20Sopenharmony_ci * The open entry point is called when a network interface is made 27068c2ecf20Sopenharmony_ci * active by the system (IFF_UP). At this point all resources needed 27078c2ecf20Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt 27088c2ecf20Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started, 27098c2ecf20Sopenharmony_ci * and the stack is notified that the interface is ready. 27108c2ecf20Sopenharmony_ci */ 27118c2ecf20Sopenharmony_cistatic int atl1_open(struct net_device *netdev) 27128c2ecf20Sopenharmony_ci{ 27138c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 27148c2ecf20Sopenharmony_ci int err; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci /* allocate transmit descriptors */ 27198c2ecf20Sopenharmony_ci err = atl1_setup_ring_resources(adapter); 27208c2ecf20Sopenharmony_ci if (err) 27218c2ecf20Sopenharmony_ci return err; 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci err = atl1_up(adapter); 27248c2ecf20Sopenharmony_ci if (err) 27258c2ecf20Sopenharmony_ci goto err_up; 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci return 0; 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_cierr_up: 27308c2ecf20Sopenharmony_ci atl1_reset(adapter); 27318c2ecf20Sopenharmony_ci return err; 27328c2ecf20Sopenharmony_ci} 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci/** 27358c2ecf20Sopenharmony_ci * atl1_close - Disables a network interface 27368c2ecf20Sopenharmony_ci * @netdev: network interface device structure 27378c2ecf20Sopenharmony_ci * 27388c2ecf20Sopenharmony_ci * Returns 0, this is not allowed to fail 27398c2ecf20Sopenharmony_ci * 27408c2ecf20Sopenharmony_ci * The close entry point is called when an interface is de-activated 27418c2ecf20Sopenharmony_ci * by the OS. The hardware is still under the drivers control, but 27428c2ecf20Sopenharmony_ci * needs to be disabled. A global MAC reset is issued to stop the 27438c2ecf20Sopenharmony_ci * hardware, and all transmit and receive resources are freed. 27448c2ecf20Sopenharmony_ci */ 27458c2ecf20Sopenharmony_cistatic int atl1_close(struct net_device *netdev) 27468c2ecf20Sopenharmony_ci{ 27478c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 27488c2ecf20Sopenharmony_ci atl1_down(adapter); 27498c2ecf20Sopenharmony_ci atl1_free_ring_resources(adapter); 27508c2ecf20Sopenharmony_ci return 0; 27518c2ecf20Sopenharmony_ci} 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 27548c2ecf20Sopenharmony_cistatic int atl1_suspend(struct device *dev) 27558c2ecf20Sopenharmony_ci{ 27568c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 27578c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 27588c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 27598c2ecf20Sopenharmony_ci u32 ctrl = 0; 27608c2ecf20Sopenharmony_ci u32 wufc = adapter->wol; 27618c2ecf20Sopenharmony_ci u32 val; 27628c2ecf20Sopenharmony_ci u16 speed; 27638c2ecf20Sopenharmony_ci u16 duplex; 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci netif_device_detach(netdev); 27668c2ecf20Sopenharmony_ci if (netif_running(netdev)) 27678c2ecf20Sopenharmony_ci atl1_down(adapter); 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); 27708c2ecf20Sopenharmony_ci atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); 27718c2ecf20Sopenharmony_ci val = ctrl & BMSR_LSTATUS; 27728c2ecf20Sopenharmony_ci if (val) 27738c2ecf20Sopenharmony_ci wufc &= ~ATLX_WUFC_LNKC; 27748c2ecf20Sopenharmony_ci if (!wufc) 27758c2ecf20Sopenharmony_ci goto disable_wol; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci if (val) { 27788c2ecf20Sopenharmony_ci val = atl1_get_speed_and_duplex(hw, &speed, &duplex); 27798c2ecf20Sopenharmony_ci if (val) { 27808c2ecf20Sopenharmony_ci if (netif_msg_ifdown(adapter)) 27818c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, dev, 27828c2ecf20Sopenharmony_ci "error getting speed/duplex\n"); 27838c2ecf20Sopenharmony_ci goto disable_wol; 27848c2ecf20Sopenharmony_ci } 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci ctrl = 0; 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci /* enable magic packet WOL */ 27898c2ecf20Sopenharmony_ci if (wufc & ATLX_WUFC_MAG) 27908c2ecf20Sopenharmony_ci ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN); 27918c2ecf20Sopenharmony_ci iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL); 27928c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_WOL_CTRL); 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci /* configure the mac */ 27958c2ecf20Sopenharmony_ci ctrl = MAC_CTRL_RX_EN; 27968c2ecf20Sopenharmony_ci ctrl |= ((u32)((speed == SPEED_1000) ? MAC_CTRL_SPEED_1000 : 27978c2ecf20Sopenharmony_ci MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT); 27988c2ecf20Sopenharmony_ci if (duplex == FULL_DUPLEX) 27998c2ecf20Sopenharmony_ci ctrl |= MAC_CTRL_DUPLX; 28008c2ecf20Sopenharmony_ci ctrl |= (((u32)adapter->hw.preamble_len & 28018c2ecf20Sopenharmony_ci MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); 28028c2ecf20Sopenharmony_ci __atlx_vlan_mode(netdev->features, &ctrl); 28038c2ecf20Sopenharmony_ci if (wufc & ATLX_WUFC_MAG) 28048c2ecf20Sopenharmony_ci ctrl |= MAC_CTRL_BC_EN; 28058c2ecf20Sopenharmony_ci iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL); 28068c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_MAC_CTRL); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci /* poke the PHY */ 28098c2ecf20Sopenharmony_ci ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC); 28108c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 28118c2ecf20Sopenharmony_ci iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC); 28128c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_PCIE_PHYMISC); 28138c2ecf20Sopenharmony_ci } else { 28148c2ecf20Sopenharmony_ci ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); 28158c2ecf20Sopenharmony_ci iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL); 28168c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_WOL_CTRL); 28178c2ecf20Sopenharmony_ci iowrite32(0, hw->hw_addr + REG_MAC_CTRL); 28188c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_MAC_CTRL); 28198c2ecf20Sopenharmony_ci hw->phy_configured = false; 28208c2ecf20Sopenharmony_ci } 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci return 0; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci disable_wol: 28258c2ecf20Sopenharmony_ci iowrite32(0, hw->hw_addr + REG_WOL_CTRL); 28268c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_WOL_CTRL); 28278c2ecf20Sopenharmony_ci ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC); 28288c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 28298c2ecf20Sopenharmony_ci iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC); 28308c2ecf20Sopenharmony_ci ioread32(hw->hw_addr + REG_PCIE_PHYMISC); 28318c2ecf20Sopenharmony_ci hw->phy_configured = false; 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci return 0; 28348c2ecf20Sopenharmony_ci} 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_cistatic int atl1_resume(struct device *dev) 28378c2ecf20Sopenharmony_ci{ 28388c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 28398c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci atl1_reset_hw(&adapter->hw); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 28468c2ecf20Sopenharmony_ci adapter->cmb.cmb->int_stats = 0; 28478c2ecf20Sopenharmony_ci atl1_up(adapter); 28488c2ecf20Sopenharmony_ci } 28498c2ecf20Sopenharmony_ci netif_device_attach(netdev); 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci return 0; 28528c2ecf20Sopenharmony_ci} 28538c2ecf20Sopenharmony_ci#endif 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atl1_pm_ops, atl1_suspend, atl1_resume); 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_cistatic void atl1_shutdown(struct pci_dev *pdev) 28588c2ecf20Sopenharmony_ci{ 28598c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 28608c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 28638c2ecf20Sopenharmony_ci atl1_suspend(&pdev->dev); 28648c2ecf20Sopenharmony_ci#endif 28658c2ecf20Sopenharmony_ci pci_wake_from_d3(pdev, adapter->wol); 28668c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D3hot); 28678c2ecf20Sopenharmony_ci} 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 28708c2ecf20Sopenharmony_cistatic void atl1_poll_controller(struct net_device *netdev) 28718c2ecf20Sopenharmony_ci{ 28728c2ecf20Sopenharmony_ci disable_irq(netdev->irq); 28738c2ecf20Sopenharmony_ci atl1_intr(netdev->irq, netdev); 28748c2ecf20Sopenharmony_ci enable_irq(netdev->irq); 28758c2ecf20Sopenharmony_ci} 28768c2ecf20Sopenharmony_ci#endif 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_cistatic const struct net_device_ops atl1_netdev_ops = { 28798c2ecf20Sopenharmony_ci .ndo_open = atl1_open, 28808c2ecf20Sopenharmony_ci .ndo_stop = atl1_close, 28818c2ecf20Sopenharmony_ci .ndo_start_xmit = atl1_xmit_frame, 28828c2ecf20Sopenharmony_ci .ndo_set_rx_mode = atlx_set_multi, 28838c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 28848c2ecf20Sopenharmony_ci .ndo_set_mac_address = atl1_set_mac, 28858c2ecf20Sopenharmony_ci .ndo_change_mtu = atl1_change_mtu, 28868c2ecf20Sopenharmony_ci .ndo_fix_features = atlx_fix_features, 28878c2ecf20Sopenharmony_ci .ndo_set_features = atlx_set_features, 28888c2ecf20Sopenharmony_ci .ndo_do_ioctl = atlx_ioctl, 28898c2ecf20Sopenharmony_ci .ndo_tx_timeout = atlx_tx_timeout, 28908c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 28918c2ecf20Sopenharmony_ci .ndo_poll_controller = atl1_poll_controller, 28928c2ecf20Sopenharmony_ci#endif 28938c2ecf20Sopenharmony_ci}; 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci/** 28968c2ecf20Sopenharmony_ci * atl1_probe - Device Initialization Routine 28978c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 28988c2ecf20Sopenharmony_ci * @ent: entry in atl1_pci_tbl 28998c2ecf20Sopenharmony_ci * 29008c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 29018c2ecf20Sopenharmony_ci * 29028c2ecf20Sopenharmony_ci * atl1_probe initializes an adapter identified by a pci_dev structure. 29038c2ecf20Sopenharmony_ci * The OS initialization, configuring of the adapter private structure, 29048c2ecf20Sopenharmony_ci * and a hardware reset occur. 29058c2ecf20Sopenharmony_ci */ 29068c2ecf20Sopenharmony_cistatic int atl1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 29078c2ecf20Sopenharmony_ci{ 29088c2ecf20Sopenharmony_ci struct net_device *netdev; 29098c2ecf20Sopenharmony_ci struct atl1_adapter *adapter; 29108c2ecf20Sopenharmony_ci static int cards_found = 0; 29118c2ecf20Sopenharmony_ci int err; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 29148c2ecf20Sopenharmony_ci if (err) 29158c2ecf20Sopenharmony_ci return err; 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci /* 29188c2ecf20Sopenharmony_ci * The atl1 chip can DMA to 64-bit addresses, but it uses a single 29198c2ecf20Sopenharmony_ci * shared register for the high 32 bits, so only a single, aligned, 29208c2ecf20Sopenharmony_ci * 4 GB physical address range can be used at a time. 29218c2ecf20Sopenharmony_ci * 29228c2ecf20Sopenharmony_ci * Supporting 64-bit DMA on this hardware is more trouble than it's 29238c2ecf20Sopenharmony_ci * worth. It is far easier to limit to 32-bit DMA than update 29248c2ecf20Sopenharmony_ci * various kernel subsystems to support the mechanics required by a 29258c2ecf20Sopenharmony_ci * fixed-high-32-bit system. 29268c2ecf20Sopenharmony_ci */ 29278c2ecf20Sopenharmony_ci err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 29288c2ecf20Sopenharmony_ci if (err) { 29298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no usable DMA configuration\n"); 29308c2ecf20Sopenharmony_ci goto err_dma; 29318c2ecf20Sopenharmony_ci } 29328c2ecf20Sopenharmony_ci /* 29338c2ecf20Sopenharmony_ci * Mark all PCI regions associated with PCI device 29348c2ecf20Sopenharmony_ci * pdev as being reserved by owner atl1_driver_name 29358c2ecf20Sopenharmony_ci */ 29368c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, ATLX_DRIVER_NAME); 29378c2ecf20Sopenharmony_ci if (err) 29388c2ecf20Sopenharmony_ci goto err_request_regions; 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci /* 29418c2ecf20Sopenharmony_ci * Enables bus-mastering on the device and calls 29428c2ecf20Sopenharmony_ci * pcibios_set_master to do the needed arch specific settings 29438c2ecf20Sopenharmony_ci */ 29448c2ecf20Sopenharmony_ci pci_set_master(pdev); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct atl1_adapter)); 29478c2ecf20Sopenharmony_ci if (!netdev) { 29488c2ecf20Sopenharmony_ci err = -ENOMEM; 29498c2ecf20Sopenharmony_ci goto err_alloc_etherdev; 29508c2ecf20Sopenharmony_ci } 29518c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, netdev); 29548c2ecf20Sopenharmony_ci adapter = netdev_priv(netdev); 29558c2ecf20Sopenharmony_ci adapter->netdev = netdev; 29568c2ecf20Sopenharmony_ci adapter->pdev = pdev; 29578c2ecf20Sopenharmony_ci adapter->hw.back = adapter; 29588c2ecf20Sopenharmony_ci adapter->msg_enable = netif_msg_init(debug, atl1_default_msg); 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci adapter->hw.hw_addr = pci_iomap(pdev, 0, 0); 29618c2ecf20Sopenharmony_ci if (!adapter->hw.hw_addr) { 29628c2ecf20Sopenharmony_ci err = -EIO; 29638c2ecf20Sopenharmony_ci goto err_pci_iomap; 29648c2ecf20Sopenharmony_ci } 29658c2ecf20Sopenharmony_ci /* get device revision number */ 29668c2ecf20Sopenharmony_ci adapter->hw.dev_rev = ioread16(adapter->hw.hw_addr + 29678c2ecf20Sopenharmony_ci (REG_MASTER_CTRL + 2)); 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci /* set default ring resource counts */ 29708c2ecf20Sopenharmony_ci adapter->rfd_ring.count = adapter->rrd_ring.count = ATL1_DEFAULT_RFD; 29718c2ecf20Sopenharmony_ci adapter->tpd_ring.count = ATL1_DEFAULT_TPD; 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci adapter->mii.dev = netdev; 29748c2ecf20Sopenharmony_ci adapter->mii.mdio_read = mdio_read; 29758c2ecf20Sopenharmony_ci adapter->mii.mdio_write = mdio_write; 29768c2ecf20Sopenharmony_ci adapter->mii.phy_id_mask = 0x1f; 29778c2ecf20Sopenharmony_ci adapter->mii.reg_num_mask = 0x1f; 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci netdev->netdev_ops = &atl1_netdev_ops; 29808c2ecf20Sopenharmony_ci netdev->watchdog_timeo = 5 * HZ; 29818c2ecf20Sopenharmony_ci netif_napi_add(netdev, &adapter->napi, atl1_rings_clean, 64); 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci netdev->ethtool_ops = &atl1_ethtool_ops; 29848c2ecf20Sopenharmony_ci adapter->bd_number = cards_found; 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci /* setup the private structure */ 29878c2ecf20Sopenharmony_ci err = atl1_sw_init(adapter); 29888c2ecf20Sopenharmony_ci if (err) 29898c2ecf20Sopenharmony_ci goto err_common; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci netdev->features = NETIF_F_HW_CSUM; 29928c2ecf20Sopenharmony_ci netdev->features |= NETIF_F_SG; 29938c2ecf20Sopenharmony_ci netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX); 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO | 29968c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX; 29978c2ecf20Sopenharmony_ci 29988c2ecf20Sopenharmony_ci /* is this valid? see atl1_setup_mac_ctrl() */ 29998c2ecf20Sopenharmony_ci netdev->features |= NETIF_F_RXCSUM; 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_ci /* MTU range: 42 - 10218 */ 30028c2ecf20Sopenharmony_ci netdev->min_mtu = ETH_ZLEN - (ETH_HLEN + VLAN_HLEN); 30038c2ecf20Sopenharmony_ci netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - 30048c2ecf20Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci /* 30078c2ecf20Sopenharmony_ci * patch for some L1 of old version, 30088c2ecf20Sopenharmony_ci * the final version of L1 may not need these 30098c2ecf20Sopenharmony_ci * patches 30108c2ecf20Sopenharmony_ci */ 30118c2ecf20Sopenharmony_ci /* atl1_pcie_patch(adapter); */ 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci /* really reset GPHY core */ 30148c2ecf20Sopenharmony_ci iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE); 30158c2ecf20Sopenharmony_ci 30168c2ecf20Sopenharmony_ci /* 30178c2ecf20Sopenharmony_ci * reset the controller to 30188c2ecf20Sopenharmony_ci * put the device in a known good starting state 30198c2ecf20Sopenharmony_ci */ 30208c2ecf20Sopenharmony_ci if (atl1_reset_hw(&adapter->hw)) { 30218c2ecf20Sopenharmony_ci err = -EIO; 30228c2ecf20Sopenharmony_ci goto err_common; 30238c2ecf20Sopenharmony_ci } 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci /* copy the MAC address out of the EEPROM */ 30268c2ecf20Sopenharmony_ci if (atl1_read_mac_addr(&adapter->hw)) { 30278c2ecf20Sopenharmony_ci /* mark random mac */ 30288c2ecf20Sopenharmony_ci netdev->addr_assign_type = NET_ADDR_RANDOM; 30298c2ecf20Sopenharmony_ci } 30308c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(netdev->dev_addr)) { 30338c2ecf20Sopenharmony_ci err = -EIO; 30348c2ecf20Sopenharmony_ci goto err_common; 30358c2ecf20Sopenharmony_ci } 30368c2ecf20Sopenharmony_ci 30378c2ecf20Sopenharmony_ci atl1_check_options(adapter); 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci /* pre-init the MAC, and setup link */ 30408c2ecf20Sopenharmony_ci err = atl1_init_hw(&adapter->hw); 30418c2ecf20Sopenharmony_ci if (err) { 30428c2ecf20Sopenharmony_ci err = -EIO; 30438c2ecf20Sopenharmony_ci goto err_common; 30448c2ecf20Sopenharmony_ci } 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci atl1_pcie_patch(adapter); 30478c2ecf20Sopenharmony_ci /* assume we have no link for now */ 30488c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci timer_setup(&adapter->phy_config_timer, atl1_phy_config, 0); 30518c2ecf20Sopenharmony_ci adapter->phy_timer_pending = false; 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci INIT_WORK(&adapter->reset_dev_task, atl1_reset_dev_task); 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci INIT_WORK(&adapter->link_chg_task, atlx_link_chg_task); 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci err = register_netdev(netdev); 30588c2ecf20Sopenharmony_ci if (err) 30598c2ecf20Sopenharmony_ci goto err_common; 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci cards_found++; 30628c2ecf20Sopenharmony_ci atl1_via_workaround(adapter); 30638c2ecf20Sopenharmony_ci return 0; 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_cierr_common: 30668c2ecf20Sopenharmony_ci pci_iounmap(pdev, adapter->hw.hw_addr); 30678c2ecf20Sopenharmony_cierr_pci_iomap: 30688c2ecf20Sopenharmony_ci free_netdev(netdev); 30698c2ecf20Sopenharmony_cierr_alloc_etherdev: 30708c2ecf20Sopenharmony_ci pci_release_regions(pdev); 30718c2ecf20Sopenharmony_cierr_dma: 30728c2ecf20Sopenharmony_cierr_request_regions: 30738c2ecf20Sopenharmony_ci pci_disable_device(pdev); 30748c2ecf20Sopenharmony_ci return err; 30758c2ecf20Sopenharmony_ci} 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci/** 30788c2ecf20Sopenharmony_ci * atl1_remove - Device Removal Routine 30798c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 30808c2ecf20Sopenharmony_ci * 30818c2ecf20Sopenharmony_ci * atl1_remove is called by the PCI subsystem to alert the driver 30828c2ecf20Sopenharmony_ci * that it should release a PCI device. The could be caused by a 30838c2ecf20Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from 30848c2ecf20Sopenharmony_ci * memory. 30858c2ecf20Sopenharmony_ci */ 30868c2ecf20Sopenharmony_cistatic void atl1_remove(struct pci_dev *pdev) 30878c2ecf20Sopenharmony_ci{ 30888c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 30898c2ecf20Sopenharmony_ci struct atl1_adapter *adapter; 30908c2ecf20Sopenharmony_ci /* Device not available. Return. */ 30918c2ecf20Sopenharmony_ci if (!netdev) 30928c2ecf20Sopenharmony_ci return; 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci adapter = netdev_priv(netdev); 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci /* 30978c2ecf20Sopenharmony_ci * Some atl1 boards lack persistent storage for their MAC, and get it 30988c2ecf20Sopenharmony_ci * from the BIOS during POST. If we've been messing with the MAC 30998c2ecf20Sopenharmony_ci * address, we need to save the permanent one. 31008c2ecf20Sopenharmony_ci */ 31018c2ecf20Sopenharmony_ci if (!ether_addr_equal_unaligned(adapter->hw.mac_addr, 31028c2ecf20Sopenharmony_ci adapter->hw.perm_mac_addr)) { 31038c2ecf20Sopenharmony_ci memcpy(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, 31048c2ecf20Sopenharmony_ci ETH_ALEN); 31058c2ecf20Sopenharmony_ci atl1_set_mac_addr(&adapter->hw); 31068c2ecf20Sopenharmony_ci } 31078c2ecf20Sopenharmony_ci 31088c2ecf20Sopenharmony_ci iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE); 31098c2ecf20Sopenharmony_ci unregister_netdev(netdev); 31108c2ecf20Sopenharmony_ci pci_iounmap(pdev, adapter->hw.hw_addr); 31118c2ecf20Sopenharmony_ci pci_release_regions(pdev); 31128c2ecf20Sopenharmony_ci free_netdev(netdev); 31138c2ecf20Sopenharmony_ci pci_disable_device(pdev); 31148c2ecf20Sopenharmony_ci} 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_cistatic struct pci_driver atl1_driver = { 31178c2ecf20Sopenharmony_ci .name = ATLX_DRIVER_NAME, 31188c2ecf20Sopenharmony_ci .id_table = atl1_pci_tbl, 31198c2ecf20Sopenharmony_ci .probe = atl1_probe, 31208c2ecf20Sopenharmony_ci .remove = atl1_remove, 31218c2ecf20Sopenharmony_ci .shutdown = atl1_shutdown, 31228c2ecf20Sopenharmony_ci .driver.pm = &atl1_pm_ops, 31238c2ecf20Sopenharmony_ci}; 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_cistruct atl1_stats { 31268c2ecf20Sopenharmony_ci char stat_string[ETH_GSTRING_LEN]; 31278c2ecf20Sopenharmony_ci int sizeof_stat; 31288c2ecf20Sopenharmony_ci int stat_offset; 31298c2ecf20Sopenharmony_ci}; 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci#define ATL1_STAT(m) \ 31328c2ecf20Sopenharmony_ci sizeof(((struct atl1_adapter *)0)->m), offsetof(struct atl1_adapter, m) 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_cistatic struct atl1_stats atl1_gstrings_stats[] = { 31358c2ecf20Sopenharmony_ci {"rx_packets", ATL1_STAT(soft_stats.rx_packets)}, 31368c2ecf20Sopenharmony_ci {"tx_packets", ATL1_STAT(soft_stats.tx_packets)}, 31378c2ecf20Sopenharmony_ci {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)}, 31388c2ecf20Sopenharmony_ci {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)}, 31398c2ecf20Sopenharmony_ci {"rx_errors", ATL1_STAT(soft_stats.rx_errors)}, 31408c2ecf20Sopenharmony_ci {"tx_errors", ATL1_STAT(soft_stats.tx_errors)}, 31418c2ecf20Sopenharmony_ci {"multicast", ATL1_STAT(soft_stats.multicast)}, 31428c2ecf20Sopenharmony_ci {"collisions", ATL1_STAT(soft_stats.collisions)}, 31438c2ecf20Sopenharmony_ci {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)}, 31448c2ecf20Sopenharmony_ci {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)}, 31458c2ecf20Sopenharmony_ci {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)}, 31468c2ecf20Sopenharmony_ci {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)}, 31478c2ecf20Sopenharmony_ci {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)}, 31488c2ecf20Sopenharmony_ci {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)}, 31498c2ecf20Sopenharmony_ci {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)}, 31508c2ecf20Sopenharmony_ci {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)}, 31518c2ecf20Sopenharmony_ci {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)}, 31528c2ecf20Sopenharmony_ci {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)}, 31538c2ecf20Sopenharmony_ci {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)}, 31548c2ecf20Sopenharmony_ci {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)}, 31558c2ecf20Sopenharmony_ci {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)}, 31568c2ecf20Sopenharmony_ci {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)}, 31578c2ecf20Sopenharmony_ci {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)}, 31588c2ecf20Sopenharmony_ci {"tx_underrun", ATL1_STAT(soft_stats.tx_underrun)}, 31598c2ecf20Sopenharmony_ci {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)}, 31608c2ecf20Sopenharmony_ci {"tx_pause", ATL1_STAT(soft_stats.tx_pause)}, 31618c2ecf20Sopenharmony_ci {"rx_pause", ATL1_STAT(soft_stats.rx_pause)}, 31628c2ecf20Sopenharmony_ci {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)}, 31638c2ecf20Sopenharmony_ci {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)} 31648c2ecf20Sopenharmony_ci}; 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_cistatic void atl1_get_ethtool_stats(struct net_device *netdev, 31678c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 31688c2ecf20Sopenharmony_ci{ 31698c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 31708c2ecf20Sopenharmony_ci int i; 31718c2ecf20Sopenharmony_ci char *p; 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) { 31748c2ecf20Sopenharmony_ci p = (char *)adapter+atl1_gstrings_stats[i].stat_offset; 31758c2ecf20Sopenharmony_ci data[i] = (atl1_gstrings_stats[i].sizeof_stat == 31768c2ecf20Sopenharmony_ci sizeof(u64)) ? *(u64 *)p : *(u32 *)p; 31778c2ecf20Sopenharmony_ci } 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci} 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_cistatic int atl1_get_sset_count(struct net_device *netdev, int sset) 31828c2ecf20Sopenharmony_ci{ 31838c2ecf20Sopenharmony_ci switch (sset) { 31848c2ecf20Sopenharmony_ci case ETH_SS_STATS: 31858c2ecf20Sopenharmony_ci return ARRAY_SIZE(atl1_gstrings_stats); 31868c2ecf20Sopenharmony_ci default: 31878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 31888c2ecf20Sopenharmony_ci } 31898c2ecf20Sopenharmony_ci} 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_cistatic int atl1_get_link_ksettings(struct net_device *netdev, 31928c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 31938c2ecf20Sopenharmony_ci{ 31948c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 31958c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 31968c2ecf20Sopenharmony_ci u32 supported, advertising; 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci supported = (SUPPORTED_10baseT_Half | 31998c2ecf20Sopenharmony_ci SUPPORTED_10baseT_Full | 32008c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Half | 32018c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Full | 32028c2ecf20Sopenharmony_ci SUPPORTED_1000baseT_Full | 32038c2ecf20Sopenharmony_ci SUPPORTED_Autoneg | SUPPORTED_TP); 32048c2ecf20Sopenharmony_ci advertising = ADVERTISED_TP; 32058c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 32068c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) { 32078c2ecf20Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 32088c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) { 32098c2ecf20Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 32108c2ecf20Sopenharmony_ci advertising |= 32118c2ecf20Sopenharmony_ci (ADVERTISED_10baseT_Half | 32128c2ecf20Sopenharmony_ci ADVERTISED_10baseT_Full | 32138c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Half | 32148c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Full | 32158c2ecf20Sopenharmony_ci ADVERTISED_1000baseT_Full); 32168c2ecf20Sopenharmony_ci } else 32178c2ecf20Sopenharmony_ci advertising |= (ADVERTISED_1000baseT_Full); 32188c2ecf20Sopenharmony_ci } 32198c2ecf20Sopenharmony_ci cmd->base.port = PORT_TP; 32208c2ecf20Sopenharmony_ci cmd->base.phy_address = 0; 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci if (netif_carrier_ok(adapter->netdev)) { 32238c2ecf20Sopenharmony_ci u16 link_speed, link_duplex; 32248c2ecf20Sopenharmony_ci atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex); 32258c2ecf20Sopenharmony_ci cmd->base.speed = link_speed; 32268c2ecf20Sopenharmony_ci if (link_duplex == FULL_DUPLEX) 32278c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 32288c2ecf20Sopenharmony_ci else 32298c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 32308c2ecf20Sopenharmony_ci } else { 32318c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 32328c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 32338c2ecf20Sopenharmony_ci } 32348c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 32358c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) 32368c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 32378c2ecf20Sopenharmony_ci else 32388c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_DISABLE; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 32418c2ecf20Sopenharmony_ci supported); 32428c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 32438c2ecf20Sopenharmony_ci advertising); 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_ci return 0; 32468c2ecf20Sopenharmony_ci} 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_cistatic int atl1_set_link_ksettings(struct net_device *netdev, 32498c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 32508c2ecf20Sopenharmony_ci{ 32518c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 32528c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 32538c2ecf20Sopenharmony_ci u16 phy_data; 32548c2ecf20Sopenharmony_ci int ret_val = 0; 32558c2ecf20Sopenharmony_ci u16 old_media_type = hw->media_type; 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { 32588c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 32598c2ecf20Sopenharmony_ci dev_dbg(&adapter->pdev->dev, 32608c2ecf20Sopenharmony_ci "ethtool shutting down adapter\n"); 32618c2ecf20Sopenharmony_ci atl1_down(adapter); 32628c2ecf20Sopenharmony_ci } 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_ENABLE) 32658c2ecf20Sopenharmony_ci hw->media_type = MEDIA_TYPE_AUTO_SENSOR; 32668c2ecf20Sopenharmony_ci else { 32678c2ecf20Sopenharmony_ci u32 speed = cmd->base.speed; 32688c2ecf20Sopenharmony_ci if (speed == SPEED_1000) { 32698c2ecf20Sopenharmony_ci if (cmd->base.duplex != DUPLEX_FULL) { 32708c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 32718c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 32728c2ecf20Sopenharmony_ci "1000M half is invalid\n"); 32738c2ecf20Sopenharmony_ci ret_val = -EINVAL; 32748c2ecf20Sopenharmony_ci goto exit_sset; 32758c2ecf20Sopenharmony_ci } 32768c2ecf20Sopenharmony_ci hw->media_type = MEDIA_TYPE_1000M_FULL; 32778c2ecf20Sopenharmony_ci } else if (speed == SPEED_100) { 32788c2ecf20Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 32798c2ecf20Sopenharmony_ci hw->media_type = MEDIA_TYPE_100M_FULL; 32808c2ecf20Sopenharmony_ci else 32818c2ecf20Sopenharmony_ci hw->media_type = MEDIA_TYPE_100M_HALF; 32828c2ecf20Sopenharmony_ci } else { 32838c2ecf20Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 32848c2ecf20Sopenharmony_ci hw->media_type = MEDIA_TYPE_10M_FULL; 32858c2ecf20Sopenharmony_ci else 32868c2ecf20Sopenharmony_ci hw->media_type = MEDIA_TYPE_10M_HALF; 32878c2ecf20Sopenharmony_ci } 32888c2ecf20Sopenharmony_ci } 32898c2ecf20Sopenharmony_ci 32908c2ecf20Sopenharmony_ci if (atl1_phy_setup_autoneg_adv(hw)) { 32918c2ecf20Sopenharmony_ci ret_val = -EINVAL; 32928c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 32938c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 32948c2ecf20Sopenharmony_ci "invalid ethtool speed/duplex setting\n"); 32958c2ecf20Sopenharmony_ci goto exit_sset; 32968c2ecf20Sopenharmony_ci } 32978c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 32988c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) 32998c2ecf20Sopenharmony_ci phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; 33008c2ecf20Sopenharmony_ci else { 33018c2ecf20Sopenharmony_ci switch (hw->media_type) { 33028c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 33038c2ecf20Sopenharmony_ci phy_data = 33048c2ecf20Sopenharmony_ci MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | 33058c2ecf20Sopenharmony_ci MII_CR_RESET; 33068c2ecf20Sopenharmony_ci break; 33078c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 33088c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_100 | MII_CR_RESET; 33098c2ecf20Sopenharmony_ci break; 33108c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 33118c2ecf20Sopenharmony_ci phy_data = 33128c2ecf20Sopenharmony_ci MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; 33138c2ecf20Sopenharmony_ci break; 33148c2ecf20Sopenharmony_ci default: 33158c2ecf20Sopenharmony_ci /* MEDIA_TYPE_10M_HALF: */ 33168c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_10 | MII_CR_RESET; 33178c2ecf20Sopenharmony_ci break; 33188c2ecf20Sopenharmony_ci } 33198c2ecf20Sopenharmony_ci } 33208c2ecf20Sopenharmony_ci atl1_write_phy_reg(hw, MII_BMCR, phy_data); 33218c2ecf20Sopenharmony_ciexit_sset: 33228c2ecf20Sopenharmony_ci if (ret_val) 33238c2ecf20Sopenharmony_ci hw->media_type = old_media_type; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { 33268c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 33278c2ecf20Sopenharmony_ci dev_dbg(&adapter->pdev->dev, 33288c2ecf20Sopenharmony_ci "ethtool starting adapter\n"); 33298c2ecf20Sopenharmony_ci atl1_up(adapter); 33308c2ecf20Sopenharmony_ci } else if (!ret_val) { 33318c2ecf20Sopenharmony_ci if (netif_msg_link(adapter)) 33328c2ecf20Sopenharmony_ci dev_dbg(&adapter->pdev->dev, 33338c2ecf20Sopenharmony_ci "ethtool resetting adapter\n"); 33348c2ecf20Sopenharmony_ci atl1_reset(adapter); 33358c2ecf20Sopenharmony_ci } 33368c2ecf20Sopenharmony_ci return ret_val; 33378c2ecf20Sopenharmony_ci} 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_cistatic void atl1_get_drvinfo(struct net_device *netdev, 33408c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 33418c2ecf20Sopenharmony_ci{ 33428c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver)); 33458c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 33468c2ecf20Sopenharmony_ci sizeof(drvinfo->bus_info)); 33478c2ecf20Sopenharmony_ci} 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_cistatic void atl1_get_wol(struct net_device *netdev, 33508c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 33518c2ecf20Sopenharmony_ci{ 33528c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci wol->supported = WAKE_MAGIC; 33558c2ecf20Sopenharmony_ci wol->wolopts = 0; 33568c2ecf20Sopenharmony_ci if (adapter->wol & ATLX_WUFC_MAG) 33578c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 33588c2ecf20Sopenharmony_ci} 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_cistatic int atl1_set_wol(struct net_device *netdev, 33618c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 33628c2ecf20Sopenharmony_ci{ 33638c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci if (wol->wolopts & (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | 33668c2ecf20Sopenharmony_ci WAKE_ARP | WAKE_MAGICSECURE)) 33678c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 33688c2ecf20Sopenharmony_ci adapter->wol = 0; 33698c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 33708c2ecf20Sopenharmony_ci adapter->wol |= ATLX_WUFC_MAG; 33718c2ecf20Sopenharmony_ci 33728c2ecf20Sopenharmony_ci device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci return 0; 33758c2ecf20Sopenharmony_ci} 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_cistatic u32 atl1_get_msglevel(struct net_device *netdev) 33788c2ecf20Sopenharmony_ci{ 33798c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 33808c2ecf20Sopenharmony_ci return adapter->msg_enable; 33818c2ecf20Sopenharmony_ci} 33828c2ecf20Sopenharmony_ci 33838c2ecf20Sopenharmony_cistatic void atl1_set_msglevel(struct net_device *netdev, u32 value) 33848c2ecf20Sopenharmony_ci{ 33858c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 33868c2ecf20Sopenharmony_ci adapter->msg_enable = value; 33878c2ecf20Sopenharmony_ci} 33888c2ecf20Sopenharmony_ci 33898c2ecf20Sopenharmony_cistatic int atl1_get_regs_len(struct net_device *netdev) 33908c2ecf20Sopenharmony_ci{ 33918c2ecf20Sopenharmony_ci return ATL1_REG_COUNT * sizeof(u32); 33928c2ecf20Sopenharmony_ci} 33938c2ecf20Sopenharmony_ci 33948c2ecf20Sopenharmony_cistatic void atl1_get_regs(struct net_device *netdev, struct ethtool_regs *regs, 33958c2ecf20Sopenharmony_ci void *p) 33968c2ecf20Sopenharmony_ci{ 33978c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 33988c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 33998c2ecf20Sopenharmony_ci unsigned int i; 34008c2ecf20Sopenharmony_ci u32 *regbuf = p; 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci for (i = 0; i < ATL1_REG_COUNT; i++) { 34038c2ecf20Sopenharmony_ci /* 34048c2ecf20Sopenharmony_ci * This switch statement avoids reserved regions 34058c2ecf20Sopenharmony_ci * of register space. 34068c2ecf20Sopenharmony_ci */ 34078c2ecf20Sopenharmony_ci switch (i) { 34088c2ecf20Sopenharmony_ci case 6 ... 9: 34098c2ecf20Sopenharmony_ci case 14: 34108c2ecf20Sopenharmony_ci case 29 ... 31: 34118c2ecf20Sopenharmony_ci case 34 ... 63: 34128c2ecf20Sopenharmony_ci case 75 ... 127: 34138c2ecf20Sopenharmony_ci case 136 ... 1023: 34148c2ecf20Sopenharmony_ci case 1027 ... 1087: 34158c2ecf20Sopenharmony_ci case 1091 ... 1151: 34168c2ecf20Sopenharmony_ci case 1194 ... 1195: 34178c2ecf20Sopenharmony_ci case 1200 ... 1201: 34188c2ecf20Sopenharmony_ci case 1206 ... 1213: 34198c2ecf20Sopenharmony_ci case 1216 ... 1279: 34208c2ecf20Sopenharmony_ci case 1290 ... 1311: 34218c2ecf20Sopenharmony_ci case 1323 ... 1343: 34228c2ecf20Sopenharmony_ci case 1358 ... 1359: 34238c2ecf20Sopenharmony_ci case 1368 ... 1375: 34248c2ecf20Sopenharmony_ci case 1378 ... 1383: 34258c2ecf20Sopenharmony_ci case 1388 ... 1391: 34268c2ecf20Sopenharmony_ci case 1393 ... 1395: 34278c2ecf20Sopenharmony_ci case 1402 ... 1403: 34288c2ecf20Sopenharmony_ci case 1410 ... 1471: 34298c2ecf20Sopenharmony_ci case 1522 ... 1535: 34308c2ecf20Sopenharmony_ci /* reserved region; don't read it */ 34318c2ecf20Sopenharmony_ci regbuf[i] = 0; 34328c2ecf20Sopenharmony_ci break; 34338c2ecf20Sopenharmony_ci default: 34348c2ecf20Sopenharmony_ci /* unreserved region */ 34358c2ecf20Sopenharmony_ci regbuf[i] = ioread32(hw->hw_addr + (i * sizeof(u32))); 34368c2ecf20Sopenharmony_ci } 34378c2ecf20Sopenharmony_ci } 34388c2ecf20Sopenharmony_ci} 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_cistatic void atl1_get_ringparam(struct net_device *netdev, 34418c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 34428c2ecf20Sopenharmony_ci{ 34438c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 34448c2ecf20Sopenharmony_ci struct atl1_tpd_ring *txdr = &adapter->tpd_ring; 34458c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rxdr = &adapter->rfd_ring; 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci ring->rx_max_pending = ATL1_MAX_RFD; 34488c2ecf20Sopenharmony_ci ring->tx_max_pending = ATL1_MAX_TPD; 34498c2ecf20Sopenharmony_ci ring->rx_pending = rxdr->count; 34508c2ecf20Sopenharmony_ci ring->tx_pending = txdr->count; 34518c2ecf20Sopenharmony_ci} 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_cistatic int atl1_set_ringparam(struct net_device *netdev, 34548c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 34558c2ecf20Sopenharmony_ci{ 34568c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 34578c2ecf20Sopenharmony_ci struct atl1_tpd_ring *tpdr = &adapter->tpd_ring; 34588c2ecf20Sopenharmony_ci struct atl1_rrd_ring *rrdr = &adapter->rrd_ring; 34598c2ecf20Sopenharmony_ci struct atl1_rfd_ring *rfdr = &adapter->rfd_ring; 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci struct atl1_tpd_ring tpd_old, tpd_new; 34628c2ecf20Sopenharmony_ci struct atl1_rfd_ring rfd_old, rfd_new; 34638c2ecf20Sopenharmony_ci struct atl1_rrd_ring rrd_old, rrd_new; 34648c2ecf20Sopenharmony_ci struct atl1_ring_header rhdr_old, rhdr_new; 34658c2ecf20Sopenharmony_ci struct atl1_smb smb; 34668c2ecf20Sopenharmony_ci struct atl1_cmb cmb; 34678c2ecf20Sopenharmony_ci int err; 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_ci tpd_old = adapter->tpd_ring; 34708c2ecf20Sopenharmony_ci rfd_old = adapter->rfd_ring; 34718c2ecf20Sopenharmony_ci rrd_old = adapter->rrd_ring; 34728c2ecf20Sopenharmony_ci rhdr_old = adapter->ring_header; 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) 34758c2ecf20Sopenharmony_ci atl1_down(adapter); 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD); 34788c2ecf20Sopenharmony_ci rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD : 34798c2ecf20Sopenharmony_ci rfdr->count; 34808c2ecf20Sopenharmony_ci rfdr->count = (rfdr->count + 3) & ~3; 34818c2ecf20Sopenharmony_ci rrdr->count = rfdr->count; 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_ci tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD); 34848c2ecf20Sopenharmony_ci tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD : 34858c2ecf20Sopenharmony_ci tpdr->count; 34868c2ecf20Sopenharmony_ci tpdr->count = (tpdr->count + 3) & ~3; 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { 34898c2ecf20Sopenharmony_ci /* try to get new resources before deleting old */ 34908c2ecf20Sopenharmony_ci err = atl1_setup_ring_resources(adapter); 34918c2ecf20Sopenharmony_ci if (err) 34928c2ecf20Sopenharmony_ci goto err_setup_ring; 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci /* 34958c2ecf20Sopenharmony_ci * save the new, restore the old in order to free it, 34968c2ecf20Sopenharmony_ci * then restore the new back again 34978c2ecf20Sopenharmony_ci */ 34988c2ecf20Sopenharmony_ci 34998c2ecf20Sopenharmony_ci rfd_new = adapter->rfd_ring; 35008c2ecf20Sopenharmony_ci rrd_new = adapter->rrd_ring; 35018c2ecf20Sopenharmony_ci tpd_new = adapter->tpd_ring; 35028c2ecf20Sopenharmony_ci rhdr_new = adapter->ring_header; 35038c2ecf20Sopenharmony_ci adapter->rfd_ring = rfd_old; 35048c2ecf20Sopenharmony_ci adapter->rrd_ring = rrd_old; 35058c2ecf20Sopenharmony_ci adapter->tpd_ring = tpd_old; 35068c2ecf20Sopenharmony_ci adapter->ring_header = rhdr_old; 35078c2ecf20Sopenharmony_ci /* 35088c2ecf20Sopenharmony_ci * Save SMB and CMB, since atl1_free_ring_resources 35098c2ecf20Sopenharmony_ci * will clear them. 35108c2ecf20Sopenharmony_ci */ 35118c2ecf20Sopenharmony_ci smb = adapter->smb; 35128c2ecf20Sopenharmony_ci cmb = adapter->cmb; 35138c2ecf20Sopenharmony_ci atl1_free_ring_resources(adapter); 35148c2ecf20Sopenharmony_ci adapter->rfd_ring = rfd_new; 35158c2ecf20Sopenharmony_ci adapter->rrd_ring = rrd_new; 35168c2ecf20Sopenharmony_ci adapter->tpd_ring = tpd_new; 35178c2ecf20Sopenharmony_ci adapter->ring_header = rhdr_new; 35188c2ecf20Sopenharmony_ci adapter->smb = smb; 35198c2ecf20Sopenharmony_ci adapter->cmb = cmb; 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci err = atl1_up(adapter); 35228c2ecf20Sopenharmony_ci if (err) 35238c2ecf20Sopenharmony_ci return err; 35248c2ecf20Sopenharmony_ci } 35258c2ecf20Sopenharmony_ci return 0; 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_cierr_setup_ring: 35288c2ecf20Sopenharmony_ci adapter->rfd_ring = rfd_old; 35298c2ecf20Sopenharmony_ci adapter->rrd_ring = rrd_old; 35308c2ecf20Sopenharmony_ci adapter->tpd_ring = tpd_old; 35318c2ecf20Sopenharmony_ci adapter->ring_header = rhdr_old; 35328c2ecf20Sopenharmony_ci atl1_up(adapter); 35338c2ecf20Sopenharmony_ci return err; 35348c2ecf20Sopenharmony_ci} 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_cistatic void atl1_get_pauseparam(struct net_device *netdev, 35378c2ecf20Sopenharmony_ci struct ethtool_pauseparam *epause) 35388c2ecf20Sopenharmony_ci{ 35398c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 35408c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 35438c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) { 35448c2ecf20Sopenharmony_ci epause->autoneg = AUTONEG_ENABLE; 35458c2ecf20Sopenharmony_ci } else { 35468c2ecf20Sopenharmony_ci epause->autoneg = AUTONEG_DISABLE; 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci epause->rx_pause = 1; 35498c2ecf20Sopenharmony_ci epause->tx_pause = 1; 35508c2ecf20Sopenharmony_ci} 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_cistatic int atl1_set_pauseparam(struct net_device *netdev, 35538c2ecf20Sopenharmony_ci struct ethtool_pauseparam *epause) 35548c2ecf20Sopenharmony_ci{ 35558c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 35568c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 35598c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) { 35608c2ecf20Sopenharmony_ci epause->autoneg = AUTONEG_ENABLE; 35618c2ecf20Sopenharmony_ci } else { 35628c2ecf20Sopenharmony_ci epause->autoneg = AUTONEG_DISABLE; 35638c2ecf20Sopenharmony_ci } 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci epause->rx_pause = 1; 35668c2ecf20Sopenharmony_ci epause->tx_pause = 1; 35678c2ecf20Sopenharmony_ci 35688c2ecf20Sopenharmony_ci return 0; 35698c2ecf20Sopenharmony_ci} 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_cistatic void atl1_get_strings(struct net_device *netdev, u32 stringset, 35728c2ecf20Sopenharmony_ci u8 *data) 35738c2ecf20Sopenharmony_ci{ 35748c2ecf20Sopenharmony_ci u8 *p = data; 35758c2ecf20Sopenharmony_ci int i; 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci switch (stringset) { 35788c2ecf20Sopenharmony_ci case ETH_SS_STATS: 35798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) { 35808c2ecf20Sopenharmony_ci memcpy(p, atl1_gstrings_stats[i].stat_string, 35818c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 35828c2ecf20Sopenharmony_ci p += ETH_GSTRING_LEN; 35838c2ecf20Sopenharmony_ci } 35848c2ecf20Sopenharmony_ci break; 35858c2ecf20Sopenharmony_ci } 35868c2ecf20Sopenharmony_ci} 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_cistatic int atl1_nway_reset(struct net_device *netdev) 35898c2ecf20Sopenharmony_ci{ 35908c2ecf20Sopenharmony_ci struct atl1_adapter *adapter = netdev_priv(netdev); 35918c2ecf20Sopenharmony_ci struct atl1_hw *hw = &adapter->hw; 35928c2ecf20Sopenharmony_ci 35938c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 35948c2ecf20Sopenharmony_ci u16 phy_data; 35958c2ecf20Sopenharmony_ci atl1_down(adapter); 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || 35988c2ecf20Sopenharmony_ci hw->media_type == MEDIA_TYPE_1000M_FULL) { 35998c2ecf20Sopenharmony_ci phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; 36008c2ecf20Sopenharmony_ci } else { 36018c2ecf20Sopenharmony_ci switch (hw->media_type) { 36028c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 36038c2ecf20Sopenharmony_ci phy_data = MII_CR_FULL_DUPLEX | 36048c2ecf20Sopenharmony_ci MII_CR_SPEED_100 | MII_CR_RESET; 36058c2ecf20Sopenharmony_ci break; 36068c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 36078c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_100 | MII_CR_RESET; 36088c2ecf20Sopenharmony_ci break; 36098c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 36108c2ecf20Sopenharmony_ci phy_data = MII_CR_FULL_DUPLEX | 36118c2ecf20Sopenharmony_ci MII_CR_SPEED_10 | MII_CR_RESET; 36128c2ecf20Sopenharmony_ci break; 36138c2ecf20Sopenharmony_ci default: 36148c2ecf20Sopenharmony_ci /* MEDIA_TYPE_10M_HALF */ 36158c2ecf20Sopenharmony_ci phy_data = MII_CR_SPEED_10 | MII_CR_RESET; 36168c2ecf20Sopenharmony_ci } 36178c2ecf20Sopenharmony_ci } 36188c2ecf20Sopenharmony_ci atl1_write_phy_reg(hw, MII_BMCR, phy_data); 36198c2ecf20Sopenharmony_ci atl1_up(adapter); 36208c2ecf20Sopenharmony_ci } 36218c2ecf20Sopenharmony_ci return 0; 36228c2ecf20Sopenharmony_ci} 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_cistatic const struct ethtool_ops atl1_ethtool_ops = { 36258c2ecf20Sopenharmony_ci .get_drvinfo = atl1_get_drvinfo, 36268c2ecf20Sopenharmony_ci .get_wol = atl1_get_wol, 36278c2ecf20Sopenharmony_ci .set_wol = atl1_set_wol, 36288c2ecf20Sopenharmony_ci .get_msglevel = atl1_get_msglevel, 36298c2ecf20Sopenharmony_ci .set_msglevel = atl1_set_msglevel, 36308c2ecf20Sopenharmony_ci .get_regs_len = atl1_get_regs_len, 36318c2ecf20Sopenharmony_ci .get_regs = atl1_get_regs, 36328c2ecf20Sopenharmony_ci .get_ringparam = atl1_get_ringparam, 36338c2ecf20Sopenharmony_ci .set_ringparam = atl1_set_ringparam, 36348c2ecf20Sopenharmony_ci .get_pauseparam = atl1_get_pauseparam, 36358c2ecf20Sopenharmony_ci .set_pauseparam = atl1_set_pauseparam, 36368c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 36378c2ecf20Sopenharmony_ci .get_strings = atl1_get_strings, 36388c2ecf20Sopenharmony_ci .nway_reset = atl1_nway_reset, 36398c2ecf20Sopenharmony_ci .get_ethtool_stats = atl1_get_ethtool_stats, 36408c2ecf20Sopenharmony_ci .get_sset_count = atl1_get_sset_count, 36418c2ecf20Sopenharmony_ci .get_link_ksettings = atl1_get_link_ksettings, 36428c2ecf20Sopenharmony_ci .set_link_ksettings = atl1_set_link_ksettings, 36438c2ecf20Sopenharmony_ci}; 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_cimodule_pci_driver(atl1_driver); 3646