18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2006 - 2007 Atheros Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright(c) 2007 - 2008 Chris Snook <csnook@redhat.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Derived from Intel e1000 driver 78c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/atomic.h> 118c2ecf20Sopenharmony_ci#include <linux/crc32.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 158c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 168c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 178c2ecf20Sopenharmony_ci#include <linux/in.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/ip.h> 208c2ecf20Sopenharmony_ci#include <linux/irqflags.h> 218c2ecf20Sopenharmony_ci#include <linux/irqreturn.h> 228c2ecf20Sopenharmony_ci#include <linux/mii.h> 238c2ecf20Sopenharmony_ci#include <linux/net.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/pci.h> 268c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 278c2ecf20Sopenharmony_ci#include <linux/pm.h> 288c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 318c2ecf20Sopenharmony_ci#include <linux/string.h> 328c2ecf20Sopenharmony_ci#include <linux/tcp.h> 338c2ecf20Sopenharmony_ci#include <linux/timer.h> 348c2ecf20Sopenharmony_ci#include <linux/types.h> 358c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "atl2.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const char atl2_driver_name[] = "atl2"; 408c2ecf20Sopenharmony_cistatic const struct ethtool_ops atl2_ethtool_ops; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Atheros Corporation <xiong.huang@atheros.com>, Chris Snook <csnook@redhat.com>"); 438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver"); 448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * atl2_pci_tbl - PCI Device ID Table 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistatic const struct pci_device_id atl2_pci_tbl[] = { 508c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)}, 518c2ecf20Sopenharmony_ci /* required last entry */ 528c2ecf20Sopenharmony_ci {0,} 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, atl2_pci_tbl); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void atl2_check_options(struct atl2_adapter *adapter); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * atl2_sw_init - Initialize general software structures (struct atl2_adapter) 608c2ecf20Sopenharmony_ci * @adapter: board private structure to initialize 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * atl2_sw_init initializes the Adapter private data structure. 638c2ecf20Sopenharmony_ci * Fields are initialized based on PCI device information and 648c2ecf20Sopenharmony_ci * OS network device settings (MTU size). 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic int atl2_sw_init(struct atl2_adapter *adapter) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 698c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* PCI config space info */ 728c2ecf20Sopenharmony_ci hw->vendor_id = pdev->vendor; 738c2ecf20Sopenharmony_ci hw->device_id = pdev->device; 748c2ecf20Sopenharmony_ci hw->subsystem_vendor_id = pdev->subsystem_vendor; 758c2ecf20Sopenharmony_ci hw->subsystem_id = pdev->subsystem_device; 768c2ecf20Sopenharmony_ci hw->revision_id = pdev->revision; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci adapter->wol = 0; 818c2ecf20Sopenharmony_ci adapter->ict = 50000; /* ~100ms */ 828c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; /* hardware init */ 838c2ecf20Sopenharmony_ci adapter->link_duplex = FULL_DUPLEX; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci hw->phy_configured = false; 868c2ecf20Sopenharmony_ci hw->preamble_len = 7; 878c2ecf20Sopenharmony_ci hw->ipgt = 0x60; 888c2ecf20Sopenharmony_ci hw->min_ifg = 0x50; 898c2ecf20Sopenharmony_ci hw->ipgr1 = 0x40; 908c2ecf20Sopenharmony_ci hw->ipgr2 = 0x60; 918c2ecf20Sopenharmony_ci hw->retry_buf = 2; 928c2ecf20Sopenharmony_ci hw->max_retry = 0xf; 938c2ecf20Sopenharmony_ci hw->lcol = 0x37; 948c2ecf20Sopenharmony_ci hw->jam_ipg = 7; 958c2ecf20Sopenharmony_ci hw->fc_rxd_hi = 0; 968c2ecf20Sopenharmony_ci hw->fc_rxd_lo = 0; 978c2ecf20Sopenharmony_ci hw->max_frame_size = adapter->netdev->mtu; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci spin_lock_init(&adapter->stats_lock); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci set_bit(__ATL2_DOWN, &adapter->flags); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/** 1078c2ecf20Sopenharmony_ci * atl2_set_multi - Multicast and Promiscuous mode set 1088c2ecf20Sopenharmony_ci * @netdev: network interface device structure 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * The set_multi entry point is called whenever the multicast address 1118c2ecf20Sopenharmony_ci * list or the network interface flags are updated. This routine is 1128c2ecf20Sopenharmony_ci * responsible for configuring the hardware for proper multicast, 1138c2ecf20Sopenharmony_ci * promiscuous mode, and all-multi behavior. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic void atl2_set_multi(struct net_device *netdev) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 1188c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 1198c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 1208c2ecf20Sopenharmony_ci u32 rctl; 1218c2ecf20Sopenharmony_ci u32 hash_value; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Check for Promiscuous and All Multicast modes */ 1248c2ecf20Sopenharmony_ci rctl = ATL2_READ_REG(hw, REG_MAC_CTRL); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 1278c2ecf20Sopenharmony_ci rctl |= MAC_CTRL_PROMIS_EN; 1288c2ecf20Sopenharmony_ci } else if (netdev->flags & IFF_ALLMULTI) { 1298c2ecf20Sopenharmony_ci rctl |= MAC_CTRL_MC_ALL_EN; 1308c2ecf20Sopenharmony_ci rctl &= ~MAC_CTRL_PROMIS_EN; 1318c2ecf20Sopenharmony_ci } else 1328c2ecf20Sopenharmony_ci rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* clear the old settings from the multicast hash table */ 1378c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); 1388c2ecf20Sopenharmony_ci ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* comoute mc addresses' hash value ,and put it into hash table */ 1418c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 1428c2ecf20Sopenharmony_ci hash_value = atl2_hash_mc_addr(hw, ha->addr); 1438c2ecf20Sopenharmony_ci atl2_hash_set(hw, hash_value); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void init_ring_ptrs(struct atl2_adapter *adapter) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci /* Read / Write Ptr Initialize: */ 1508c2ecf20Sopenharmony_ci adapter->txd_write_ptr = 0; 1518c2ecf20Sopenharmony_ci atomic_set(&adapter->txd_read_ptr, 0); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci adapter->rxd_read_ptr = 0; 1548c2ecf20Sopenharmony_ci adapter->rxd_write_ptr = 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci atomic_set(&adapter->txs_write_ptr, 0); 1578c2ecf20Sopenharmony_ci adapter->txs_next_clear = 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * atl2_configure - Configure Transmit&Receive Unit after Reset 1628c2ecf20Sopenharmony_ci * @adapter: board private structure 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Configure the Tx /Rx unit of the MAC after a reset. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic int atl2_configure(struct atl2_adapter *adapter) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 1698c2ecf20Sopenharmony_ci u32 value; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* clear interrupt status */ 1728c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* set MAC Address */ 1758c2ecf20Sopenharmony_ci value = (((u32)hw->mac_addr[2]) << 24) | 1768c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[3]) << 16) | 1778c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[4]) << 8) | 1788c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[5])); 1798c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value); 1808c2ecf20Sopenharmony_ci value = (((u32)hw->mac_addr[0]) << 8) | 1818c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[1])); 1828c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* HI base address */ 1858c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, 1868c2ecf20Sopenharmony_ci (u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* LO base address */ 1898c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO, 1908c2ecf20Sopenharmony_ci (u32)(adapter->txd_dma & 0x00000000ffffffffULL)); 1918c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO, 1928c2ecf20Sopenharmony_ci (u32)(adapter->txs_dma & 0x00000000ffffffffULL)); 1938c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO, 1948c2ecf20Sopenharmony_ci (u32)(adapter->rxd_dma & 0x00000000ffffffffULL)); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* element count */ 1978c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4)); 1988c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size); 1998c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM, (u16)adapter->rxd_ring_size); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* config Internal SRAM */ 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end); 2048c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end); 2058c2ecf20Sopenharmony_ci*/ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* config IPG/IFG */ 2088c2ecf20Sopenharmony_ci value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) << 2098c2ecf20Sopenharmony_ci MAC_IPG_IFG_IPGT_SHIFT) | 2108c2ecf20Sopenharmony_ci (((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) << 2118c2ecf20Sopenharmony_ci MAC_IPG_IFG_MIFG_SHIFT) | 2128c2ecf20Sopenharmony_ci (((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) << 2138c2ecf20Sopenharmony_ci MAC_IPG_IFG_IPGR1_SHIFT)| 2148c2ecf20Sopenharmony_ci (((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) << 2158c2ecf20Sopenharmony_ci MAC_IPG_IFG_IPGR2_SHIFT); 2168c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* config Half-Duplex Control */ 2198c2ecf20Sopenharmony_ci value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | 2208c2ecf20Sopenharmony_ci (((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) << 2218c2ecf20Sopenharmony_ci MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | 2228c2ecf20Sopenharmony_ci MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | 2238c2ecf20Sopenharmony_ci (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | 2248c2ecf20Sopenharmony_ci (((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) << 2258c2ecf20Sopenharmony_ci MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); 2268c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* set Interrupt Moderator Timer */ 2298c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt); 2308c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* set Interrupt Clear Timer */ 2338c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* set MTU */ 2368c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu + 2378c2ecf20Sopenharmony_ci ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* 1590 */ 2408c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* flow control */ 2438c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi); 2448c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Init mailbox */ 2478c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr); 2488c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* enable DMA read/write */ 2518c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN); 2528c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci value = ATL2_READ_REG(&adapter->hw, REG_ISR); 2558c2ecf20Sopenharmony_ci if ((value & ISR_PHY_LINKDOWN) != 0) 2568c2ecf20Sopenharmony_ci value = 1; /* config failed */ 2578c2ecf20Sopenharmony_ci else 2588c2ecf20Sopenharmony_ci value = 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* clear all interrupt status */ 2618c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff); 2628c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); 2638c2ecf20Sopenharmony_ci return value; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/** 2678c2ecf20Sopenharmony_ci * atl2_setup_ring_resources - allocate Tx / RX descriptor resources 2688c2ecf20Sopenharmony_ci * @adapter: board private structure 2698c2ecf20Sopenharmony_ci * 2708c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic s32 atl2_setup_ring_resources(struct atl2_adapter *adapter) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 2758c2ecf20Sopenharmony_ci int size; 2768c2ecf20Sopenharmony_ci u8 offset = 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* real ring DMA buffer */ 2798c2ecf20Sopenharmony_ci adapter->ring_size = size = 2808c2ecf20Sopenharmony_ci adapter->txd_ring_size * 1 + 7 + /* dword align */ 2818c2ecf20Sopenharmony_ci adapter->txs_ring_size * 4 + 7 + /* dword align */ 2828c2ecf20Sopenharmony_ci adapter->rxd_ring_size * 1536 + 127; /* 128bytes align */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci adapter->ring_vir_addr = dma_alloc_coherent(&pdev->dev, size, 2858c2ecf20Sopenharmony_ci &adapter->ring_dma, GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (!adapter->ring_vir_addr) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Init TXD Ring */ 2908c2ecf20Sopenharmony_ci adapter->txd_dma = adapter->ring_dma ; 2918c2ecf20Sopenharmony_ci offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0; 2928c2ecf20Sopenharmony_ci adapter->txd_dma += offset; 2938c2ecf20Sopenharmony_ci adapter->txd_ring = adapter->ring_vir_addr + offset; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Init TXS Ring */ 2968c2ecf20Sopenharmony_ci adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size; 2978c2ecf20Sopenharmony_ci offset = (adapter->txs_dma & 0x7) ? (8 - (adapter->txs_dma & 0x7)) : 0; 2988c2ecf20Sopenharmony_ci adapter->txs_dma += offset; 2998c2ecf20Sopenharmony_ci adapter->txs_ring = (struct tx_pkt_status *) 3008c2ecf20Sopenharmony_ci (((u8 *)adapter->txd_ring) + (adapter->txd_ring_size + offset)); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Init RXD Ring */ 3038c2ecf20Sopenharmony_ci adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size * 4; 3048c2ecf20Sopenharmony_ci offset = (adapter->rxd_dma & 127) ? 3058c2ecf20Sopenharmony_ci (128 - (adapter->rxd_dma & 127)) : 0; 3068c2ecf20Sopenharmony_ci if (offset > 7) 3078c2ecf20Sopenharmony_ci offset -= 8; 3088c2ecf20Sopenharmony_ci else 3098c2ecf20Sopenharmony_ci offset += (128 - 8); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci adapter->rxd_dma += offset; 3128c2ecf20Sopenharmony_ci adapter->rxd_ring = (struct rx_desc *) (((u8 *)adapter->txs_ring) + 3138c2ecf20Sopenharmony_ci (adapter->txs_ring_size * 4 + offset)); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* 3168c2ecf20Sopenharmony_ci * Read / Write Ptr Initialize: 3178c2ecf20Sopenharmony_ci * init_ring_ptrs(adapter); 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/** 3238c2ecf20Sopenharmony_ci * atl2_irq_enable - Enable default interrupt generation settings 3248c2ecf20Sopenharmony_ci * @adapter: board private structure 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_cistatic inline void atl2_irq_enable(struct atl2_adapter *adapter) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); 3298c2ecf20Sopenharmony_ci ATL2_WRITE_FLUSH(&adapter->hw); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/** 3338c2ecf20Sopenharmony_ci * atl2_irq_disable - Mask off interrupt generation on the NIC 3348c2ecf20Sopenharmony_ci * @adapter: board private structure 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic inline void atl2_irq_disable(struct atl2_adapter *adapter) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0); 3398c2ecf20Sopenharmony_ci ATL2_WRITE_FLUSH(&adapter->hw); 3408c2ecf20Sopenharmony_ci synchronize_irq(adapter->pdev->irq); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void __atl2_vlan_mode(netdev_features_t features, u32 *ctrl) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) { 3468c2ecf20Sopenharmony_ci /* enable VLAN tag insert/strip */ 3478c2ecf20Sopenharmony_ci *ctrl |= MAC_CTRL_RMV_VLAN; 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci /* disable VLAN tag insert/strip */ 3508c2ecf20Sopenharmony_ci *ctrl &= ~MAC_CTRL_RMV_VLAN; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void atl2_vlan_mode(struct net_device *netdev, 3558c2ecf20Sopenharmony_ci netdev_features_t features) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 3588c2ecf20Sopenharmony_ci u32 ctrl; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci atl2_irq_disable(adapter); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL); 3638c2ecf20Sopenharmony_ci __atl2_vlan_mode(features, &ctrl); 3648c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci atl2_irq_enable(adapter); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void atl2_restore_vlan(struct atl2_adapter *adapter) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci atl2_vlan_mode(adapter->netdev, adapter->netdev->features); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic netdev_features_t atl2_fix_features(struct net_device *netdev, 3758c2ecf20Sopenharmony_ci netdev_features_t features) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci /* 3788c2ecf20Sopenharmony_ci * Since there is no support for separate rx/tx vlan accel 3798c2ecf20Sopenharmony_ci * enable/disable make sure tx flag is always in same state as rx. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 3828c2ecf20Sopenharmony_ci features |= NETIF_F_HW_VLAN_CTAG_TX; 3838c2ecf20Sopenharmony_ci else 3848c2ecf20Sopenharmony_ci features &= ~NETIF_F_HW_VLAN_CTAG_TX; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return features; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int atl2_set_features(struct net_device *netdev, 3908c2ecf20Sopenharmony_ci netdev_features_t features) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci netdev_features_t changed = netdev->features ^ features; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_RX) 3958c2ecf20Sopenharmony_ci atl2_vlan_mode(netdev, features); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void atl2_intr_rx(struct atl2_adapter *adapter) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 4038c2ecf20Sopenharmony_ci struct rx_desc *rxd; 4048c2ecf20Sopenharmony_ci struct sk_buff *skb; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci do { 4078c2ecf20Sopenharmony_ci rxd = adapter->rxd_ring+adapter->rxd_write_ptr; 4088c2ecf20Sopenharmony_ci if (!rxd->status.update) 4098c2ecf20Sopenharmony_ci break; /* end of tx */ 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* clear this flag at once */ 4128c2ecf20Sopenharmony_ci rxd->status.update = 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (rxd->status.ok && rxd->status.pkt_size >= 60) { 4158c2ecf20Sopenharmony_ci int rx_size = (int)(rxd->status.pkt_size - 4); 4168c2ecf20Sopenharmony_ci /* alloc new buffer */ 4178c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, rx_size); 4188c2ecf20Sopenharmony_ci if (NULL == skb) { 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * Check that some rx space is free. If not, 4218c2ecf20Sopenharmony_ci * free one and mark stats->rx_dropped++. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci netdev->stats.rx_dropped++; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci memcpy(skb->data, rxd->packet, rx_size); 4278c2ecf20Sopenharmony_ci skb_put(skb, rx_size); 4288c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 4298c2ecf20Sopenharmony_ci if (rxd->status.vlan) { 4308c2ecf20Sopenharmony_ci u16 vlan_tag = (rxd->status.vtag>>4) | 4318c2ecf20Sopenharmony_ci ((rxd->status.vtag&7) << 13) | 4328c2ecf20Sopenharmony_ci ((rxd->status.vtag&8) << 9); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci netif_rx(skb); 4378c2ecf20Sopenharmony_ci netdev->stats.rx_bytes += rx_size; 4388c2ecf20Sopenharmony_ci netdev->stats.rx_packets++; 4398c2ecf20Sopenharmony_ci } else { 4408c2ecf20Sopenharmony_ci netdev->stats.rx_errors++; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (rxd->status.ok && rxd->status.pkt_size <= 60) 4438c2ecf20Sopenharmony_ci netdev->stats.rx_length_errors++; 4448c2ecf20Sopenharmony_ci if (rxd->status.mcast) 4458c2ecf20Sopenharmony_ci netdev->stats.multicast++; 4468c2ecf20Sopenharmony_ci if (rxd->status.crc) 4478c2ecf20Sopenharmony_ci netdev->stats.rx_crc_errors++; 4488c2ecf20Sopenharmony_ci if (rxd->status.align) 4498c2ecf20Sopenharmony_ci netdev->stats.rx_frame_errors++; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* advance write ptr */ 4538c2ecf20Sopenharmony_ci if (++adapter->rxd_write_ptr == adapter->rxd_ring_size) 4548c2ecf20Sopenharmony_ci adapter->rxd_write_ptr = 0; 4558c2ecf20Sopenharmony_ci } while (1); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* update mailbox? */ 4588c2ecf20Sopenharmony_ci adapter->rxd_read_ptr = adapter->rxd_write_ptr; 4598c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void atl2_intr_tx(struct atl2_adapter *adapter) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 4658c2ecf20Sopenharmony_ci u32 txd_read_ptr; 4668c2ecf20Sopenharmony_ci u32 txs_write_ptr; 4678c2ecf20Sopenharmony_ci struct tx_pkt_status *txs; 4688c2ecf20Sopenharmony_ci struct tx_pkt_header *txph; 4698c2ecf20Sopenharmony_ci int free_hole = 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci do { 4728c2ecf20Sopenharmony_ci txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); 4738c2ecf20Sopenharmony_ci txs = adapter->txs_ring + txs_write_ptr; 4748c2ecf20Sopenharmony_ci if (!txs->update) 4758c2ecf20Sopenharmony_ci break; /* tx stop here */ 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci free_hole = 1; 4788c2ecf20Sopenharmony_ci txs->update = 0; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (++txs_write_ptr == adapter->txs_ring_size) 4818c2ecf20Sopenharmony_ci txs_write_ptr = 0; 4828c2ecf20Sopenharmony_ci atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr); 4858c2ecf20Sopenharmony_ci txph = (struct tx_pkt_header *) 4868c2ecf20Sopenharmony_ci (((u8 *)adapter->txd_ring) + txd_read_ptr); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (txph->pkt_size != txs->pkt_size) { 4898c2ecf20Sopenharmony_ci struct tx_pkt_status *old_txs = txs; 4908c2ecf20Sopenharmony_ci printk(KERN_WARNING 4918c2ecf20Sopenharmony_ci "%s: txs packet size not consistent with txd" 4928c2ecf20Sopenharmony_ci " txd_:0x%08x, txs_:0x%08x!\n", 4938c2ecf20Sopenharmony_ci adapter->netdev->name, 4948c2ecf20Sopenharmony_ci *(u32 *)txph, *(u32 *)txs); 4958c2ecf20Sopenharmony_ci printk(KERN_WARNING 4968c2ecf20Sopenharmony_ci "txd read ptr: 0x%x\n", 4978c2ecf20Sopenharmony_ci txd_read_ptr); 4988c2ecf20Sopenharmony_ci txs = adapter->txs_ring + txs_write_ptr; 4998c2ecf20Sopenharmony_ci printk(KERN_WARNING 5008c2ecf20Sopenharmony_ci "txs-behind:0x%08x\n", 5018c2ecf20Sopenharmony_ci *(u32 *)txs); 5028c2ecf20Sopenharmony_ci if (txs_write_ptr < 2) { 5038c2ecf20Sopenharmony_ci txs = adapter->txs_ring + 5048c2ecf20Sopenharmony_ci (adapter->txs_ring_size + 5058c2ecf20Sopenharmony_ci txs_write_ptr - 2); 5068c2ecf20Sopenharmony_ci } else { 5078c2ecf20Sopenharmony_ci txs = adapter->txs_ring + (txs_write_ptr - 2); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci printk(KERN_WARNING 5108c2ecf20Sopenharmony_ci "txs-before:0x%08x\n", 5118c2ecf20Sopenharmony_ci *(u32 *)txs); 5128c2ecf20Sopenharmony_ci txs = old_txs; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 4for TPH */ 5168c2ecf20Sopenharmony_ci txd_read_ptr += (((u32)(txph->pkt_size) + 7) & ~3); 5178c2ecf20Sopenharmony_ci if (txd_read_ptr >= adapter->txd_ring_size) 5188c2ecf20Sopenharmony_ci txd_read_ptr -= adapter->txd_ring_size; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* tx statistics: */ 5238c2ecf20Sopenharmony_ci if (txs->ok) { 5248c2ecf20Sopenharmony_ci netdev->stats.tx_bytes += txs->pkt_size; 5258c2ecf20Sopenharmony_ci netdev->stats.tx_packets++; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci else 5288c2ecf20Sopenharmony_ci netdev->stats.tx_errors++; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (txs->defer) 5318c2ecf20Sopenharmony_ci netdev->stats.collisions++; 5328c2ecf20Sopenharmony_ci if (txs->abort_col) 5338c2ecf20Sopenharmony_ci netdev->stats.tx_aborted_errors++; 5348c2ecf20Sopenharmony_ci if (txs->late_col) 5358c2ecf20Sopenharmony_ci netdev->stats.tx_window_errors++; 5368c2ecf20Sopenharmony_ci if (txs->underrun) 5378c2ecf20Sopenharmony_ci netdev->stats.tx_fifo_errors++; 5388c2ecf20Sopenharmony_ci } while (1); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (free_hole) { 5418c2ecf20Sopenharmony_ci if (netif_queue_stopped(adapter->netdev) && 5428c2ecf20Sopenharmony_ci netif_carrier_ok(adapter->netdev)) 5438c2ecf20Sopenharmony_ci netif_wake_queue(adapter->netdev); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void atl2_check_for_link(struct atl2_adapter *adapter) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 5508c2ecf20Sopenharmony_ci u16 phy_data = 0; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci spin_lock(&adapter->stats_lock); 5538c2ecf20Sopenharmony_ci atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); 5548c2ecf20Sopenharmony_ci atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); 5558c2ecf20Sopenharmony_ci spin_unlock(&adapter->stats_lock); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* notify upper layer link down ASAP */ 5588c2ecf20Sopenharmony_ci if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */ 5598c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { /* old link state: Up */ 5608c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %s NIC Link is Down\n", 5618c2ecf20Sopenharmony_ci atl2_driver_name, netdev->name); 5628c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 5638c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 5648c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci schedule_work(&adapter->link_chg_task); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic inline void atl2_clear_phy_int(struct atl2_adapter *adapter) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci u16 phy_data; 5738c2ecf20Sopenharmony_ci spin_lock(&adapter->stats_lock); 5748c2ecf20Sopenharmony_ci atl2_read_phy_reg(&adapter->hw, 19, &phy_data); 5758c2ecf20Sopenharmony_ci spin_unlock(&adapter->stats_lock); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/** 5798c2ecf20Sopenharmony_ci * atl2_intr - Interrupt Handler 5808c2ecf20Sopenharmony_ci * @irq: interrupt number 5818c2ecf20Sopenharmony_ci * @data: pointer to a network interface device structure 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic irqreturn_t atl2_intr(int irq, void *data) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(data); 5868c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 5878c2ecf20Sopenharmony_ci u32 status; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci status = ATL2_READ_REG(hw, REG_ISR); 5908c2ecf20Sopenharmony_ci if (0 == status) 5918c2ecf20Sopenharmony_ci return IRQ_NONE; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* link event */ 5948c2ecf20Sopenharmony_ci if (status & ISR_PHY) 5958c2ecf20Sopenharmony_ci atl2_clear_phy_int(adapter); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* clear ISR status, and Enable CMB DMA/Disable Interrupt */ 5988c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* check if PCIE PHY Link down */ 6018c2ecf20Sopenharmony_ci if (status & ISR_PHY_LINKDOWN) { 6028c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { /* reset MAC */ 6038c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_ISR, 0); 6048c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_IMR, 0); 6058c2ecf20Sopenharmony_ci ATL2_WRITE_FLUSH(hw); 6068c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 6078c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* check if DMA read/write error? */ 6128c2ecf20Sopenharmony_ci if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { 6138c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_ISR, 0); 6148c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_IMR, 0); 6158c2ecf20Sopenharmony_ci ATL2_WRITE_FLUSH(hw); 6168c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 6178c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* link event */ 6218c2ecf20Sopenharmony_ci if (status & (ISR_PHY | ISR_MANUAL)) { 6228c2ecf20Sopenharmony_ci adapter->netdev->stats.tx_carrier_errors++; 6238c2ecf20Sopenharmony_ci atl2_check_for_link(adapter); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* transmit event */ 6278c2ecf20Sopenharmony_ci if (status & ISR_TX_EVENT) 6288c2ecf20Sopenharmony_ci atl2_intr_tx(adapter); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* rx exception */ 6318c2ecf20Sopenharmony_ci if (status & ISR_RX_EVENT) 6328c2ecf20Sopenharmony_ci atl2_intr_rx(adapter); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* re-enable Interrupt */ 6358c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); 6368c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int atl2_request_irq(struct atl2_adapter *adapter) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 6428c2ecf20Sopenharmony_ci int flags, err = 0; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci flags = IRQF_SHARED; 6458c2ecf20Sopenharmony_ci adapter->have_msi = true; 6468c2ecf20Sopenharmony_ci err = pci_enable_msi(adapter->pdev); 6478c2ecf20Sopenharmony_ci if (err) 6488c2ecf20Sopenharmony_ci adapter->have_msi = false; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (adapter->have_msi) 6518c2ecf20Sopenharmony_ci flags &= ~IRQF_SHARED; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name, 6548c2ecf20Sopenharmony_ci netdev); 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/** 6588c2ecf20Sopenharmony_ci * atl2_free_ring_resources - Free Tx / RX descriptor Resources 6598c2ecf20Sopenharmony_ci * @adapter: board private structure 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * Free all transmit software resources 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_cistatic void atl2_free_ring_resources(struct atl2_adapter *adapter) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 6668c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, adapter->ring_size, 6678c2ecf20Sopenharmony_ci adapter->ring_vir_addr, adapter->ring_dma); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/** 6718c2ecf20Sopenharmony_ci * atl2_open - Called when a network interface is made active 6728c2ecf20Sopenharmony_ci * @netdev: network interface device structure 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure 6758c2ecf20Sopenharmony_ci * 6768c2ecf20Sopenharmony_ci * The open entry point is called when a network interface is made 6778c2ecf20Sopenharmony_ci * active by the system (IFF_UP). At this point all resources needed 6788c2ecf20Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt 6798c2ecf20Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started, 6808c2ecf20Sopenharmony_ci * and the stack is notified that the interface is ready. 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_cistatic int atl2_open(struct net_device *netdev) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 6858c2ecf20Sopenharmony_ci int err; 6868c2ecf20Sopenharmony_ci u32 val; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* disallow open during test */ 6898c2ecf20Sopenharmony_ci if (test_bit(__ATL2_TESTING, &adapter->flags)) 6908c2ecf20Sopenharmony_ci return -EBUSY; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* allocate transmit descriptors */ 6938c2ecf20Sopenharmony_ci err = atl2_setup_ring_resources(adapter); 6948c2ecf20Sopenharmony_ci if (err) 6958c2ecf20Sopenharmony_ci return err; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci err = atl2_init_hw(&adapter->hw); 6988c2ecf20Sopenharmony_ci if (err) { 6998c2ecf20Sopenharmony_ci err = -EIO; 7008c2ecf20Sopenharmony_ci goto err_init_hw; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* hardware has been reset, we need to reload some things */ 7048c2ecf20Sopenharmony_ci atl2_set_multi(netdev); 7058c2ecf20Sopenharmony_ci init_ring_ptrs(adapter); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci atl2_restore_vlan(adapter); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (atl2_configure(adapter)) { 7108c2ecf20Sopenharmony_ci err = -EIO; 7118c2ecf20Sopenharmony_ci goto err_config; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci err = atl2_request_irq(adapter); 7158c2ecf20Sopenharmony_ci if (err) 7168c2ecf20Sopenharmony_ci goto err_req_irq; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci clear_bit(__ATL2_DOWN, &adapter->flags); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4*HZ)); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); 7238c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, 7248c2ecf20Sopenharmony_ci val | MASTER_CTRL_MANUAL_INT); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci atl2_irq_enable(adapter); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return 0; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cierr_init_hw: 7318c2ecf20Sopenharmony_cierr_req_irq: 7328c2ecf20Sopenharmony_cierr_config: 7338c2ecf20Sopenharmony_ci atl2_free_ring_resources(adapter); 7348c2ecf20Sopenharmony_ci atl2_reset_hw(&adapter->hw); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return err; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic void atl2_down(struct atl2_adapter *adapter) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* signal that we're down so the interrupt handler does not 7448c2ecf20Sopenharmony_ci * reschedule our watchdog timer */ 7458c2ecf20Sopenharmony_ci set_bit(__ATL2_DOWN, &adapter->flags); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci netif_tx_disable(netdev); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* reset MAC to disable all RX/TX */ 7508c2ecf20Sopenharmony_ci atl2_reset_hw(&adapter->hw); 7518c2ecf20Sopenharmony_ci msleep(1); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci atl2_irq_disable(adapter); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci del_timer_sync(&adapter->watchdog_timer); 7568c2ecf20Sopenharmony_ci del_timer_sync(&adapter->phy_config_timer); 7578c2ecf20Sopenharmony_ci clear_bit(0, &adapter->cfg_phy); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 7608c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 7618c2ecf20Sopenharmony_ci adapter->link_duplex = -1; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic void atl2_free_irq(struct atl2_adapter *adapter) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci free_irq(adapter->pdev->irq, netdev); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 7718c2ecf20Sopenharmony_ci if (adapter->have_msi) 7728c2ecf20Sopenharmony_ci pci_disable_msi(adapter->pdev); 7738c2ecf20Sopenharmony_ci#endif 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/** 7778c2ecf20Sopenharmony_ci * atl2_close - Disables a network interface 7788c2ecf20Sopenharmony_ci * @netdev: network interface device structure 7798c2ecf20Sopenharmony_ci * 7808c2ecf20Sopenharmony_ci * Returns 0, this is not allowed to fail 7818c2ecf20Sopenharmony_ci * 7828c2ecf20Sopenharmony_ci * The close entry point is called when an interface is de-activated 7838c2ecf20Sopenharmony_ci * by the OS. The hardware is still under the drivers control, but 7848c2ecf20Sopenharmony_ci * needs to be disabled. A global MAC reset is issued to stop the 7858c2ecf20Sopenharmony_ci * hardware, and all transmit and receive resources are freed. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_cistatic int atl2_close(struct net_device *netdev) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci atl2_down(adapter); 7948c2ecf20Sopenharmony_ci atl2_free_irq(adapter); 7958c2ecf20Sopenharmony_ci atl2_free_ring_resources(adapter); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return 0; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic inline int TxsFreeUnit(struct atl2_adapter *adapter) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return (adapter->txs_next_clear >= txs_write_ptr) ? 8058c2ecf20Sopenharmony_ci (int) (adapter->txs_ring_size - adapter->txs_next_clear + 8068c2ecf20Sopenharmony_ci txs_write_ptr - 1) : 8078c2ecf20Sopenharmony_ci (int) (txs_write_ptr - adapter->txs_next_clear - 1); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic inline int TxdFreeBytes(struct atl2_adapter *adapter) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return (adapter->txd_write_ptr >= txd_read_ptr) ? 8158c2ecf20Sopenharmony_ci (int) (adapter->txd_ring_size - adapter->txd_write_ptr + 8168c2ecf20Sopenharmony_ci txd_read_ptr - 1) : 8178c2ecf20Sopenharmony_ci (int) (txd_read_ptr - adapter->txd_write_ptr - 1); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic netdev_tx_t atl2_xmit_frame(struct sk_buff *skb, 8218c2ecf20Sopenharmony_ci struct net_device *netdev) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 8248c2ecf20Sopenharmony_ci struct tx_pkt_header *txph; 8258c2ecf20Sopenharmony_ci u32 offset, copy_len; 8268c2ecf20Sopenharmony_ci int txs_unused; 8278c2ecf20Sopenharmony_ci int txbuf_unused; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (test_bit(__ATL2_DOWN, &adapter->flags)) { 8308c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8318c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (unlikely(skb->len <= 0)) { 8358c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8368c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci txs_unused = TxsFreeUnit(adapter); 8408c2ecf20Sopenharmony_ci txbuf_unused = TxdFreeBytes(adapter); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (skb->len + sizeof(struct tx_pkt_header) + 4 > txbuf_unused || 8438c2ecf20Sopenharmony_ci txs_unused < 1) { 8448c2ecf20Sopenharmony_ci /* not enough resources */ 8458c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 8468c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci offset = adapter->txd_write_ptr; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + offset); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci *(u32 *)txph = 0; 8548c2ecf20Sopenharmony_ci txph->pkt_size = skb->len; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci offset += 4; 8578c2ecf20Sopenharmony_ci if (offset >= adapter->txd_ring_size) 8588c2ecf20Sopenharmony_ci offset -= adapter->txd_ring_size; 8598c2ecf20Sopenharmony_ci copy_len = adapter->txd_ring_size - offset; 8608c2ecf20Sopenharmony_ci if (copy_len >= skb->len) { 8618c2ecf20Sopenharmony_ci memcpy(((u8 *)adapter->txd_ring) + offset, skb->data, skb->len); 8628c2ecf20Sopenharmony_ci offset += ((u32)(skb->len + 3) & ~3); 8638c2ecf20Sopenharmony_ci } else { 8648c2ecf20Sopenharmony_ci memcpy(((u8 *)adapter->txd_ring)+offset, skb->data, copy_len); 8658c2ecf20Sopenharmony_ci memcpy((u8 *)adapter->txd_ring, skb->data+copy_len, 8668c2ecf20Sopenharmony_ci skb->len-copy_len); 8678c2ecf20Sopenharmony_ci offset = ((u32)(skb->len-copy_len + 3) & ~3); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci#ifdef NETIF_F_HW_VLAN_CTAG_TX 8708c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 8718c2ecf20Sopenharmony_ci u16 vlan_tag = skb_vlan_tag_get(skb); 8728c2ecf20Sopenharmony_ci vlan_tag = (vlan_tag << 4) | 8738c2ecf20Sopenharmony_ci (vlan_tag >> 13) | 8748c2ecf20Sopenharmony_ci ((vlan_tag >> 9) & 0x8); 8758c2ecf20Sopenharmony_ci txph->ins_vlan = 1; 8768c2ecf20Sopenharmony_ci txph->vlan = vlan_tag; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci#endif 8798c2ecf20Sopenharmony_ci if (offset >= adapter->txd_ring_size) 8808c2ecf20Sopenharmony_ci offset -= adapter->txd_ring_size; 8818c2ecf20Sopenharmony_ci adapter->txd_write_ptr = offset; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* clear txs before send */ 8848c2ecf20Sopenharmony_ci adapter->txs_ring[adapter->txs_next_clear].update = 0; 8858c2ecf20Sopenharmony_ci if (++adapter->txs_next_clear == adapter->txs_ring_size) 8868c2ecf20Sopenharmony_ci adapter->txs_next_clear = 0; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX, 8898c2ecf20Sopenharmony_ci (adapter->txd_write_ptr >> 2)); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 8928c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci/** 8968c2ecf20Sopenharmony_ci * atl2_change_mtu - Change the Maximum Transfer Unit 8978c2ecf20Sopenharmony_ci * @netdev: network interface device structure 8988c2ecf20Sopenharmony_ci * @new_mtu: new value for maximum frame size 8998c2ecf20Sopenharmony_ci * 9008c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_cistatic int atl2_change_mtu(struct net_device *netdev, int new_mtu) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 9058c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* set MTU */ 9088c2ecf20Sopenharmony_ci netdev->mtu = new_mtu; 9098c2ecf20Sopenharmony_ci hw->max_frame_size = new_mtu; 9108c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ETH_HLEN + 9118c2ecf20Sopenharmony_ci VLAN_HLEN + ETH_FCS_LEN); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/** 9178c2ecf20Sopenharmony_ci * atl2_set_mac - Change the Ethernet Address of the NIC 9188c2ecf20Sopenharmony_ci * @netdev: network interface device structure 9198c2ecf20Sopenharmony_ci * @p: pointer to an address structure 9208c2ecf20Sopenharmony_ci * 9218c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_cistatic int atl2_set_mac(struct net_device *netdev, void *p) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 9268c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 9298c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (netif_running(netdev)) 9328c2ecf20Sopenharmony_ci return -EBUSY; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); 9358c2ecf20Sopenharmony_ci memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci atl2_set_mac_addr(&adapter->hw); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci return 0; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic int atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 9458c2ecf20Sopenharmony_ci struct mii_ioctl_data *data = if_mii(ifr); 9468c2ecf20Sopenharmony_ci unsigned long flags; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci switch (cmd) { 9498c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 9508c2ecf20Sopenharmony_ci data->phy_id = 0; 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci case SIOCGMIIREG: 9538c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->stats_lock, flags); 9548c2ecf20Sopenharmony_ci if (atl2_read_phy_reg(&adapter->hw, 9558c2ecf20Sopenharmony_ci data->reg_num & 0x1F, &data->val_out)) { 9568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 9578c2ecf20Sopenharmony_ci return -EIO; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 9608c2ecf20Sopenharmony_ci break; 9618c2ecf20Sopenharmony_ci case SIOCSMIIREG: 9628c2ecf20Sopenharmony_ci if (data->reg_num & ~(0x1F)) 9638c2ecf20Sopenharmony_ci return -EFAULT; 9648c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->stats_lock, flags); 9658c2ecf20Sopenharmony_ci if (atl2_write_phy_reg(&adapter->hw, data->reg_num, 9668c2ecf20Sopenharmony_ci data->val_in)) { 9678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 9688c2ecf20Sopenharmony_ci return -EIO; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci default: 9738c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci return 0; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic int atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci switch (cmd) { 9818c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 9828c2ecf20Sopenharmony_ci case SIOCGMIIREG: 9838c2ecf20Sopenharmony_ci case SIOCSMIIREG: 9848c2ecf20Sopenharmony_ci return atl2_mii_ioctl(netdev, ifr, cmd); 9858c2ecf20Sopenharmony_ci#ifdef ETHTOOL_OPS_COMPAT 9868c2ecf20Sopenharmony_ci case SIOCETHTOOL: 9878c2ecf20Sopenharmony_ci return ethtool_ioctl(ifr); 9888c2ecf20Sopenharmony_ci#endif 9898c2ecf20Sopenharmony_ci default: 9908c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/** 9958c2ecf20Sopenharmony_ci * atl2_tx_timeout - Respond to a Tx Hang 9968c2ecf20Sopenharmony_ci * @netdev: network interface device structure 9978c2ecf20Sopenharmony_ci * @txqueue: index of the hanging transmit queue 9988c2ecf20Sopenharmony_ci */ 9998c2ecf20Sopenharmony_cistatic void atl2_tx_timeout(struct net_device *netdev, unsigned int txqueue) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* Do the reset outside of interrupt context */ 10048c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci/** 10088c2ecf20Sopenharmony_ci * atl2_watchdog - Timer Call-back 10098c2ecf20Sopenharmony_ci * @t: timer list containing a pointer to netdev cast into an unsigned long 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_cistatic void atl2_watchdog(struct timer_list *t) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = from_timer(adapter, t, watchdog_timer); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (!test_bit(__ATL2_DOWN, &adapter->flags)) { 10168c2ecf20Sopenharmony_ci u32 drop_rxd, drop_rxs; 10178c2ecf20Sopenharmony_ci unsigned long flags; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->stats_lock, flags); 10208c2ecf20Sopenharmony_ci drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV); 10218c2ecf20Sopenharmony_ci drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV); 10228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci adapter->netdev->stats.rx_over_errors += drop_rxd + drop_rxs; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Reset the timer */ 10278c2ecf20Sopenharmony_ci mod_timer(&adapter->watchdog_timer, 10288c2ecf20Sopenharmony_ci round_jiffies(jiffies + 4 * HZ)); 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci/** 10338c2ecf20Sopenharmony_ci * atl2_phy_config - Timer Call-back 10348c2ecf20Sopenharmony_ci * @t: timer list containing a pointer to netdev cast into an unsigned long 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_cistatic void atl2_phy_config(struct timer_list *t) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = from_timer(adapter, t, 10398c2ecf20Sopenharmony_ci phy_config_timer); 10408c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 10418c2ecf20Sopenharmony_ci unsigned long flags; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->stats_lock, flags); 10448c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); 10458c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN | 10468c2ecf20Sopenharmony_ci MII_CR_RESTART_AUTO_NEG); 10478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 10488c2ecf20Sopenharmony_ci clear_bit(0, &adapter->cfg_phy); 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic int atl2_up(struct atl2_adapter *adapter) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 10548c2ecf20Sopenharmony_ci int err = 0; 10558c2ecf20Sopenharmony_ci u32 val; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* hardware has been reset, we need to reload some things */ 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci err = atl2_init_hw(&adapter->hw); 10608c2ecf20Sopenharmony_ci if (err) { 10618c2ecf20Sopenharmony_ci err = -EIO; 10628c2ecf20Sopenharmony_ci return err; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci atl2_set_multi(netdev); 10668c2ecf20Sopenharmony_ci init_ring_ptrs(adapter); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci atl2_restore_vlan(adapter); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci if (atl2_configure(adapter)) { 10718c2ecf20Sopenharmony_ci err = -EIO; 10728c2ecf20Sopenharmony_ci goto err_up; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci clear_bit(__ATL2_DOWN, &adapter->flags); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); 10788c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | 10798c2ecf20Sopenharmony_ci MASTER_CTRL_MANUAL_INT); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci atl2_irq_enable(adapter); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cierr_up: 10848c2ecf20Sopenharmony_ci return err; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic void atl2_reinit_locked(struct atl2_adapter *adapter) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) 10908c2ecf20Sopenharmony_ci msleep(1); 10918c2ecf20Sopenharmony_ci atl2_down(adapter); 10928c2ecf20Sopenharmony_ci atl2_up(adapter); 10938c2ecf20Sopenharmony_ci clear_bit(__ATL2_RESETTING, &adapter->flags); 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void atl2_reset_task(struct work_struct *work) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct atl2_adapter *adapter; 10998c2ecf20Sopenharmony_ci adapter = container_of(work, struct atl2_adapter, reset_task); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci atl2_reinit_locked(adapter); 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic void atl2_setup_mac_ctrl(struct atl2_adapter *adapter) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci u32 value; 11078c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 11088c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* Config MAC CTRL Register */ 11118c2ecf20Sopenharmony_ci value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* duplex */ 11148c2ecf20Sopenharmony_ci if (FULL_DUPLEX == adapter->link_duplex) 11158c2ecf20Sopenharmony_ci value |= MAC_CTRL_DUPLX; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* flow control */ 11188c2ecf20Sopenharmony_ci value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* PAD & CRC */ 11218c2ecf20Sopenharmony_ci value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci /* preamble length */ 11248c2ecf20Sopenharmony_ci value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << 11258c2ecf20Sopenharmony_ci MAC_CTRL_PRMLEN_SHIFT); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* vlan */ 11288c2ecf20Sopenharmony_ci __atl2_vlan_mode(netdev->features, &value); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* filter mode */ 11318c2ecf20Sopenharmony_ci value |= MAC_CTRL_BC_EN; 11328c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 11338c2ecf20Sopenharmony_ci value |= MAC_CTRL_PROMIS_EN; 11348c2ecf20Sopenharmony_ci else if (netdev->flags & IFF_ALLMULTI) 11358c2ecf20Sopenharmony_ci value |= MAC_CTRL_MC_ALL_EN; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* half retry buffer */ 11388c2ecf20Sopenharmony_ci value |= (((u32)(adapter->hw.retry_buf & 11398c2ecf20Sopenharmony_ci MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int atl2_check_link(struct atl2_adapter *adapter) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 11478c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 11488c2ecf20Sopenharmony_ci int ret_val; 11498c2ecf20Sopenharmony_ci u16 speed, duplex, phy_data; 11508c2ecf20Sopenharmony_ci int reconfig = 0; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* MII_BMSR must read twise */ 11538c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_BMSR, &phy_data); 11548c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_BMSR, &phy_data); 11558c2ecf20Sopenharmony_ci if (!(phy_data&BMSR_LSTATUS)) { /* link down */ 11568c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { /* old link state: Up */ 11578c2ecf20Sopenharmony_ci u32 value; 11588c2ecf20Sopenharmony_ci /* disable rx */ 11598c2ecf20Sopenharmony_ci value = ATL2_READ_REG(hw, REG_MAC_CTRL); 11608c2ecf20Sopenharmony_ci value &= ~MAC_CTRL_RX_EN; 11618c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); 11628c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 11638c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 11648c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci return 0; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* Link Up */ 11708c2ecf20Sopenharmony_ci ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); 11718c2ecf20Sopenharmony_ci if (ret_val) 11728c2ecf20Sopenharmony_ci return ret_val; 11738c2ecf20Sopenharmony_ci switch (hw->MediaType) { 11748c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 11758c2ecf20Sopenharmony_ci if (speed != SPEED_100 || duplex != FULL_DUPLEX) 11768c2ecf20Sopenharmony_ci reconfig = 1; 11778c2ecf20Sopenharmony_ci break; 11788c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 11798c2ecf20Sopenharmony_ci if (speed != SPEED_100 || duplex != HALF_DUPLEX) 11808c2ecf20Sopenharmony_ci reconfig = 1; 11818c2ecf20Sopenharmony_ci break; 11828c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 11838c2ecf20Sopenharmony_ci if (speed != SPEED_10 || duplex != FULL_DUPLEX) 11848c2ecf20Sopenharmony_ci reconfig = 1; 11858c2ecf20Sopenharmony_ci break; 11868c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_HALF: 11878c2ecf20Sopenharmony_ci if (speed != SPEED_10 || duplex != HALF_DUPLEX) 11888c2ecf20Sopenharmony_ci reconfig = 1; 11898c2ecf20Sopenharmony_ci break; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci /* link result is our setting */ 11928c2ecf20Sopenharmony_ci if (reconfig == 0) { 11938c2ecf20Sopenharmony_ci if (adapter->link_speed != speed || 11948c2ecf20Sopenharmony_ci adapter->link_duplex != duplex) { 11958c2ecf20Sopenharmony_ci adapter->link_speed = speed; 11968c2ecf20Sopenharmony_ci adapter->link_duplex = duplex; 11978c2ecf20Sopenharmony_ci atl2_setup_mac_ctrl(adapter); 11988c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n", 11998c2ecf20Sopenharmony_ci atl2_driver_name, netdev->name, 12008c2ecf20Sopenharmony_ci adapter->link_speed, 12018c2ecf20Sopenharmony_ci adapter->link_duplex == FULL_DUPLEX ? 12028c2ecf20Sopenharmony_ci "Full Duplex" : "Half Duplex"); 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ 12068c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 12078c2ecf20Sopenharmony_ci netif_wake_queue(netdev); 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci return 0; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* change original link status */ 12138c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { 12148c2ecf20Sopenharmony_ci u32 value; 12158c2ecf20Sopenharmony_ci /* disable rx */ 12168c2ecf20Sopenharmony_ci value = ATL2_READ_REG(hw, REG_MAC_CTRL); 12178c2ecf20Sopenharmony_ci value &= ~MAC_CTRL_RX_EN; 12188c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 12218c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 12228c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci /* auto-neg, insert timer to re-config phy 12268c2ecf20Sopenharmony_ci * (if interval smaller than 5 seconds, something strange) */ 12278c2ecf20Sopenharmony_ci if (!test_bit(__ATL2_DOWN, &adapter->flags)) { 12288c2ecf20Sopenharmony_ci if (!test_and_set_bit(0, &adapter->cfg_phy)) 12298c2ecf20Sopenharmony_ci mod_timer(&adapter->phy_config_timer, 12308c2ecf20Sopenharmony_ci round_jiffies(jiffies + 5 * HZ)); 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci return 0; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci/** 12378c2ecf20Sopenharmony_ci * atl2_link_chg_task - deal with link change event Out of interrupt context 12388c2ecf20Sopenharmony_ci * @work: pointer to work struct with private info 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_cistatic void atl2_link_chg_task(struct work_struct *work) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci struct atl2_adapter *adapter; 12438c2ecf20Sopenharmony_ci unsigned long flags; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci adapter = container_of(work, struct atl2_adapter, link_chg_task); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->stats_lock, flags); 12488c2ecf20Sopenharmony_ci atl2_check_link(adapter); 12498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->stats_lock, flags); 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic void atl2_setup_pcicmd(struct pci_dev *pdev) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci u16 cmd; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &cmd); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (cmd & PCI_COMMAND_INTX_DISABLE) 12598c2ecf20Sopenharmony_ci cmd &= ~PCI_COMMAND_INTX_DISABLE; 12608c2ecf20Sopenharmony_ci if (cmd & PCI_COMMAND_IO) 12618c2ecf20Sopenharmony_ci cmd &= ~PCI_COMMAND_IO; 12628c2ecf20Sopenharmony_ci if (0 == (cmd & PCI_COMMAND_MEMORY)) 12638c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_MEMORY; 12648c2ecf20Sopenharmony_ci if (0 == (cmd & PCI_COMMAND_MASTER)) 12658c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_MASTER; 12668c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci /* 12698c2ecf20Sopenharmony_ci * some motherboards BIOS(PXE/EFI) driver may set PME 12708c2ecf20Sopenharmony_ci * while they transfer control to OS (Windows/Linux) 12718c2ecf20Sopenharmony_ci * so we should clear this bit before NIC work normally 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 12778c2ecf20Sopenharmony_cistatic void atl2_poll_controller(struct net_device *netdev) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci disable_irq(netdev->irq); 12808c2ecf20Sopenharmony_ci atl2_intr(netdev->irq, netdev); 12818c2ecf20Sopenharmony_ci enable_irq(netdev->irq); 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci#endif 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic const struct net_device_ops atl2_netdev_ops = { 12878c2ecf20Sopenharmony_ci .ndo_open = atl2_open, 12888c2ecf20Sopenharmony_ci .ndo_stop = atl2_close, 12898c2ecf20Sopenharmony_ci .ndo_start_xmit = atl2_xmit_frame, 12908c2ecf20Sopenharmony_ci .ndo_set_rx_mode = atl2_set_multi, 12918c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 12928c2ecf20Sopenharmony_ci .ndo_set_mac_address = atl2_set_mac, 12938c2ecf20Sopenharmony_ci .ndo_change_mtu = atl2_change_mtu, 12948c2ecf20Sopenharmony_ci .ndo_fix_features = atl2_fix_features, 12958c2ecf20Sopenharmony_ci .ndo_set_features = atl2_set_features, 12968c2ecf20Sopenharmony_ci .ndo_do_ioctl = atl2_ioctl, 12978c2ecf20Sopenharmony_ci .ndo_tx_timeout = atl2_tx_timeout, 12988c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 12998c2ecf20Sopenharmony_ci .ndo_poll_controller = atl2_poll_controller, 13008c2ecf20Sopenharmony_ci#endif 13018c2ecf20Sopenharmony_ci}; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci/** 13048c2ecf20Sopenharmony_ci * atl2_probe - Device Initialization Routine 13058c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 13068c2ecf20Sopenharmony_ci * @ent: entry in atl2_pci_tbl 13078c2ecf20Sopenharmony_ci * 13088c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 13098c2ecf20Sopenharmony_ci * 13108c2ecf20Sopenharmony_ci * atl2_probe initializes an adapter identified by a pci_dev structure. 13118c2ecf20Sopenharmony_ci * The OS initialization, configuring of the adapter private structure, 13128c2ecf20Sopenharmony_ci * and a hardware reset occur. 13138c2ecf20Sopenharmony_ci */ 13148c2ecf20Sopenharmony_cistatic int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci struct net_device *netdev; 13178c2ecf20Sopenharmony_ci struct atl2_adapter *adapter; 13188c2ecf20Sopenharmony_ci static int cards_found = 0; 13198c2ecf20Sopenharmony_ci unsigned long mmio_start; 13208c2ecf20Sopenharmony_ci int mmio_len; 13218c2ecf20Sopenharmony_ci int err; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 13248c2ecf20Sopenharmony_ci if (err) 13258c2ecf20Sopenharmony_ci return err; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* 13288c2ecf20Sopenharmony_ci * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA 13298c2ecf20Sopenharmony_ci * until the kernel has the proper infrastructure to support 64-bit DMA 13308c2ecf20Sopenharmony_ci * on these devices. 13318c2ecf20Sopenharmony_ci */ 13328c2ecf20Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) && 13338c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) { 13348c2ecf20Sopenharmony_ci printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n"); 13358c2ecf20Sopenharmony_ci err = -EIO; 13368c2ecf20Sopenharmony_ci goto err_dma; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci /* Mark all PCI regions associated with PCI device 13408c2ecf20Sopenharmony_ci * pdev as being reserved by owner atl2_driver_name */ 13418c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, atl2_driver_name); 13428c2ecf20Sopenharmony_ci if (err) 13438c2ecf20Sopenharmony_ci goto err_pci_reg; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* Enables bus-mastering on the device and calls 13468c2ecf20Sopenharmony_ci * pcibios_set_master to do the needed arch specific settings */ 13478c2ecf20Sopenharmony_ci pci_set_master(pdev); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct atl2_adapter)); 13508c2ecf20Sopenharmony_ci if (!netdev) { 13518c2ecf20Sopenharmony_ci err = -ENOMEM; 13528c2ecf20Sopenharmony_ci goto err_alloc_etherdev; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, netdev); 13588c2ecf20Sopenharmony_ci adapter = netdev_priv(netdev); 13598c2ecf20Sopenharmony_ci adapter->netdev = netdev; 13608c2ecf20Sopenharmony_ci adapter->pdev = pdev; 13618c2ecf20Sopenharmony_ci adapter->hw.back = adapter; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci mmio_start = pci_resource_start(pdev, 0x0); 13648c2ecf20Sopenharmony_ci mmio_len = pci_resource_len(pdev, 0x0); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci adapter->hw.mem_rang = (u32)mmio_len; 13678c2ecf20Sopenharmony_ci adapter->hw.hw_addr = ioremap(mmio_start, mmio_len); 13688c2ecf20Sopenharmony_ci if (!adapter->hw.hw_addr) { 13698c2ecf20Sopenharmony_ci err = -EIO; 13708c2ecf20Sopenharmony_ci goto err_ioremap; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci atl2_setup_pcicmd(pdev); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci netdev->netdev_ops = &atl2_netdev_ops; 13768c2ecf20Sopenharmony_ci netdev->ethtool_ops = &atl2_ethtool_ops; 13778c2ecf20Sopenharmony_ci netdev->watchdog_timeo = 5 * HZ; 13788c2ecf20Sopenharmony_ci netdev->min_mtu = 40; 13798c2ecf20Sopenharmony_ci netdev->max_mtu = ETH_DATA_LEN + VLAN_HLEN; 13808c2ecf20Sopenharmony_ci strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci netdev->mem_start = mmio_start; 13838c2ecf20Sopenharmony_ci netdev->mem_end = mmio_start + mmio_len; 13848c2ecf20Sopenharmony_ci adapter->bd_number = cards_found; 13858c2ecf20Sopenharmony_ci adapter->pci_using_64 = false; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci /* setup the private structure */ 13888c2ecf20Sopenharmony_ci err = atl2_sw_init(adapter); 13898c2ecf20Sopenharmony_ci if (err) 13908c2ecf20Sopenharmony_ci goto err_sw_init; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci netdev->hw_features = NETIF_F_HW_VLAN_CTAG_RX; 13938c2ecf20Sopenharmony_ci netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci /* Init PHY as early as possible due to power saving issue */ 13968c2ecf20Sopenharmony_ci atl2_phy_init(&adapter->hw); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* reset the controller to 13998c2ecf20Sopenharmony_ci * put the device in a known good starting state */ 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci if (atl2_reset_hw(&adapter->hw)) { 14028c2ecf20Sopenharmony_ci err = -EIO; 14038c2ecf20Sopenharmony_ci goto err_reset; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci /* copy the MAC address out of the EEPROM */ 14078c2ecf20Sopenharmony_ci atl2_read_mac_addr(&adapter->hw); 14088c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); 14098c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(netdev->dev_addr)) { 14108c2ecf20Sopenharmony_ci err = -EIO; 14118c2ecf20Sopenharmony_ci goto err_eeprom; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci atl2_check_options(adapter); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci timer_setup(&adapter->watchdog_timer, atl2_watchdog, 0); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci timer_setup(&adapter->phy_config_timer, atl2_phy_config, 0); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci INIT_WORK(&adapter->reset_task, atl2_reset_task); 14218c2ecf20Sopenharmony_ci INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci strcpy(netdev->name, "eth%d"); /* ?? */ 14248c2ecf20Sopenharmony_ci err = register_netdev(netdev); 14258c2ecf20Sopenharmony_ci if (err) 14268c2ecf20Sopenharmony_ci goto err_register; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* assume we have no link for now */ 14298c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 14308c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci cards_found++; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci return 0; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cierr_reset: 14378c2ecf20Sopenharmony_cierr_register: 14388c2ecf20Sopenharmony_cierr_sw_init: 14398c2ecf20Sopenharmony_cierr_eeprom: 14408c2ecf20Sopenharmony_ci iounmap(adapter->hw.hw_addr); 14418c2ecf20Sopenharmony_cierr_ioremap: 14428c2ecf20Sopenharmony_ci free_netdev(netdev); 14438c2ecf20Sopenharmony_cierr_alloc_etherdev: 14448c2ecf20Sopenharmony_ci pci_release_regions(pdev); 14458c2ecf20Sopenharmony_cierr_pci_reg: 14468c2ecf20Sopenharmony_cierr_dma: 14478c2ecf20Sopenharmony_ci pci_disable_device(pdev); 14488c2ecf20Sopenharmony_ci return err; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci/** 14528c2ecf20Sopenharmony_ci * atl2_remove - Device Removal Routine 14538c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 14548c2ecf20Sopenharmony_ci * 14558c2ecf20Sopenharmony_ci * atl2_remove is called by the PCI subsystem to alert the driver 14568c2ecf20Sopenharmony_ci * that it should release a PCI device. The could be caused by a 14578c2ecf20Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from 14588c2ecf20Sopenharmony_ci * memory. 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci/* FIXME: write the original MAC address back in case it was changed from a 14618c2ecf20Sopenharmony_ci * BIOS-set value, as in atl1 -- CHS */ 14628c2ecf20Sopenharmony_cistatic void atl2_remove(struct pci_dev *pdev) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 14658c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* flush_scheduled work may reschedule our watchdog task, so 14688c2ecf20Sopenharmony_ci * explicitly disable watchdog tasks from being rescheduled */ 14698c2ecf20Sopenharmony_ci set_bit(__ATL2_DOWN, &adapter->flags); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci del_timer_sync(&adapter->watchdog_timer); 14728c2ecf20Sopenharmony_ci del_timer_sync(&adapter->phy_config_timer); 14738c2ecf20Sopenharmony_ci cancel_work_sync(&adapter->reset_task); 14748c2ecf20Sopenharmony_ci cancel_work_sync(&adapter->link_chg_task); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci unregister_netdev(netdev); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci atl2_force_ps(&adapter->hw); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci iounmap(adapter->hw.hw_addr); 14818c2ecf20Sopenharmony_ci pci_release_regions(pdev); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci free_netdev(netdev); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci pci_disable_device(pdev); 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic int atl2_suspend(struct pci_dev *pdev, pm_message_t state) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 14918c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 14928c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 14938c2ecf20Sopenharmony_ci u16 speed, duplex; 14948c2ecf20Sopenharmony_ci u32 ctrl = 0; 14958c2ecf20Sopenharmony_ci u32 wufc = adapter->wol; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 14988c2ecf20Sopenharmony_ci int retval = 0; 14998c2ecf20Sopenharmony_ci#endif 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci netif_device_detach(netdev); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 15048c2ecf20Sopenharmony_ci WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); 15058c2ecf20Sopenharmony_ci atl2_down(adapter); 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 15098c2ecf20Sopenharmony_ci retval = pci_save_state(pdev); 15108c2ecf20Sopenharmony_ci if (retval) 15118c2ecf20Sopenharmony_ci return retval; 15128c2ecf20Sopenharmony_ci#endif 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl); 15158c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl); 15168c2ecf20Sopenharmony_ci if (ctrl & BMSR_LSTATUS) 15178c2ecf20Sopenharmony_ci wufc &= ~ATLX_WUFC_LNKC; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) { 15208c2ecf20Sopenharmony_ci u32 ret_val; 15218c2ecf20Sopenharmony_ci /* get current link speed & duplex */ 15228c2ecf20Sopenharmony_ci ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); 15238c2ecf20Sopenharmony_ci if (ret_val) { 15248c2ecf20Sopenharmony_ci printk(KERN_DEBUG 15258c2ecf20Sopenharmony_ci "%s: get speed&duplex error while suspend\n", 15268c2ecf20Sopenharmony_ci atl2_driver_name); 15278c2ecf20Sopenharmony_ci goto wol_dis; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci ctrl = 0; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci /* turn on magic packet wol */ 15338c2ecf20Sopenharmony_ci if (wufc & ATLX_WUFC_MAG) 15348c2ecf20Sopenharmony_ci ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* ignore Link Chg event when Link is up */ 15378c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci /* Config MAC CTRL Register */ 15408c2ecf20Sopenharmony_ci ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; 15418c2ecf20Sopenharmony_ci if (FULL_DUPLEX == adapter->link_duplex) 15428c2ecf20Sopenharmony_ci ctrl |= MAC_CTRL_DUPLX; 15438c2ecf20Sopenharmony_ci ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); 15448c2ecf20Sopenharmony_ci ctrl |= (((u32)adapter->hw.preamble_len & 15458c2ecf20Sopenharmony_ci MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); 15468c2ecf20Sopenharmony_ci ctrl |= (((u32)(adapter->hw.retry_buf & 15478c2ecf20Sopenharmony_ci MAC_CTRL_HALF_LEFT_BUF_MASK)) << 15488c2ecf20Sopenharmony_ci MAC_CTRL_HALF_LEFT_BUF_SHIFT); 15498c2ecf20Sopenharmony_ci if (wufc & ATLX_WUFC_MAG) { 15508c2ecf20Sopenharmony_ci /* magic packet maybe Broadcast&multicast&Unicast */ 15518c2ecf20Sopenharmony_ci ctrl |= MAC_CTRL_BC_EN; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* pcie patch */ 15578c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); 15588c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 15598c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); 15608c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); 15618c2ecf20Sopenharmony_ci ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; 15628c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); 15658c2ecf20Sopenharmony_ci goto suspend_exit; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) { 15698c2ecf20Sopenharmony_ci /* link is down, so only LINK CHG WOL event enable */ 15708c2ecf20Sopenharmony_ci ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); 15718c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); 15728c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci /* pcie patch */ 15758c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); 15768c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 15778c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); 15788c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); 15798c2ecf20Sopenharmony_ci ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; 15808c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci hw->phy_configured = false; /* re-init PHY when resume */ 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci goto suspend_exit; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ciwol_dis: 15908c2ecf20Sopenharmony_ci /* WOL disabled */ 15918c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci /* pcie patch */ 15948c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); 15958c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 15968c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); 15978c2ecf20Sopenharmony_ci ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); 15988c2ecf20Sopenharmony_ci ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; 15998c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci atl2_force_ps(hw); 16028c2ecf20Sopenharmony_ci hw->phy_configured = false; /* re-init PHY when resume */ 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cisuspend_exit: 16078c2ecf20Sopenharmony_ci if (netif_running(netdev)) 16088c2ecf20Sopenharmony_ci atl2_free_irq(adapter); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci pci_disable_device(pdev); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci pci_set_power_state(pdev, pci_choose_state(pdev, state)); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci return 0; 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 16188c2ecf20Sopenharmony_cistatic int atl2_resume(struct pci_dev *pdev) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 16218c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 16228c2ecf20Sopenharmony_ci u32 err; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 16258c2ecf20Sopenharmony_ci pci_restore_state(pdev); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 16288c2ecf20Sopenharmony_ci if (err) { 16298c2ecf20Sopenharmony_ci printk(KERN_ERR 16308c2ecf20Sopenharmony_ci "atl2: Cannot enable PCI device from suspend\n"); 16318c2ecf20Sopenharmony_ci return err; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci pci_set_master(pdev); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D3hot, 0); 16398c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D3cold, 0); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 16448c2ecf20Sopenharmony_ci err = atl2_request_irq(adapter); 16458c2ecf20Sopenharmony_ci if (err) 16468c2ecf20Sopenharmony_ci return err; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci atl2_reset_hw(&adapter->hw); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (netif_running(netdev)) 16528c2ecf20Sopenharmony_ci atl2_up(adapter); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci netif_device_attach(netdev); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci return 0; 16578c2ecf20Sopenharmony_ci} 16588c2ecf20Sopenharmony_ci#endif 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_cistatic void atl2_shutdown(struct pci_dev *pdev) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci atl2_suspend(pdev, PMSG_SUSPEND); 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic struct pci_driver atl2_driver = { 16668c2ecf20Sopenharmony_ci .name = atl2_driver_name, 16678c2ecf20Sopenharmony_ci .id_table = atl2_pci_tbl, 16688c2ecf20Sopenharmony_ci .probe = atl2_probe, 16698c2ecf20Sopenharmony_ci .remove = atl2_remove, 16708c2ecf20Sopenharmony_ci /* Power Management Hooks */ 16718c2ecf20Sopenharmony_ci .suspend = atl2_suspend, 16728c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 16738c2ecf20Sopenharmony_ci .resume = atl2_resume, 16748c2ecf20Sopenharmony_ci#endif 16758c2ecf20Sopenharmony_ci .shutdown = atl2_shutdown, 16768c2ecf20Sopenharmony_ci}; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci/** 16798c2ecf20Sopenharmony_ci * atl2_init_module - Driver Registration Routine 16808c2ecf20Sopenharmony_ci * 16818c2ecf20Sopenharmony_ci * atl2_init_module is the first routine called when the driver is 16828c2ecf20Sopenharmony_ci * loaded. All it does is register with the PCI subsystem. 16838c2ecf20Sopenharmony_ci */ 16848c2ecf20Sopenharmony_cistatic int __init atl2_init_module(void) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci return pci_register_driver(&atl2_driver); 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_cimodule_init(atl2_init_module); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci/** 16918c2ecf20Sopenharmony_ci * atl2_exit_module - Driver Exit Cleanup Routine 16928c2ecf20Sopenharmony_ci * 16938c2ecf20Sopenharmony_ci * atl2_exit_module is called just before the driver is removed 16948c2ecf20Sopenharmony_ci * from memory. 16958c2ecf20Sopenharmony_ci */ 16968c2ecf20Sopenharmony_cistatic void __exit atl2_exit_module(void) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci pci_unregister_driver(&atl2_driver); 16998c2ecf20Sopenharmony_ci} 17008c2ecf20Sopenharmony_cimodule_exit(atl2_exit_module); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = hw->back; 17058c2ecf20Sopenharmony_ci pci_read_config_word(adapter->pdev, reg, value); 17068c2ecf20Sopenharmony_ci} 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_cistatic void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) 17098c2ecf20Sopenharmony_ci{ 17108c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = hw->back; 17118c2ecf20Sopenharmony_ci pci_write_config_word(adapter->pdev, reg, *value); 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic int atl2_get_link_ksettings(struct net_device *netdev, 17158c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 17168c2ecf20Sopenharmony_ci{ 17178c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 17188c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 17198c2ecf20Sopenharmony_ci u32 supported, advertising; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci supported = (SUPPORTED_10baseT_Half | 17228c2ecf20Sopenharmony_ci SUPPORTED_10baseT_Full | 17238c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Half | 17248c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Full | 17258c2ecf20Sopenharmony_ci SUPPORTED_Autoneg | 17268c2ecf20Sopenharmony_ci SUPPORTED_TP); 17278c2ecf20Sopenharmony_ci advertising = ADVERTISED_TP; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 17308c2ecf20Sopenharmony_ci advertising |= hw->autoneg_advertised; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci cmd->base.port = PORT_TP; 17338c2ecf20Sopenharmony_ci cmd->base.phy_address = 0; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (adapter->link_speed != SPEED_0) { 17368c2ecf20Sopenharmony_ci cmd->base.speed = adapter->link_speed; 17378c2ecf20Sopenharmony_ci if (adapter->link_duplex == FULL_DUPLEX) 17388c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 17398c2ecf20Sopenharmony_ci else 17408c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 17418c2ecf20Sopenharmony_ci } else { 17428c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 17438c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 17498c2ecf20Sopenharmony_ci supported); 17508c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 17518c2ecf20Sopenharmony_ci advertising); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci return 0; 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_cistatic int atl2_set_link_ksettings(struct net_device *netdev, 17578c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 17588c2ecf20Sopenharmony_ci{ 17598c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 17608c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 17618c2ecf20Sopenharmony_ci u32 advertising; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&advertising, 17648c2ecf20Sopenharmony_ci cmd->link_modes.advertising); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) 17678c2ecf20Sopenharmony_ci msleep(1); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_ENABLE) { 17708c2ecf20Sopenharmony_ci#define MY_ADV_MASK (ADVERTISE_10_HALF | \ 17718c2ecf20Sopenharmony_ci ADVERTISE_10_FULL | \ 17728c2ecf20Sopenharmony_ci ADVERTISE_100_HALF| \ 17738c2ecf20Sopenharmony_ci ADVERTISE_100_FULL) 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci if ((advertising & MY_ADV_MASK) == MY_ADV_MASK) { 17768c2ecf20Sopenharmony_ci hw->MediaType = MEDIA_TYPE_AUTO_SENSOR; 17778c2ecf20Sopenharmony_ci hw->autoneg_advertised = MY_ADV_MASK; 17788c2ecf20Sopenharmony_ci } else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_FULL) { 17798c2ecf20Sopenharmony_ci hw->MediaType = MEDIA_TYPE_100M_FULL; 17808c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_100_FULL; 17818c2ecf20Sopenharmony_ci } else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_HALF) { 17828c2ecf20Sopenharmony_ci hw->MediaType = MEDIA_TYPE_100M_HALF; 17838c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_100_HALF; 17848c2ecf20Sopenharmony_ci } else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_FULL) { 17858c2ecf20Sopenharmony_ci hw->MediaType = MEDIA_TYPE_10M_FULL; 17868c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_10_FULL; 17878c2ecf20Sopenharmony_ci } else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_HALF) { 17888c2ecf20Sopenharmony_ci hw->MediaType = MEDIA_TYPE_10M_HALF; 17898c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_10_HALF; 17908c2ecf20Sopenharmony_ci } else { 17918c2ecf20Sopenharmony_ci clear_bit(__ATL2_RESETTING, &adapter->flags); 17928c2ecf20Sopenharmony_ci return -EINVAL; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci advertising = hw->autoneg_advertised | 17958c2ecf20Sopenharmony_ci ADVERTISED_TP | ADVERTISED_Autoneg; 17968c2ecf20Sopenharmony_ci } else { 17978c2ecf20Sopenharmony_ci clear_bit(__ATL2_RESETTING, &adapter->flags); 17988c2ecf20Sopenharmony_ci return -EINVAL; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci /* reset the link */ 18028c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { 18038c2ecf20Sopenharmony_ci atl2_down(adapter); 18048c2ecf20Sopenharmony_ci atl2_up(adapter); 18058c2ecf20Sopenharmony_ci } else 18068c2ecf20Sopenharmony_ci atl2_reset_hw(&adapter->hw); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci clear_bit(__ATL2_RESETTING, &adapter->flags); 18098c2ecf20Sopenharmony_ci return 0; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic u32 atl2_get_msglevel(struct net_device *netdev) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci return 0; 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci/* 18188c2ecf20Sopenharmony_ci * It's sane for this to be empty, but we might want to take advantage of this. 18198c2ecf20Sopenharmony_ci */ 18208c2ecf20Sopenharmony_cistatic void atl2_set_msglevel(struct net_device *netdev, u32 data) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci} 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_cistatic int atl2_get_regs_len(struct net_device *netdev) 18258c2ecf20Sopenharmony_ci{ 18268c2ecf20Sopenharmony_ci#define ATL2_REGS_LEN 42 18278c2ecf20Sopenharmony_ci return sizeof(u32) * ATL2_REGS_LEN; 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_cistatic void atl2_get_regs(struct net_device *netdev, 18318c2ecf20Sopenharmony_ci struct ethtool_regs *regs, void *p) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 18348c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 18358c2ecf20Sopenharmony_ci u32 *regs_buff = p; 18368c2ecf20Sopenharmony_ci u16 phy_data; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci memset(p, 0, sizeof(u32) * ATL2_REGS_LEN); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci regs_buff[0] = ATL2_READ_REG(hw, REG_VPD_CAP); 18438c2ecf20Sopenharmony_ci regs_buff[1] = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); 18448c2ecf20Sopenharmony_ci regs_buff[2] = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG); 18458c2ecf20Sopenharmony_ci regs_buff[3] = ATL2_READ_REG(hw, REG_TWSI_CTRL); 18468c2ecf20Sopenharmony_ci regs_buff[4] = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); 18478c2ecf20Sopenharmony_ci regs_buff[5] = ATL2_READ_REG(hw, REG_MASTER_CTRL); 18488c2ecf20Sopenharmony_ci regs_buff[6] = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT); 18498c2ecf20Sopenharmony_ci regs_buff[7] = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); 18508c2ecf20Sopenharmony_ci regs_buff[8] = ATL2_READ_REG(hw, REG_PHY_ENABLE); 18518c2ecf20Sopenharmony_ci regs_buff[9] = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER); 18528c2ecf20Sopenharmony_ci regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS); 18538c2ecf20Sopenharmony_ci regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL); 18548c2ecf20Sopenharmony_ci regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK); 18558c2ecf20Sopenharmony_ci regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL); 18568c2ecf20Sopenharmony_ci regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG); 18578c2ecf20Sopenharmony_ci regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); 18588c2ecf20Sopenharmony_ci regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4); 18598c2ecf20Sopenharmony_ci regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE); 18608c2ecf20Sopenharmony_ci regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4); 18618c2ecf20Sopenharmony_ci regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); 18628c2ecf20Sopenharmony_ci regs_buff[20] = ATL2_READ_REG(hw, REG_MTU); 18638c2ecf20Sopenharmony_ci regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL); 18648c2ecf20Sopenharmony_ci regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END); 18658c2ecf20Sopenharmony_ci regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI); 18668c2ecf20Sopenharmony_ci regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO); 18678c2ecf20Sopenharmony_ci regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE); 18688c2ecf20Sopenharmony_ci regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO); 18698c2ecf20Sopenharmony_ci regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE); 18708c2ecf20Sopenharmony_ci regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO); 18718c2ecf20Sopenharmony_ci regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM); 18728c2ecf20Sopenharmony_ci regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR); 18738c2ecf20Sopenharmony_ci regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH); 18748c2ecf20Sopenharmony_ci regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW); 18758c2ecf20Sopenharmony_ci regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH); 18768c2ecf20Sopenharmony_ci regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH); 18778c2ecf20Sopenharmony_ci regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX); 18788c2ecf20Sopenharmony_ci regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX); 18798c2ecf20Sopenharmony_ci regs_buff[38] = ATL2_READ_REG(hw, REG_ISR); 18808c2ecf20Sopenharmony_ci regs_buff[39] = ATL2_READ_REG(hw, REG_IMR); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_BMCR, &phy_data); 18838c2ecf20Sopenharmony_ci regs_buff[40] = (u32)phy_data; 18848c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_BMSR, &phy_data); 18858c2ecf20Sopenharmony_ci regs_buff[41] = (u32)phy_data; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic int atl2_get_eeprom_len(struct net_device *netdev) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci if (!atl2_check_eeprom_exist(&adapter->hw)) 18938c2ecf20Sopenharmony_ci return 512; 18948c2ecf20Sopenharmony_ci else 18958c2ecf20Sopenharmony_ci return 0; 18968c2ecf20Sopenharmony_ci} 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_cistatic int atl2_get_eeprom(struct net_device *netdev, 18998c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *bytes) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 19028c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 19038c2ecf20Sopenharmony_ci u32 *eeprom_buff; 19048c2ecf20Sopenharmony_ci int first_dword, last_dword; 19058c2ecf20Sopenharmony_ci int ret_val = 0; 19068c2ecf20Sopenharmony_ci int i; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci if (eeprom->len == 0) 19098c2ecf20Sopenharmony_ci return -EINVAL; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci if (atl2_check_eeprom_exist(hw)) 19128c2ecf20Sopenharmony_ci return -EINVAL; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci eeprom->magic = hw->vendor_id | (hw->device_id << 16); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci first_dword = eeprom->offset >> 2; 19178c2ecf20Sopenharmony_ci last_dword = (eeprom->offset + eeprom->len - 1) >> 2; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32), 19208c2ecf20Sopenharmony_ci GFP_KERNEL); 19218c2ecf20Sopenharmony_ci if (!eeprom_buff) 19228c2ecf20Sopenharmony_ci return -ENOMEM; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci for (i = first_dword; i < last_dword; i++) { 19258c2ecf20Sopenharmony_ci if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) { 19268c2ecf20Sopenharmony_ci ret_val = -EIO; 19278c2ecf20Sopenharmony_ci goto free; 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), 19328c2ecf20Sopenharmony_ci eeprom->len); 19338c2ecf20Sopenharmony_cifree: 19348c2ecf20Sopenharmony_ci kfree(eeprom_buff); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci return ret_val; 19378c2ecf20Sopenharmony_ci} 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_cistatic int atl2_set_eeprom(struct net_device *netdev, 19408c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *bytes) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 19438c2ecf20Sopenharmony_ci struct atl2_hw *hw = &adapter->hw; 19448c2ecf20Sopenharmony_ci u32 *eeprom_buff; 19458c2ecf20Sopenharmony_ci u32 *ptr; 19468c2ecf20Sopenharmony_ci int max_len, first_dword, last_dword, ret_val = 0; 19478c2ecf20Sopenharmony_ci int i; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci if (eeprom->len == 0) 19508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) 19538c2ecf20Sopenharmony_ci return -EFAULT; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci max_len = 512; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci first_dword = eeprom->offset >> 2; 19588c2ecf20Sopenharmony_ci last_dword = (eeprom->offset + eeprom->len - 1) >> 2; 19598c2ecf20Sopenharmony_ci eeprom_buff = kmalloc(max_len, GFP_KERNEL); 19608c2ecf20Sopenharmony_ci if (!eeprom_buff) 19618c2ecf20Sopenharmony_ci return -ENOMEM; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci ptr = eeprom_buff; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci if (eeprom->offset & 3) { 19668c2ecf20Sopenharmony_ci /* need read/modify/write of first changed EEPROM word */ 19678c2ecf20Sopenharmony_ci /* only the second byte of the word is being modified */ 19688c2ecf20Sopenharmony_ci if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) { 19698c2ecf20Sopenharmony_ci ret_val = -EIO; 19708c2ecf20Sopenharmony_ci goto out; 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci ptr++; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci if (((eeprom->offset + eeprom->len) & 3)) { 19758c2ecf20Sopenharmony_ci /* 19768c2ecf20Sopenharmony_ci * need read/modify/write of last changed EEPROM word 19778c2ecf20Sopenharmony_ci * only the first byte of the word is being modified 19788c2ecf20Sopenharmony_ci */ 19798c2ecf20Sopenharmony_ci if (!atl2_read_eeprom(hw, last_dword * 4, 19808c2ecf20Sopenharmony_ci &(eeprom_buff[last_dword - first_dword]))) { 19818c2ecf20Sopenharmony_ci ret_val = -EIO; 19828c2ecf20Sopenharmony_ci goto out; 19838c2ecf20Sopenharmony_ci } 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci /* Device's eeprom is always little-endian, word addressable */ 19878c2ecf20Sopenharmony_ci memcpy(ptr, bytes, eeprom->len); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci for (i = 0; i < last_dword - first_dword + 1; i++) { 19908c2ecf20Sopenharmony_ci if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) { 19918c2ecf20Sopenharmony_ci ret_val = -EIO; 19928c2ecf20Sopenharmony_ci goto out; 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci out: 19968c2ecf20Sopenharmony_ci kfree(eeprom_buff); 19978c2ecf20Sopenharmony_ci return ret_val; 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_cistatic void atl2_get_drvinfo(struct net_device *netdev, 20018c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 20028c2ecf20Sopenharmony_ci{ 20038c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, atl2_driver_name, sizeof(drvinfo->driver)); 20068c2ecf20Sopenharmony_ci strlcpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version)); 20078c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 20088c2ecf20Sopenharmony_ci sizeof(drvinfo->bus_info)); 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic void atl2_get_wol(struct net_device *netdev, 20128c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 20138c2ecf20Sopenharmony_ci{ 20148c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci wol->supported = WAKE_MAGIC; 20178c2ecf20Sopenharmony_ci wol->wolopts = 0; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci if (adapter->wol & ATLX_WUFC_EX) 20208c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 20218c2ecf20Sopenharmony_ci if (adapter->wol & ATLX_WUFC_MC) 20228c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_MCAST; 20238c2ecf20Sopenharmony_ci if (adapter->wol & ATLX_WUFC_BC) 20248c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_BCAST; 20258c2ecf20Sopenharmony_ci if (adapter->wol & ATLX_WUFC_MAG) 20268c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 20278c2ecf20Sopenharmony_ci if (adapter->wol & ATLX_WUFC_LNKC) 20288c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_PHY; 20298c2ecf20Sopenharmony_ci} 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_cistatic int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 20328c2ecf20Sopenharmony_ci{ 20338c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)) 20368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) 20398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci /* these settings will always override what we currently have */ 20428c2ecf20Sopenharmony_ci adapter->wol = 0; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 20458c2ecf20Sopenharmony_ci adapter->wol |= ATLX_WUFC_MAG; 20468c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_PHY) 20478c2ecf20Sopenharmony_ci adapter->wol |= ATLX_WUFC_LNKC; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci return 0; 20508c2ecf20Sopenharmony_ci} 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic int atl2_nway_reset(struct net_device *netdev) 20538c2ecf20Sopenharmony_ci{ 20548c2ecf20Sopenharmony_ci struct atl2_adapter *adapter = netdev_priv(netdev); 20558c2ecf20Sopenharmony_ci if (netif_running(netdev)) 20568c2ecf20Sopenharmony_ci atl2_reinit_locked(adapter); 20578c2ecf20Sopenharmony_ci return 0; 20588c2ecf20Sopenharmony_ci} 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_cistatic const struct ethtool_ops atl2_ethtool_ops = { 20618c2ecf20Sopenharmony_ci .get_drvinfo = atl2_get_drvinfo, 20628c2ecf20Sopenharmony_ci .get_regs_len = atl2_get_regs_len, 20638c2ecf20Sopenharmony_ci .get_regs = atl2_get_regs, 20648c2ecf20Sopenharmony_ci .get_wol = atl2_get_wol, 20658c2ecf20Sopenharmony_ci .set_wol = atl2_set_wol, 20668c2ecf20Sopenharmony_ci .get_msglevel = atl2_get_msglevel, 20678c2ecf20Sopenharmony_ci .set_msglevel = atl2_set_msglevel, 20688c2ecf20Sopenharmony_ci .nway_reset = atl2_nway_reset, 20698c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 20708c2ecf20Sopenharmony_ci .get_eeprom_len = atl2_get_eeprom_len, 20718c2ecf20Sopenharmony_ci .get_eeprom = atl2_get_eeprom, 20728c2ecf20Sopenharmony_ci .set_eeprom = atl2_set_eeprom, 20738c2ecf20Sopenharmony_ci .get_link_ksettings = atl2_get_link_ksettings, 20748c2ecf20Sopenharmony_ci .set_link_ksettings = atl2_set_link_ksettings, 20758c2ecf20Sopenharmony_ci}; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci#define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \ 20788c2ecf20Sopenharmony_ci (((a) & 0xff00ff00) >> 8)) 20798c2ecf20Sopenharmony_ci#define LONGSWAP(a) ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16)) 20808c2ecf20Sopenharmony_ci#define SHORTSWAP(a) (((a) << 8) | ((a) >> 8)) 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci/* 20838c2ecf20Sopenharmony_ci * Reset the transmit and receive units; mask and clear all interrupts. 20848c2ecf20Sopenharmony_ci * 20858c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 20868c2ecf20Sopenharmony_ci * return : 0 or idle status (if error) 20878c2ecf20Sopenharmony_ci */ 20888c2ecf20Sopenharmony_cistatic s32 atl2_reset_hw(struct atl2_hw *hw) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci u32 icr; 20918c2ecf20Sopenharmony_ci u16 pci_cfg_cmd_word; 20928c2ecf20Sopenharmony_ci int i; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ 20958c2ecf20Sopenharmony_ci atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); 20968c2ecf20Sopenharmony_ci if ((pci_cfg_cmd_word & 20978c2ecf20Sopenharmony_ci (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) != 20988c2ecf20Sopenharmony_ci (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) { 20998c2ecf20Sopenharmony_ci pci_cfg_cmd_word |= 21008c2ecf20Sopenharmony_ci (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER); 21018c2ecf20Sopenharmony_ci atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); 21028c2ecf20Sopenharmony_ci } 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci /* Clear Interrupt mask to stop board from generating 21058c2ecf20Sopenharmony_ci * interrupts & Clear any pending interrupt events 21068c2ecf20Sopenharmony_ci */ 21078c2ecf20Sopenharmony_ci /* FIXME */ 21088c2ecf20Sopenharmony_ci /* ATL2_WRITE_REG(hw, REG_IMR, 0); */ 21098c2ecf20Sopenharmony_ci /* ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); */ 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci /* Issue Soft Reset to the MAC. This will reset the chip's 21128c2ecf20Sopenharmony_ci * transmit, receive, DMA. It will not effect 21138c2ecf20Sopenharmony_ci * the current PCI configuration. The global reset bit is self- 21148c2ecf20Sopenharmony_ci * clearing, and should clear within a microsecond. 21158c2ecf20Sopenharmony_ci */ 21168c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); 21178c2ecf20Sopenharmony_ci wmb(); 21188c2ecf20Sopenharmony_ci msleep(1); /* delay about 1ms */ 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci /* Wait at least 10ms for All module to be Idle */ 21218c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 21228c2ecf20Sopenharmony_ci icr = ATL2_READ_REG(hw, REG_IDLE_STATUS); 21238c2ecf20Sopenharmony_ci if (!icr) 21248c2ecf20Sopenharmony_ci break; 21258c2ecf20Sopenharmony_ci msleep(1); /* delay 1 ms */ 21268c2ecf20Sopenharmony_ci cpu_relax(); 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci if (icr) 21308c2ecf20Sopenharmony_ci return icr; 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci return 0; 21338c2ecf20Sopenharmony_ci} 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CS_SETUP 2 21368c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CLK_HI 2 21378c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CLK_LO 2 21388c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CS_HOLD 2 21398c2ecf20Sopenharmony_ci#define CUSTOM_SPI_CS_HI 3 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_cistatic struct atl2_spi_flash_dev flash_table[] = 21428c2ecf20Sopenharmony_ci{ 21438c2ecf20Sopenharmony_ci/* MFR WRSR READ PROGRAM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */ 21448c2ecf20Sopenharmony_ci{"Atmel", 0x0, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62 }, 21458c2ecf20Sopenharmony_ci{"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60 }, 21468c2ecf20Sopenharmony_ci{"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7 }, 21478c2ecf20Sopenharmony_ci}; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_cistatic bool atl2_spi_read(struct atl2_hw *hw, u32 addr, u32 *buf) 21508c2ecf20Sopenharmony_ci{ 21518c2ecf20Sopenharmony_ci int i; 21528c2ecf20Sopenharmony_ci u32 value; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_SPI_DATA, 0); 21558c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci value = SPI_FLASH_CTRL_WAIT_READY | 21588c2ecf20Sopenharmony_ci (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << 21598c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_SETUP_SHIFT | 21608c2ecf20Sopenharmony_ci (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) << 21618c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CLK_HI_SHIFT | 21628c2ecf20Sopenharmony_ci (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) << 21638c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CLK_LO_SHIFT | 21648c2ecf20Sopenharmony_ci (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) << 21658c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_HOLD_SHIFT | 21668c2ecf20Sopenharmony_ci (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) << 21678c2ecf20Sopenharmony_ci SPI_FLASH_CTRL_CS_HI_SHIFT | 21688c2ecf20Sopenharmony_ci (0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci value |= SPI_FLASH_CTRL_START; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 21778c2ecf20Sopenharmony_ci msleep(1); 21788c2ecf20Sopenharmony_ci value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); 21798c2ecf20Sopenharmony_ci if (!(value & SPI_FLASH_CTRL_START)) 21808c2ecf20Sopenharmony_ci break; 21818c2ecf20Sopenharmony_ci } 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci if (value & SPI_FLASH_CTRL_START) 21848c2ecf20Sopenharmony_ci return false; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci *buf = ATL2_READ_REG(hw, REG_SPI_DATA); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci return true; 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci/* 21928c2ecf20Sopenharmony_ci * get_permanent_address 21938c2ecf20Sopenharmony_ci * return 0 if get valid mac address, 21948c2ecf20Sopenharmony_ci */ 21958c2ecf20Sopenharmony_cistatic int get_permanent_address(struct atl2_hw *hw) 21968c2ecf20Sopenharmony_ci{ 21978c2ecf20Sopenharmony_ci u32 Addr[2]; 21988c2ecf20Sopenharmony_ci u32 i, Control; 21998c2ecf20Sopenharmony_ci u16 Register; 22008c2ecf20Sopenharmony_ci u8 EthAddr[ETH_ALEN]; 22018c2ecf20Sopenharmony_ci bool KeyValid; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (is_valid_ether_addr(hw->perm_mac_addr)) 22048c2ecf20Sopenharmony_ci return 0; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci Addr[0] = 0; 22078c2ecf20Sopenharmony_ci Addr[1] = 0; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */ 22108c2ecf20Sopenharmony_ci Register = 0; 22118c2ecf20Sopenharmony_ci KeyValid = false; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci /* Read out all EEPROM content */ 22148c2ecf20Sopenharmony_ci i = 0; 22158c2ecf20Sopenharmony_ci while (1) { 22168c2ecf20Sopenharmony_ci if (atl2_read_eeprom(hw, i + 0x100, &Control)) { 22178c2ecf20Sopenharmony_ci if (KeyValid) { 22188c2ecf20Sopenharmony_ci if (Register == REG_MAC_STA_ADDR) 22198c2ecf20Sopenharmony_ci Addr[0] = Control; 22208c2ecf20Sopenharmony_ci else if (Register == 22218c2ecf20Sopenharmony_ci (REG_MAC_STA_ADDR + 4)) 22228c2ecf20Sopenharmony_ci Addr[1] = Control; 22238c2ecf20Sopenharmony_ci KeyValid = false; 22248c2ecf20Sopenharmony_ci } else if ((Control & 0xff) == 0x5A) { 22258c2ecf20Sopenharmony_ci KeyValid = true; 22268c2ecf20Sopenharmony_ci Register = (u16) (Control >> 16); 22278c2ecf20Sopenharmony_ci } else { 22288c2ecf20Sopenharmony_ci /* assume data end while encount an invalid KEYWORD */ 22298c2ecf20Sopenharmony_ci break; 22308c2ecf20Sopenharmony_ci } 22318c2ecf20Sopenharmony_ci } else { 22328c2ecf20Sopenharmony_ci break; /* read error */ 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci i += 4; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); 22388c2ecf20Sopenharmony_ci *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]); 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci if (is_valid_ether_addr(EthAddr)) { 22418c2ecf20Sopenharmony_ci memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN); 22428c2ecf20Sopenharmony_ci return 0; 22438c2ecf20Sopenharmony_ci } 22448c2ecf20Sopenharmony_ci return 1; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci /* see if SPI flash exists? */ 22488c2ecf20Sopenharmony_ci Addr[0] = 0; 22498c2ecf20Sopenharmony_ci Addr[1] = 0; 22508c2ecf20Sopenharmony_ci Register = 0; 22518c2ecf20Sopenharmony_ci KeyValid = false; 22528c2ecf20Sopenharmony_ci i = 0; 22538c2ecf20Sopenharmony_ci while (1) { 22548c2ecf20Sopenharmony_ci if (atl2_spi_read(hw, i + 0x1f000, &Control)) { 22558c2ecf20Sopenharmony_ci if (KeyValid) { 22568c2ecf20Sopenharmony_ci if (Register == REG_MAC_STA_ADDR) 22578c2ecf20Sopenharmony_ci Addr[0] = Control; 22588c2ecf20Sopenharmony_ci else if (Register == (REG_MAC_STA_ADDR + 4)) 22598c2ecf20Sopenharmony_ci Addr[1] = Control; 22608c2ecf20Sopenharmony_ci KeyValid = false; 22618c2ecf20Sopenharmony_ci } else if ((Control & 0xff) == 0x5A) { 22628c2ecf20Sopenharmony_ci KeyValid = true; 22638c2ecf20Sopenharmony_ci Register = (u16) (Control >> 16); 22648c2ecf20Sopenharmony_ci } else { 22658c2ecf20Sopenharmony_ci break; /* data end */ 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ci } else { 22688c2ecf20Sopenharmony_ci break; /* read error */ 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci i += 4; 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); 22748c2ecf20Sopenharmony_ci *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]); 22758c2ecf20Sopenharmony_ci if (is_valid_ether_addr(EthAddr)) { 22768c2ecf20Sopenharmony_ci memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN); 22778c2ecf20Sopenharmony_ci return 0; 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci /* maybe MAC-address is from BIOS */ 22808c2ecf20Sopenharmony_ci Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); 22818c2ecf20Sopenharmony_ci Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4); 22828c2ecf20Sopenharmony_ci *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); 22838c2ecf20Sopenharmony_ci *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]); 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci if (is_valid_ether_addr(EthAddr)) { 22868c2ecf20Sopenharmony_ci memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN); 22878c2ecf20Sopenharmony_ci return 0; 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci return 1; 22918c2ecf20Sopenharmony_ci} 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci/* 22948c2ecf20Sopenharmony_ci * Reads the adapter's MAC address from the EEPROM 22958c2ecf20Sopenharmony_ci * 22968c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 22978c2ecf20Sopenharmony_ci */ 22988c2ecf20Sopenharmony_cistatic s32 atl2_read_mac_addr(struct atl2_hw *hw) 22998c2ecf20Sopenharmony_ci{ 23008c2ecf20Sopenharmony_ci if (get_permanent_address(hw)) { 23018c2ecf20Sopenharmony_ci /* for test */ 23028c2ecf20Sopenharmony_ci /* FIXME: shouldn't we use eth_random_addr() here? */ 23038c2ecf20Sopenharmony_ci hw->perm_mac_addr[0] = 0x00; 23048c2ecf20Sopenharmony_ci hw->perm_mac_addr[1] = 0x13; 23058c2ecf20Sopenharmony_ci hw->perm_mac_addr[2] = 0x74; 23068c2ecf20Sopenharmony_ci hw->perm_mac_addr[3] = 0x00; 23078c2ecf20Sopenharmony_ci hw->perm_mac_addr[4] = 0x5c; 23088c2ecf20Sopenharmony_ci hw->perm_mac_addr[5] = 0x38; 23098c2ecf20Sopenharmony_ci } 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci memcpy(hw->mac_addr, hw->perm_mac_addr, ETH_ALEN); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci return 0; 23148c2ecf20Sopenharmony_ci} 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci/* 23178c2ecf20Sopenharmony_ci * Hashes an address to determine its location in the multicast table 23188c2ecf20Sopenharmony_ci * 23198c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 23208c2ecf20Sopenharmony_ci * mc_addr - the multicast address to hash 23218c2ecf20Sopenharmony_ci * 23228c2ecf20Sopenharmony_ci * atl2_hash_mc_addr 23238c2ecf20Sopenharmony_ci * purpose 23248c2ecf20Sopenharmony_ci * set hash value for a multicast address 23258c2ecf20Sopenharmony_ci * hash calcu processing : 23268c2ecf20Sopenharmony_ci * 1. calcu 32bit CRC for multicast address 23278c2ecf20Sopenharmony_ci * 2. reverse crc with MSB to LSB 23288c2ecf20Sopenharmony_ci */ 23298c2ecf20Sopenharmony_cistatic u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr) 23308c2ecf20Sopenharmony_ci{ 23318c2ecf20Sopenharmony_ci u32 crc32, value; 23328c2ecf20Sopenharmony_ci int i; 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci value = 0; 23358c2ecf20Sopenharmony_ci crc32 = ether_crc_le(6, mc_addr); 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) 23388c2ecf20Sopenharmony_ci value |= (((crc32 >> i) & 1) << (31 - i)); 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci return value; 23418c2ecf20Sopenharmony_ci} 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci/* 23448c2ecf20Sopenharmony_ci * Sets the bit in the multicast table corresponding to the hash value. 23458c2ecf20Sopenharmony_ci * 23468c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 23478c2ecf20Sopenharmony_ci * hash_value - Multicast address hash value 23488c2ecf20Sopenharmony_ci */ 23498c2ecf20Sopenharmony_cistatic void atl2_hash_set(struct atl2_hw *hw, u32 hash_value) 23508c2ecf20Sopenharmony_ci{ 23518c2ecf20Sopenharmony_ci u32 hash_bit, hash_reg; 23528c2ecf20Sopenharmony_ci u32 mta; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci /* The HASH Table is a register array of 2 32-bit registers. 23558c2ecf20Sopenharmony_ci * It is treated like an array of 64 bits. We want to set 23568c2ecf20Sopenharmony_ci * bit BitArray[hash_value]. So we figure out what register 23578c2ecf20Sopenharmony_ci * the bit is in, read it, OR in the new bit, then write 23588c2ecf20Sopenharmony_ci * back the new value. The register is determined by the 23598c2ecf20Sopenharmony_ci * upper 7 bits of the hash value and the bit within that 23608c2ecf20Sopenharmony_ci * register are determined by the lower 5 bits of the value. 23618c2ecf20Sopenharmony_ci */ 23628c2ecf20Sopenharmony_ci hash_reg = (hash_value >> 31) & 0x1; 23638c2ecf20Sopenharmony_ci hash_bit = (hash_value >> 26) & 0x1F; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci mta |= (1 << hash_bit); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); 23708c2ecf20Sopenharmony_ci} 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci/* 23738c2ecf20Sopenharmony_ci * atl2_init_pcie - init PCIE module 23748c2ecf20Sopenharmony_ci */ 23758c2ecf20Sopenharmony_cistatic void atl2_init_pcie(struct atl2_hw *hw) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci u32 value; 23788c2ecf20Sopenharmony_ci value = LTSSM_TEST_MODE_DEF; 23798c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value); 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci value = PCIE_DLL_TX_CTRL1_DEF; 23828c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value); 23838c2ecf20Sopenharmony_ci} 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_cistatic void atl2_init_flash_opcode(struct atl2_hw *hw) 23868c2ecf20Sopenharmony_ci{ 23878c2ecf20Sopenharmony_ci if (hw->flash_vendor >= ARRAY_SIZE(flash_table)) 23888c2ecf20Sopenharmony_ci hw->flash_vendor = 0; /* ATMEL */ 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci /* Init OP table */ 23918c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM, 23928c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdPROGRAM); 23938c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE, 23948c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdSECTOR_ERASE); 23958c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE, 23968c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdCHIP_ERASE); 23978c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID, 23988c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdRDID); 23998c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN, 24008c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdWREN); 24018c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR, 24028c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdRDSR); 24038c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR, 24048c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdWRSR); 24058c2ecf20Sopenharmony_ci ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ, 24068c2ecf20Sopenharmony_ci flash_table[hw->flash_vendor].cmdREAD); 24078c2ecf20Sopenharmony_ci} 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci/******************************************************************** 24108c2ecf20Sopenharmony_ci* Performs basic configuration of the adapter. 24118c2ecf20Sopenharmony_ci* 24128c2ecf20Sopenharmony_ci* hw - Struct containing variables accessed by shared code 24138c2ecf20Sopenharmony_ci* Assumes that the controller has previously been reset and is in a 24148c2ecf20Sopenharmony_ci* post-reset uninitialized state. Initializes multicast table, 24158c2ecf20Sopenharmony_ci* and Calls routines to setup link 24168c2ecf20Sopenharmony_ci* Leaves the transmit and receive units disabled and uninitialized. 24178c2ecf20Sopenharmony_ci********************************************************************/ 24188c2ecf20Sopenharmony_cistatic s32 atl2_init_hw(struct atl2_hw *hw) 24198c2ecf20Sopenharmony_ci{ 24208c2ecf20Sopenharmony_ci u32 ret_val = 0; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci atl2_init_pcie(hw); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci /* Zero out the Multicast HASH table */ 24258c2ecf20Sopenharmony_ci /* clear the old settings from the multicast hash table */ 24268c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); 24278c2ecf20Sopenharmony_ci ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci atl2_init_flash_opcode(hw); 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci ret_val = atl2_phy_init(hw); 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci return ret_val; 24348c2ecf20Sopenharmony_ci} 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci/* 24378c2ecf20Sopenharmony_ci * Detects the current speed and duplex settings of the hardware. 24388c2ecf20Sopenharmony_ci * 24398c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 24408c2ecf20Sopenharmony_ci * speed - Speed of the connection 24418c2ecf20Sopenharmony_ci * duplex - Duplex setting of the connection 24428c2ecf20Sopenharmony_ci */ 24438c2ecf20Sopenharmony_cistatic s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, 24448c2ecf20Sopenharmony_ci u16 *duplex) 24458c2ecf20Sopenharmony_ci{ 24468c2ecf20Sopenharmony_ci s32 ret_val; 24478c2ecf20Sopenharmony_ci u16 phy_data; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci /* Read PHY Specific Status Register (17) */ 24508c2ecf20Sopenharmony_ci ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data); 24518c2ecf20Sopenharmony_ci if (ret_val) 24528c2ecf20Sopenharmony_ci return ret_val; 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED)) 24558c2ecf20Sopenharmony_ci return ATLX_ERR_PHY_RES; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci switch (phy_data & MII_ATLX_PSSR_SPEED) { 24588c2ecf20Sopenharmony_ci case MII_ATLX_PSSR_100MBS: 24598c2ecf20Sopenharmony_ci *speed = SPEED_100; 24608c2ecf20Sopenharmony_ci break; 24618c2ecf20Sopenharmony_ci case MII_ATLX_PSSR_10MBS: 24628c2ecf20Sopenharmony_ci *speed = SPEED_10; 24638c2ecf20Sopenharmony_ci break; 24648c2ecf20Sopenharmony_ci default: 24658c2ecf20Sopenharmony_ci return ATLX_ERR_PHY_SPEED; 24668c2ecf20Sopenharmony_ci } 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci if (phy_data & MII_ATLX_PSSR_DPLX) 24698c2ecf20Sopenharmony_ci *duplex = FULL_DUPLEX; 24708c2ecf20Sopenharmony_ci else 24718c2ecf20Sopenharmony_ci *duplex = HALF_DUPLEX; 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci return 0; 24748c2ecf20Sopenharmony_ci} 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci/* 24778c2ecf20Sopenharmony_ci * Reads the value from a PHY register 24788c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 24798c2ecf20Sopenharmony_ci * reg_addr - address of the PHY register to read 24808c2ecf20Sopenharmony_ci */ 24818c2ecf20Sopenharmony_cistatic s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data) 24828c2ecf20Sopenharmony_ci{ 24838c2ecf20Sopenharmony_ci u32 val; 24848c2ecf20Sopenharmony_ci int i; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | 24878c2ecf20Sopenharmony_ci MDIO_START | 24888c2ecf20Sopenharmony_ci MDIO_SUP_PREAMBLE | 24898c2ecf20Sopenharmony_ci MDIO_RW | 24908c2ecf20Sopenharmony_ci MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; 24918c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci wmb(); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci for (i = 0; i < MDIO_WAIT_TIMES; i++) { 24968c2ecf20Sopenharmony_ci udelay(2); 24978c2ecf20Sopenharmony_ci val = ATL2_READ_REG(hw, REG_MDIO_CTRL); 24988c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 24998c2ecf20Sopenharmony_ci break; 25008c2ecf20Sopenharmony_ci wmb(); 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) { 25038c2ecf20Sopenharmony_ci *phy_data = (u16)val; 25048c2ecf20Sopenharmony_ci return 0; 25058c2ecf20Sopenharmony_ci } 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci return ATLX_ERR_PHY; 25088c2ecf20Sopenharmony_ci} 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci/* 25118c2ecf20Sopenharmony_ci * Writes a value to a PHY register 25128c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 25138c2ecf20Sopenharmony_ci * reg_addr - address of the PHY register to write 25148c2ecf20Sopenharmony_ci * data - data to write to the PHY 25158c2ecf20Sopenharmony_ci */ 25168c2ecf20Sopenharmony_cistatic s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data) 25178c2ecf20Sopenharmony_ci{ 25188c2ecf20Sopenharmony_ci int i; 25198c2ecf20Sopenharmony_ci u32 val; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | 25228c2ecf20Sopenharmony_ci (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | 25238c2ecf20Sopenharmony_ci MDIO_SUP_PREAMBLE | 25248c2ecf20Sopenharmony_ci MDIO_START | 25258c2ecf20Sopenharmony_ci MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; 25268c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci wmb(); 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci for (i = 0; i < MDIO_WAIT_TIMES; i++) { 25318c2ecf20Sopenharmony_ci udelay(2); 25328c2ecf20Sopenharmony_ci val = ATL2_READ_REG(hw, REG_MDIO_CTRL); 25338c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 25348c2ecf20Sopenharmony_ci break; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci wmb(); 25378c2ecf20Sopenharmony_ci } 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 25408c2ecf20Sopenharmony_ci return 0; 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci return ATLX_ERR_PHY; 25438c2ecf20Sopenharmony_ci} 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci/* 25468c2ecf20Sopenharmony_ci * Configures PHY autoneg and flow control advertisement settings 25478c2ecf20Sopenharmony_ci * 25488c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 25498c2ecf20Sopenharmony_ci */ 25508c2ecf20Sopenharmony_cistatic s32 atl2_phy_setup_autoneg_adv(struct atl2_hw *hw) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci s32 ret_val; 25538c2ecf20Sopenharmony_ci s16 mii_autoneg_adv_reg; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci /* Read the MII Auto-Neg Advertisement Register (Address 4). */ 25568c2ecf20Sopenharmony_ci mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci /* Need to parse autoneg_advertised and set up 25598c2ecf20Sopenharmony_ci * the appropriate PHY registers. First we will parse for 25608c2ecf20Sopenharmony_ci * autoneg_advertised software override. Since we can advertise 25618c2ecf20Sopenharmony_ci * a plethora of combinations, we need to check each bit 25628c2ecf20Sopenharmony_ci * individually. 25638c2ecf20Sopenharmony_ci */ 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci /* First we clear all the 10/100 mb speed bits in the Auto-Neg 25668c2ecf20Sopenharmony_ci * Advertisement Register (Address 4) and the 1000 mb speed bits in 25678c2ecf20Sopenharmony_ci * the 1000Base-T Control Register (Address 9). */ 25688c2ecf20Sopenharmony_ci mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci /* Need to parse MediaType and setup the 25718c2ecf20Sopenharmony_ci * appropriate PHY registers. */ 25728c2ecf20Sopenharmony_ci switch (hw->MediaType) { 25738c2ecf20Sopenharmony_ci case MEDIA_TYPE_AUTO_SENSOR: 25748c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= 25758c2ecf20Sopenharmony_ci (MII_AR_10T_HD_CAPS | 25768c2ecf20Sopenharmony_ci MII_AR_10T_FD_CAPS | 25778c2ecf20Sopenharmony_ci MII_AR_100TX_HD_CAPS| 25788c2ecf20Sopenharmony_ci MII_AR_100TX_FD_CAPS); 25798c2ecf20Sopenharmony_ci hw->autoneg_advertised = 25808c2ecf20Sopenharmony_ci ADVERTISE_10_HALF | 25818c2ecf20Sopenharmony_ci ADVERTISE_10_FULL | 25828c2ecf20Sopenharmony_ci ADVERTISE_100_HALF| 25838c2ecf20Sopenharmony_ci ADVERTISE_100_FULL; 25848c2ecf20Sopenharmony_ci break; 25858c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 25868c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; 25878c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_100_FULL; 25888c2ecf20Sopenharmony_ci break; 25898c2ecf20Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 25908c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; 25918c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_100_HALF; 25928c2ecf20Sopenharmony_ci break; 25938c2ecf20Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 25948c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; 25958c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_10_FULL; 25968c2ecf20Sopenharmony_ci break; 25978c2ecf20Sopenharmony_ci default: 25988c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; 25998c2ecf20Sopenharmony_ci hw->autoneg_advertised = ADVERTISE_10_HALF; 26008c2ecf20Sopenharmony_ci break; 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci /* flow control fixed to enable all */ 26048c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci ret_val = atl2_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci if (ret_val) 26118c2ecf20Sopenharmony_ci return ret_val; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci return 0; 26148c2ecf20Sopenharmony_ci} 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci/* 26178c2ecf20Sopenharmony_ci * Resets the PHY and make all config validate 26188c2ecf20Sopenharmony_ci * 26198c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code 26208c2ecf20Sopenharmony_ci * 26218c2ecf20Sopenharmony_ci * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) 26228c2ecf20Sopenharmony_ci */ 26238c2ecf20Sopenharmony_cistatic s32 atl2_phy_commit(struct atl2_hw *hw) 26248c2ecf20Sopenharmony_ci{ 26258c2ecf20Sopenharmony_ci s32 ret_val; 26268c2ecf20Sopenharmony_ci u16 phy_data; 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG; 26298c2ecf20Sopenharmony_ci ret_val = atl2_write_phy_reg(hw, MII_BMCR, phy_data); 26308c2ecf20Sopenharmony_ci if (ret_val) { 26318c2ecf20Sopenharmony_ci u32 val; 26328c2ecf20Sopenharmony_ci int i; 26338c2ecf20Sopenharmony_ci /* pcie serdes link may be down ! */ 26348c2ecf20Sopenharmony_ci for (i = 0; i < 25; i++) { 26358c2ecf20Sopenharmony_ci msleep(1); 26368c2ecf20Sopenharmony_ci val = ATL2_READ_REG(hw, REG_MDIO_CTRL); 26378c2ecf20Sopenharmony_ci if (!(val & (MDIO_START | MDIO_BUSY))) 26388c2ecf20Sopenharmony_ci break; 26398c2ecf20Sopenharmony_ci } 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci if (0 != (val & (MDIO_START | MDIO_BUSY))) { 26428c2ecf20Sopenharmony_ci printk(KERN_ERR "atl2: PCIe link down for at least 25ms !\n"); 26438c2ecf20Sopenharmony_ci return ret_val; 26448c2ecf20Sopenharmony_ci } 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci return 0; 26478c2ecf20Sopenharmony_ci} 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_cistatic s32 atl2_phy_init(struct atl2_hw *hw) 26508c2ecf20Sopenharmony_ci{ 26518c2ecf20Sopenharmony_ci s32 ret_val; 26528c2ecf20Sopenharmony_ci u16 phy_val; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci if (hw->phy_configured) 26558c2ecf20Sopenharmony_ci return 0; 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci /* Enable PHY */ 26588c2ecf20Sopenharmony_ci ATL2_WRITE_REGW(hw, REG_PHY_ENABLE, 1); 26598c2ecf20Sopenharmony_ci ATL2_WRITE_FLUSH(hw); 26608c2ecf20Sopenharmony_ci msleep(1); 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci /* check if the PHY is in powersaving mode */ 26638c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); 26648c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci /* 024E / 124E 0r 0274 / 1274 ? */ 26678c2ecf20Sopenharmony_ci if (phy_val & 0x1000) { 26688c2ecf20Sopenharmony_ci phy_val &= ~0x1000; 26698c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val); 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci msleep(1); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci /*Enable PHY LinkChange Interrupt */ 26758c2ecf20Sopenharmony_ci ret_val = atl2_write_phy_reg(hw, 18, 0xC00); 26768c2ecf20Sopenharmony_ci if (ret_val) 26778c2ecf20Sopenharmony_ci return ret_val; 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci /* setup AutoNeg parameters */ 26808c2ecf20Sopenharmony_ci ret_val = atl2_phy_setup_autoneg_adv(hw); 26818c2ecf20Sopenharmony_ci if (ret_val) 26828c2ecf20Sopenharmony_ci return ret_val; 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci /* SW.Reset & En-Auto-Neg to restart Auto-Neg */ 26858c2ecf20Sopenharmony_ci ret_val = atl2_phy_commit(hw); 26868c2ecf20Sopenharmony_ci if (ret_val) 26878c2ecf20Sopenharmony_ci return ret_val; 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci hw->phy_configured = true; 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci return ret_val; 26928c2ecf20Sopenharmony_ci} 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_cistatic void atl2_set_mac_addr(struct atl2_hw *hw) 26958c2ecf20Sopenharmony_ci{ 26968c2ecf20Sopenharmony_ci u32 value; 26978c2ecf20Sopenharmony_ci /* 00-0B-6A-F6-00-DC 26988c2ecf20Sopenharmony_ci * 0: 6AF600DC 1: 000B 26998c2ecf20Sopenharmony_ci * low dword */ 27008c2ecf20Sopenharmony_ci value = (((u32)hw->mac_addr[2]) << 24) | 27018c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[3]) << 16) | 27028c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[4]) << 8) | 27038c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[5])); 27048c2ecf20Sopenharmony_ci ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); 27058c2ecf20Sopenharmony_ci /* hight dword */ 27068c2ecf20Sopenharmony_ci value = (((u32)hw->mac_addr[0]) << 8) | 27078c2ecf20Sopenharmony_ci (((u32)hw->mac_addr[1])); 27088c2ecf20Sopenharmony_ci ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); 27098c2ecf20Sopenharmony_ci} 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci/* 27128c2ecf20Sopenharmony_ci * check_eeprom_exist 27138c2ecf20Sopenharmony_ci * return 0 if eeprom exist 27148c2ecf20Sopenharmony_ci */ 27158c2ecf20Sopenharmony_cistatic int atl2_check_eeprom_exist(struct atl2_hw *hw) 27168c2ecf20Sopenharmony_ci{ 27178c2ecf20Sopenharmony_ci u32 value; 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); 27208c2ecf20Sopenharmony_ci if (value & SPI_FLASH_CTRL_EN_VPD) { 27218c2ecf20Sopenharmony_ci value &= ~SPI_FLASH_CTRL_EN_VPD; 27228c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); 27238c2ecf20Sopenharmony_ci } 27248c2ecf20Sopenharmony_ci value = ATL2_READ_REGW(hw, REG_PCIE_CAP_LIST); 27258c2ecf20Sopenharmony_ci return ((value & 0xFF00) == 0x6C00) ? 0 : 1; 27268c2ecf20Sopenharmony_ci} 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci/* FIXME: This doesn't look right. -- CHS */ 27298c2ecf20Sopenharmony_cistatic bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value) 27308c2ecf20Sopenharmony_ci{ 27318c2ecf20Sopenharmony_ci return true; 27328c2ecf20Sopenharmony_ci} 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_cistatic bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue) 27358c2ecf20Sopenharmony_ci{ 27368c2ecf20Sopenharmony_ci int i; 27378c2ecf20Sopenharmony_ci u32 Control; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci if (Offset & 0x3) 27408c2ecf20Sopenharmony_ci return false; /* address do not align */ 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_VPD_DATA, 0); 27438c2ecf20Sopenharmony_ci Control = (Offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; 27448c2ecf20Sopenharmony_ci ATL2_WRITE_REG(hw, REG_VPD_CAP, Control); 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 27478c2ecf20Sopenharmony_ci msleep(2); 27488c2ecf20Sopenharmony_ci Control = ATL2_READ_REG(hw, REG_VPD_CAP); 27498c2ecf20Sopenharmony_ci if (Control & VPD_CAP_VPD_FLAG) 27508c2ecf20Sopenharmony_ci break; 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci if (Control & VPD_CAP_VPD_FLAG) { 27548c2ecf20Sopenharmony_ci *pValue = ATL2_READ_REG(hw, REG_VPD_DATA); 27558c2ecf20Sopenharmony_ci return true; 27568c2ecf20Sopenharmony_ci } 27578c2ecf20Sopenharmony_ci return false; /* timeout */ 27588c2ecf20Sopenharmony_ci} 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_cistatic void atl2_force_ps(struct atl2_hw *hw) 27618c2ecf20Sopenharmony_ci{ 27628c2ecf20Sopenharmony_ci u16 phy_val; 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); 27658c2ecf20Sopenharmony_ci atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); 27668c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val | 0x1000); 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_ADDR, 2); 27698c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_DATA, 0x3000); 27708c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_ADDR, 3); 27718c2ecf20Sopenharmony_ci atl2_write_phy_reg(hw, MII_DBG_DATA, 0); 27728c2ecf20Sopenharmony_ci} 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci/* This is the only thing that needs to be changed to adjust the 27758c2ecf20Sopenharmony_ci * maximum number of ports that the driver can manage. 27768c2ecf20Sopenharmony_ci */ 27778c2ecf20Sopenharmony_ci#define ATL2_MAX_NIC 4 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci#define OPTION_UNSET -1 27808c2ecf20Sopenharmony_ci#define OPTION_DISABLED 0 27818c2ecf20Sopenharmony_ci#define OPTION_ENABLED 1 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci/* All parameters are treated the same, as an integer array of values. 27848c2ecf20Sopenharmony_ci * This macro just reduces the need to repeat the same declaration code 27858c2ecf20Sopenharmony_ci * over and over (plus this helps to avoid typo bugs). 27868c2ecf20Sopenharmony_ci */ 27878c2ecf20Sopenharmony_ci#define ATL2_PARAM_INIT {[0 ... ATL2_MAX_NIC] = OPTION_UNSET} 27888c2ecf20Sopenharmony_ci#ifndef module_param_array 27898c2ecf20Sopenharmony_ci/* Module Parameters are always initialized to -1, so that the driver 27908c2ecf20Sopenharmony_ci * can tell the difference between no user specified value or the 27918c2ecf20Sopenharmony_ci * user asking for the default value. 27928c2ecf20Sopenharmony_ci * The true default values are loaded in when atl2_check_options is called. 27938c2ecf20Sopenharmony_ci * 27948c2ecf20Sopenharmony_ci * This is a GCC extension to ANSI C. 27958c2ecf20Sopenharmony_ci * See the item "Labeled Elements in Initializers" in the section 27968c2ecf20Sopenharmony_ci * "Extensions to the C Language Family" of the GCC documentation. 27978c2ecf20Sopenharmony_ci */ 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci#define ATL2_PARAM(X, desc) \ 28008c2ecf20Sopenharmony_ci static const int X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \ 28018c2ecf20Sopenharmony_ci MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \ 28028c2ecf20Sopenharmony_ci MODULE_PARM_DESC(X, desc); 28038c2ecf20Sopenharmony_ci#else 28048c2ecf20Sopenharmony_ci#define ATL2_PARAM(X, desc) \ 28058c2ecf20Sopenharmony_ci static int X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \ 28068c2ecf20Sopenharmony_ci static unsigned int num_##X; \ 28078c2ecf20Sopenharmony_ci module_param_array_named(X, X, int, &num_##X, 0); \ 28088c2ecf20Sopenharmony_ci MODULE_PARM_DESC(X, desc); 28098c2ecf20Sopenharmony_ci#endif 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci/* 28128c2ecf20Sopenharmony_ci * Transmit Memory Size 28138c2ecf20Sopenharmony_ci * Valid Range: 64-2048 28148c2ecf20Sopenharmony_ci * Default Value: 128 28158c2ecf20Sopenharmony_ci */ 28168c2ecf20Sopenharmony_ci#define ATL2_MIN_TX_MEMSIZE 4 /* 4KB */ 28178c2ecf20Sopenharmony_ci#define ATL2_MAX_TX_MEMSIZE 64 /* 64KB */ 28188c2ecf20Sopenharmony_ci#define ATL2_DEFAULT_TX_MEMSIZE 8 /* 8KB */ 28198c2ecf20Sopenharmony_ciATL2_PARAM(TxMemSize, "Bytes of Transmit Memory"); 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci/* 28228c2ecf20Sopenharmony_ci * Receive Memory Block Count 28238c2ecf20Sopenharmony_ci * Valid Range: 16-512 28248c2ecf20Sopenharmony_ci * Default Value: 128 28258c2ecf20Sopenharmony_ci */ 28268c2ecf20Sopenharmony_ci#define ATL2_MIN_RXD_COUNT 16 28278c2ecf20Sopenharmony_ci#define ATL2_MAX_RXD_COUNT 512 28288c2ecf20Sopenharmony_ci#define ATL2_DEFAULT_RXD_COUNT 64 28298c2ecf20Sopenharmony_ciATL2_PARAM(RxMemBlock, "Number of receive memory block"); 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci/* 28328c2ecf20Sopenharmony_ci * User Specified MediaType Override 28338c2ecf20Sopenharmony_ci * 28348c2ecf20Sopenharmony_ci * Valid Range: 0-5 28358c2ecf20Sopenharmony_ci * - 0 - auto-negotiate at all supported speeds 28368c2ecf20Sopenharmony_ci * - 1 - only link at 1000Mbps Full Duplex 28378c2ecf20Sopenharmony_ci * - 2 - only link at 100Mbps Full Duplex 28388c2ecf20Sopenharmony_ci * - 3 - only link at 100Mbps Half Duplex 28398c2ecf20Sopenharmony_ci * - 4 - only link at 10Mbps Full Duplex 28408c2ecf20Sopenharmony_ci * - 5 - only link at 10Mbps Half Duplex 28418c2ecf20Sopenharmony_ci * Default Value: 0 28428c2ecf20Sopenharmony_ci */ 28438c2ecf20Sopenharmony_ciATL2_PARAM(MediaType, "MediaType Select"); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci/* 28468c2ecf20Sopenharmony_ci * Interrupt Moderate Timer in units of 2048 ns (~2 us) 28478c2ecf20Sopenharmony_ci * Valid Range: 10-65535 28488c2ecf20Sopenharmony_ci * Default Value: 45000(90ms) 28498c2ecf20Sopenharmony_ci */ 28508c2ecf20Sopenharmony_ci#define INT_MOD_DEFAULT_CNT 100 /* 200us */ 28518c2ecf20Sopenharmony_ci#define INT_MOD_MAX_CNT 65000 28528c2ecf20Sopenharmony_ci#define INT_MOD_MIN_CNT 50 28538c2ecf20Sopenharmony_ciATL2_PARAM(IntModTimer, "Interrupt Moderator Timer"); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci/* 28568c2ecf20Sopenharmony_ci * FlashVendor 28578c2ecf20Sopenharmony_ci * Valid Range: 0-2 28588c2ecf20Sopenharmony_ci * 0 - Atmel 28598c2ecf20Sopenharmony_ci * 1 - SST 28608c2ecf20Sopenharmony_ci * 2 - ST 28618c2ecf20Sopenharmony_ci */ 28628c2ecf20Sopenharmony_ciATL2_PARAM(FlashVendor, "SPI Flash Vendor"); 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci#define AUTONEG_ADV_DEFAULT 0x2F 28658c2ecf20Sopenharmony_ci#define AUTONEG_ADV_MASK 0x2F 28668c2ecf20Sopenharmony_ci#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci#define FLASH_VENDOR_DEFAULT 0 28698c2ecf20Sopenharmony_ci#define FLASH_VENDOR_MIN 0 28708c2ecf20Sopenharmony_ci#define FLASH_VENDOR_MAX 2 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_cistruct atl2_option { 28738c2ecf20Sopenharmony_ci enum { enable_option, range_option, list_option } type; 28748c2ecf20Sopenharmony_ci char *name; 28758c2ecf20Sopenharmony_ci char *err; 28768c2ecf20Sopenharmony_ci int def; 28778c2ecf20Sopenharmony_ci union { 28788c2ecf20Sopenharmony_ci struct { /* range_option info */ 28798c2ecf20Sopenharmony_ci int min; 28808c2ecf20Sopenharmony_ci int max; 28818c2ecf20Sopenharmony_ci } r; 28828c2ecf20Sopenharmony_ci struct { /* list_option info */ 28838c2ecf20Sopenharmony_ci int nr; 28848c2ecf20Sopenharmony_ci struct atl2_opt_list { int i; char *str; } *p; 28858c2ecf20Sopenharmony_ci } l; 28868c2ecf20Sopenharmony_ci } arg; 28878c2ecf20Sopenharmony_ci}; 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_cistatic int atl2_validate_option(int *value, struct atl2_option *opt) 28908c2ecf20Sopenharmony_ci{ 28918c2ecf20Sopenharmony_ci int i; 28928c2ecf20Sopenharmony_ci struct atl2_opt_list *ent; 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci if (*value == OPTION_UNSET) { 28958c2ecf20Sopenharmony_ci *value = opt->def; 28968c2ecf20Sopenharmony_ci return 0; 28978c2ecf20Sopenharmony_ci } 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci switch (opt->type) { 29008c2ecf20Sopenharmony_ci case enable_option: 29018c2ecf20Sopenharmony_ci switch (*value) { 29028c2ecf20Sopenharmony_ci case OPTION_ENABLED: 29038c2ecf20Sopenharmony_ci printk(KERN_INFO "%s Enabled\n", opt->name); 29048c2ecf20Sopenharmony_ci return 0; 29058c2ecf20Sopenharmony_ci case OPTION_DISABLED: 29068c2ecf20Sopenharmony_ci printk(KERN_INFO "%s Disabled\n", opt->name); 29078c2ecf20Sopenharmony_ci return 0; 29088c2ecf20Sopenharmony_ci } 29098c2ecf20Sopenharmony_ci break; 29108c2ecf20Sopenharmony_ci case range_option: 29118c2ecf20Sopenharmony_ci if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { 29128c2ecf20Sopenharmony_ci printk(KERN_INFO "%s set to %i\n", opt->name, *value); 29138c2ecf20Sopenharmony_ci return 0; 29148c2ecf20Sopenharmony_ci } 29158c2ecf20Sopenharmony_ci break; 29168c2ecf20Sopenharmony_ci case list_option: 29178c2ecf20Sopenharmony_ci for (i = 0; i < opt->arg.l.nr; i++) { 29188c2ecf20Sopenharmony_ci ent = &opt->arg.l.p[i]; 29198c2ecf20Sopenharmony_ci if (*value == ent->i) { 29208c2ecf20Sopenharmony_ci if (ent->str[0] != '\0') 29218c2ecf20Sopenharmony_ci printk(KERN_INFO "%s\n", ent->str); 29228c2ecf20Sopenharmony_ci return 0; 29238c2ecf20Sopenharmony_ci } 29248c2ecf20Sopenharmony_ci } 29258c2ecf20Sopenharmony_ci break; 29268c2ecf20Sopenharmony_ci default: 29278c2ecf20Sopenharmony_ci BUG(); 29288c2ecf20Sopenharmony_ci } 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci printk(KERN_INFO "Invalid %s specified (%i) %s\n", 29318c2ecf20Sopenharmony_ci opt->name, *value, opt->err); 29328c2ecf20Sopenharmony_ci *value = opt->def; 29338c2ecf20Sopenharmony_ci return -1; 29348c2ecf20Sopenharmony_ci} 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci/** 29378c2ecf20Sopenharmony_ci * atl2_check_options - Range Checking for Command Line Parameters 29388c2ecf20Sopenharmony_ci * @adapter: board private structure 29398c2ecf20Sopenharmony_ci * 29408c2ecf20Sopenharmony_ci * This routine checks all command line parameters for valid user 29418c2ecf20Sopenharmony_ci * input. If an invalid value is given, or if no user specified 29428c2ecf20Sopenharmony_ci * value exists, a default value is used. The final value is stored 29438c2ecf20Sopenharmony_ci * in a variable in the adapter structure. 29448c2ecf20Sopenharmony_ci */ 29458c2ecf20Sopenharmony_cistatic void atl2_check_options(struct atl2_adapter *adapter) 29468c2ecf20Sopenharmony_ci{ 29478c2ecf20Sopenharmony_ci int val; 29488c2ecf20Sopenharmony_ci struct atl2_option opt; 29498c2ecf20Sopenharmony_ci int bd = adapter->bd_number; 29508c2ecf20Sopenharmony_ci if (bd >= ATL2_MAX_NIC) { 29518c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Warning: no configuration for board #%i\n", 29528c2ecf20Sopenharmony_ci bd); 29538c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Using defaults for all values\n"); 29548c2ecf20Sopenharmony_ci#ifndef module_param_array 29558c2ecf20Sopenharmony_ci bd = ATL2_MAX_NIC; 29568c2ecf20Sopenharmony_ci#endif 29578c2ecf20Sopenharmony_ci } 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci /* Bytes of Transmit Memory */ 29608c2ecf20Sopenharmony_ci opt.type = range_option; 29618c2ecf20Sopenharmony_ci opt.name = "Bytes of Transmit Memory"; 29628c2ecf20Sopenharmony_ci opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_TX_MEMSIZE); 29638c2ecf20Sopenharmony_ci opt.def = ATL2_DEFAULT_TX_MEMSIZE; 29648c2ecf20Sopenharmony_ci opt.arg.r.min = ATL2_MIN_TX_MEMSIZE; 29658c2ecf20Sopenharmony_ci opt.arg.r.max = ATL2_MAX_TX_MEMSIZE; 29668c2ecf20Sopenharmony_ci#ifdef module_param_array 29678c2ecf20Sopenharmony_ci if (num_TxMemSize > bd) { 29688c2ecf20Sopenharmony_ci#endif 29698c2ecf20Sopenharmony_ci val = TxMemSize[bd]; 29708c2ecf20Sopenharmony_ci atl2_validate_option(&val, &opt); 29718c2ecf20Sopenharmony_ci adapter->txd_ring_size = ((u32) val) * 1024; 29728c2ecf20Sopenharmony_ci#ifdef module_param_array 29738c2ecf20Sopenharmony_ci } else 29748c2ecf20Sopenharmony_ci adapter->txd_ring_size = ((u32)opt.def) * 1024; 29758c2ecf20Sopenharmony_ci#endif 29768c2ecf20Sopenharmony_ci /* txs ring size: */ 29778c2ecf20Sopenharmony_ci adapter->txs_ring_size = adapter->txd_ring_size / 128; 29788c2ecf20Sopenharmony_ci if (adapter->txs_ring_size > 160) 29798c2ecf20Sopenharmony_ci adapter->txs_ring_size = 160; 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci /* Receive Memory Block Count */ 29828c2ecf20Sopenharmony_ci opt.type = range_option; 29838c2ecf20Sopenharmony_ci opt.name = "Number of receive memory block"; 29848c2ecf20Sopenharmony_ci opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_RXD_COUNT); 29858c2ecf20Sopenharmony_ci opt.def = ATL2_DEFAULT_RXD_COUNT; 29868c2ecf20Sopenharmony_ci opt.arg.r.min = ATL2_MIN_RXD_COUNT; 29878c2ecf20Sopenharmony_ci opt.arg.r.max = ATL2_MAX_RXD_COUNT; 29888c2ecf20Sopenharmony_ci#ifdef module_param_array 29898c2ecf20Sopenharmony_ci if (num_RxMemBlock > bd) { 29908c2ecf20Sopenharmony_ci#endif 29918c2ecf20Sopenharmony_ci val = RxMemBlock[bd]; 29928c2ecf20Sopenharmony_ci atl2_validate_option(&val, &opt); 29938c2ecf20Sopenharmony_ci adapter->rxd_ring_size = (u32)val; 29948c2ecf20Sopenharmony_ci /* FIXME */ 29958c2ecf20Sopenharmony_ci /* ((u16)val)&~1; */ /* even number */ 29968c2ecf20Sopenharmony_ci#ifdef module_param_array 29978c2ecf20Sopenharmony_ci } else 29988c2ecf20Sopenharmony_ci adapter->rxd_ring_size = (u32)opt.def; 29998c2ecf20Sopenharmony_ci#endif 30008c2ecf20Sopenharmony_ci /* init RXD Flow control value */ 30018c2ecf20Sopenharmony_ci adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size / 8) * 7; 30028c2ecf20Sopenharmony_ci adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT / 8) > 30038c2ecf20Sopenharmony_ci (adapter->rxd_ring_size / 12) ? (ATL2_MIN_RXD_COUNT / 8) : 30048c2ecf20Sopenharmony_ci (adapter->rxd_ring_size / 12); 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci /* Interrupt Moderate Timer */ 30078c2ecf20Sopenharmony_ci opt.type = range_option; 30088c2ecf20Sopenharmony_ci opt.name = "Interrupt Moderate Timer"; 30098c2ecf20Sopenharmony_ci opt.err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT); 30108c2ecf20Sopenharmony_ci opt.def = INT_MOD_DEFAULT_CNT; 30118c2ecf20Sopenharmony_ci opt.arg.r.min = INT_MOD_MIN_CNT; 30128c2ecf20Sopenharmony_ci opt.arg.r.max = INT_MOD_MAX_CNT; 30138c2ecf20Sopenharmony_ci#ifdef module_param_array 30148c2ecf20Sopenharmony_ci if (num_IntModTimer > bd) { 30158c2ecf20Sopenharmony_ci#endif 30168c2ecf20Sopenharmony_ci val = IntModTimer[bd]; 30178c2ecf20Sopenharmony_ci atl2_validate_option(&val, &opt); 30188c2ecf20Sopenharmony_ci adapter->imt = (u16) val; 30198c2ecf20Sopenharmony_ci#ifdef module_param_array 30208c2ecf20Sopenharmony_ci } else 30218c2ecf20Sopenharmony_ci adapter->imt = (u16)(opt.def); 30228c2ecf20Sopenharmony_ci#endif 30238c2ecf20Sopenharmony_ci /* Flash Vendor */ 30248c2ecf20Sopenharmony_ci opt.type = range_option; 30258c2ecf20Sopenharmony_ci opt.name = "SPI Flash Vendor"; 30268c2ecf20Sopenharmony_ci opt.err = "using default of " __MODULE_STRING(FLASH_VENDOR_DEFAULT); 30278c2ecf20Sopenharmony_ci opt.def = FLASH_VENDOR_DEFAULT; 30288c2ecf20Sopenharmony_ci opt.arg.r.min = FLASH_VENDOR_MIN; 30298c2ecf20Sopenharmony_ci opt.arg.r.max = FLASH_VENDOR_MAX; 30308c2ecf20Sopenharmony_ci#ifdef module_param_array 30318c2ecf20Sopenharmony_ci if (num_FlashVendor > bd) { 30328c2ecf20Sopenharmony_ci#endif 30338c2ecf20Sopenharmony_ci val = FlashVendor[bd]; 30348c2ecf20Sopenharmony_ci atl2_validate_option(&val, &opt); 30358c2ecf20Sopenharmony_ci adapter->hw.flash_vendor = (u8) val; 30368c2ecf20Sopenharmony_ci#ifdef module_param_array 30378c2ecf20Sopenharmony_ci } else 30388c2ecf20Sopenharmony_ci adapter->hw.flash_vendor = (u8)(opt.def); 30398c2ecf20Sopenharmony_ci#endif 30408c2ecf20Sopenharmony_ci /* MediaType */ 30418c2ecf20Sopenharmony_ci opt.type = range_option; 30428c2ecf20Sopenharmony_ci opt.name = "Speed/Duplex Selection"; 30438c2ecf20Sopenharmony_ci opt.err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR); 30448c2ecf20Sopenharmony_ci opt.def = MEDIA_TYPE_AUTO_SENSOR; 30458c2ecf20Sopenharmony_ci opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR; 30468c2ecf20Sopenharmony_ci opt.arg.r.max = MEDIA_TYPE_10M_HALF; 30478c2ecf20Sopenharmony_ci#ifdef module_param_array 30488c2ecf20Sopenharmony_ci if (num_MediaType > bd) { 30498c2ecf20Sopenharmony_ci#endif 30508c2ecf20Sopenharmony_ci val = MediaType[bd]; 30518c2ecf20Sopenharmony_ci atl2_validate_option(&val, &opt); 30528c2ecf20Sopenharmony_ci adapter->hw.MediaType = (u16) val; 30538c2ecf20Sopenharmony_ci#ifdef module_param_array 30548c2ecf20Sopenharmony_ci } else 30558c2ecf20Sopenharmony_ci adapter->hw.MediaType = (u16)(opt.def); 30568c2ecf20Sopenharmony_ci#endif 30578c2ecf20Sopenharmony_ci} 3058