18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2007 Atheros Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Derived from Intel e1000 driver 68c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "atl1e.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cichar atl1e_driver_name[] = "ATL1E"; 128c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_ATTANSIC_L1E 0x1026 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * atl1e_pci_tbl - PCI Device ID Table 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Wildcard entries (PCI_ANY_ID) should come last 178c2ecf20Sopenharmony_ci * Last entry must be all 0s 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, 208c2ecf20Sopenharmony_ci * Class, Class Mask, private data (not used) } 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistatic const struct pci_device_id atl1e_pci_tbl[] = { 238c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1E)}, 248c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, 0x1066)}, 258c2ecf20Sopenharmony_ci /* required last entry */ 268c2ecf20Sopenharmony_ci { 0 } 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, atl1e_pci_tbl); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Atheros Corporation, <xiong.huang@atheros.com>, Jie Yang <jie.yang@atheros.com>"); 318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver"); 328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const u16 378c2ecf20Sopenharmony_ciatl1e_rx_page_vld_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci {REG_HOST_RXF0_PAGE0_VLD, REG_HOST_RXF0_PAGE1_VLD}, 408c2ecf20Sopenharmony_ci {REG_HOST_RXF1_PAGE0_VLD, REG_HOST_RXF1_PAGE1_VLD}, 418c2ecf20Sopenharmony_ci {REG_HOST_RXF2_PAGE0_VLD, REG_HOST_RXF2_PAGE1_VLD}, 428c2ecf20Sopenharmony_ci {REG_HOST_RXF3_PAGE0_VLD, REG_HOST_RXF3_PAGE1_VLD} 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const u16 atl1e_rx_page_hi_addr_regs[AT_MAX_RECEIVE_QUEUE] = 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci REG_RXF0_BASE_ADDR_HI, 488c2ecf20Sopenharmony_ci REG_RXF1_BASE_ADDR_HI, 498c2ecf20Sopenharmony_ci REG_RXF2_BASE_ADDR_HI, 508c2ecf20Sopenharmony_ci REG_RXF3_BASE_ADDR_HI 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const u16 548c2ecf20Sopenharmony_ciatl1e_rx_page_lo_addr_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci {REG_HOST_RXF0_PAGE0_LO, REG_HOST_RXF0_PAGE1_LO}, 578c2ecf20Sopenharmony_ci {REG_HOST_RXF1_PAGE0_LO, REG_HOST_RXF1_PAGE1_LO}, 588c2ecf20Sopenharmony_ci {REG_HOST_RXF2_PAGE0_LO, REG_HOST_RXF2_PAGE1_LO}, 598c2ecf20Sopenharmony_ci {REG_HOST_RXF3_PAGE0_LO, REG_HOST_RXF3_PAGE1_LO} 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const u16 638c2ecf20Sopenharmony_ciatl1e_rx_page_write_offset_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci {REG_HOST_RXF0_MB0_LO, REG_HOST_RXF0_MB1_LO}, 668c2ecf20Sopenharmony_ci {REG_HOST_RXF1_MB0_LO, REG_HOST_RXF1_MB1_LO}, 678c2ecf20Sopenharmony_ci {REG_HOST_RXF2_MB0_LO, REG_HOST_RXF2_MB1_LO}, 688c2ecf20Sopenharmony_ci {REG_HOST_RXF3_MB0_LO, REG_HOST_RXF3_MB1_LO} 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const u16 atl1e_pay_load_size[] = { 728c2ecf20Sopenharmony_ci 128, 256, 512, 1024, 2048, 4096, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * atl1e_irq_enable - Enable default interrupt generation settings 778c2ecf20Sopenharmony_ci * @adapter: board private structure 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic inline void atl1e_irq_enable(struct atl1e_adapter *adapter) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci if (likely(atomic_dec_and_test(&adapter->irq_sem))) { 828c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_ISR, 0); 838c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); 848c2ecf20Sopenharmony_ci AT_WRITE_FLUSH(&adapter->hw); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/** 898c2ecf20Sopenharmony_ci * atl1e_irq_disable - Mask off interrupt generation on the NIC 908c2ecf20Sopenharmony_ci * @adapter: board private structure 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic inline void atl1e_irq_disable(struct atl1e_adapter *adapter) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci atomic_inc(&adapter->irq_sem); 958c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_IMR, 0); 968c2ecf20Sopenharmony_ci AT_WRITE_FLUSH(&adapter->hw); 978c2ecf20Sopenharmony_ci synchronize_irq(adapter->pdev->irq); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/** 1018c2ecf20Sopenharmony_ci * atl1e_irq_reset - reset interrupt confiure on the NIC 1028c2ecf20Sopenharmony_ci * @adapter: board private structure 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic inline void atl1e_irq_reset(struct atl1e_adapter *adapter) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci atomic_set(&adapter->irq_sem, 0); 1078c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_ISR, 0); 1088c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_IMR, 0); 1098c2ecf20Sopenharmony_ci AT_WRITE_FLUSH(&adapter->hw); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * atl1e_phy_config - Timer Call-back 1148c2ecf20Sopenharmony_ci * @t: timer list containing pointer to netdev cast into an unsigned long 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic void atl1e_phy_config(struct timer_list *t) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = from_timer(adapter, t, 1198c2ecf20Sopenharmony_ci phy_config_timer); 1208c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 1218c2ecf20Sopenharmony_ci unsigned long flags; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->mdio_lock, flags); 1248c2ecf20Sopenharmony_ci atl1e_restart_autoneg(hw); 1258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->mdio_lock, flags); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_civoid atl1e_reinit_locked(struct atl1e_adapter *adapter) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) 1318c2ecf20Sopenharmony_ci msleep(1); 1328c2ecf20Sopenharmony_ci atl1e_down(adapter); 1338c2ecf20Sopenharmony_ci atl1e_up(adapter); 1348c2ecf20Sopenharmony_ci clear_bit(__AT_RESETTING, &adapter->flags); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void atl1e_reset_task(struct work_struct *work) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter; 1408c2ecf20Sopenharmony_ci adapter = container_of(work, struct atl1e_adapter, reset_task); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci atl1e_reinit_locked(adapter); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int atl1e_check_link(struct atl1e_adapter *adapter) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 1488c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 1498c2ecf20Sopenharmony_ci int err = 0; 1508c2ecf20Sopenharmony_ci u16 speed, duplex, phy_data; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* MII_BMSR must read twice */ 1538c2ecf20Sopenharmony_ci atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); 1548c2ecf20Sopenharmony_ci atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); 1558c2ecf20Sopenharmony_ci if ((phy_data & BMSR_LSTATUS) == 0) { 1568c2ecf20Sopenharmony_ci /* link down */ 1578c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { /* old link state: Up */ 1588c2ecf20Sopenharmony_ci u32 value; 1598c2ecf20Sopenharmony_ci /* disable rx */ 1608c2ecf20Sopenharmony_ci value = AT_READ_REG(hw, REG_MAC_CTRL); 1618c2ecf20Sopenharmony_ci value &= ~MAC_CTRL_RX_EN; 1628c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_MAC_CTRL, value); 1638c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 1648c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 1658c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci /* Link Up */ 1698c2ecf20Sopenharmony_ci err = atl1e_get_speed_and_duplex(hw, &speed, &duplex); 1708c2ecf20Sopenharmony_ci if (unlikely(err)) 1718c2ecf20Sopenharmony_ci return err; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* link result is our setting */ 1748c2ecf20Sopenharmony_ci if (adapter->link_speed != speed || 1758c2ecf20Sopenharmony_ci adapter->link_duplex != duplex) { 1768c2ecf20Sopenharmony_ci adapter->link_speed = speed; 1778c2ecf20Sopenharmony_ci adapter->link_duplex = duplex; 1788c2ecf20Sopenharmony_ci atl1e_setup_mac_ctrl(adapter); 1798c2ecf20Sopenharmony_ci netdev_info(netdev, 1808c2ecf20Sopenharmony_ci "NIC Link is Up <%d Mbps %s Duplex>\n", 1818c2ecf20Sopenharmony_ci adapter->link_speed, 1828c2ecf20Sopenharmony_ci adapter->link_duplex == FULL_DUPLEX ? 1838c2ecf20Sopenharmony_ci "Full" : "Half"); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!netif_carrier_ok(netdev)) { 1878c2ecf20Sopenharmony_ci /* Link down -> Up */ 1888c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 1898c2ecf20Sopenharmony_ci netif_wake_queue(netdev); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * atl1e_link_chg_task - deal with link change event Out of interrupt context 1978c2ecf20Sopenharmony_ci * @work: work struct with driver info 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void atl1e_link_chg_task(struct work_struct *work) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter; 2028c2ecf20Sopenharmony_ci unsigned long flags; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci adapter = container_of(work, struct atl1e_adapter, link_chg_task); 2058c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->mdio_lock, flags); 2068c2ecf20Sopenharmony_ci atl1e_check_link(adapter); 2078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->mdio_lock, flags); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void atl1e_link_chg_event(struct atl1e_adapter *adapter) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 2138c2ecf20Sopenharmony_ci u16 phy_data = 0; 2148c2ecf20Sopenharmony_ci u16 link_up = 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci spin_lock(&adapter->mdio_lock); 2178c2ecf20Sopenharmony_ci atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); 2188c2ecf20Sopenharmony_ci atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); 2198c2ecf20Sopenharmony_ci spin_unlock(&adapter->mdio_lock); 2208c2ecf20Sopenharmony_ci link_up = phy_data & BMSR_LSTATUS; 2218c2ecf20Sopenharmony_ci /* notify upper layer link down ASAP */ 2228c2ecf20Sopenharmony_ci if (!link_up) { 2238c2ecf20Sopenharmony_ci if (netif_carrier_ok(netdev)) { 2248c2ecf20Sopenharmony_ci /* old link state: Up */ 2258c2ecf20Sopenharmony_ci netdev_info(netdev, "NIC Link is Down\n"); 2268c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 2278c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci schedule_work(&adapter->link_chg_task); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void atl1e_del_timer(struct atl1e_adapter *adapter) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci del_timer_sync(&adapter->phy_config_timer); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void atl1e_cancel_work(struct atl1e_adapter *adapter) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci cancel_work_sync(&adapter->reset_task); 2418c2ecf20Sopenharmony_ci cancel_work_sync(&adapter->link_chg_task); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/** 2458c2ecf20Sopenharmony_ci * atl1e_tx_timeout - Respond to a Tx Hang 2468c2ecf20Sopenharmony_ci * @netdev: network interface device structure 2478c2ecf20Sopenharmony_ci * @txqueue: the index of the hanging queue 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistatic void atl1e_tx_timeout(struct net_device *netdev, unsigned int txqueue) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Do the reset outside of interrupt context */ 2548c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/** 2588c2ecf20Sopenharmony_ci * atl1e_set_multi - Multicast and Promiscuous mode set 2598c2ecf20Sopenharmony_ci * @netdev: network interface device structure 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * The set_multi entry point is called whenever the multicast address 2628c2ecf20Sopenharmony_ci * list or the network interface flags are updated. This routine is 2638c2ecf20Sopenharmony_ci * responsible for configuring the hardware for proper multicast, 2648c2ecf20Sopenharmony_ci * promiscuous mode, and all-multi behavior. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic void atl1e_set_multi(struct net_device *netdev) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 2698c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 2708c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2718c2ecf20Sopenharmony_ci u32 mac_ctrl_data = 0; 2728c2ecf20Sopenharmony_ci u32 hash_value; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Check for Promiscuous and All Multicast modes */ 2758c2ecf20Sopenharmony_ci mac_ctrl_data = AT_READ_REG(hw, REG_MAC_CTRL); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 2788c2ecf20Sopenharmony_ci mac_ctrl_data |= MAC_CTRL_PROMIS_EN; 2798c2ecf20Sopenharmony_ci } else if (netdev->flags & IFF_ALLMULTI) { 2808c2ecf20Sopenharmony_ci mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; 2818c2ecf20Sopenharmony_ci mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN; 2828c2ecf20Sopenharmony_ci } else { 2838c2ecf20Sopenharmony_ci mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* clear the old settings from the multicast hash table */ 2898c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); 2908c2ecf20Sopenharmony_ci AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* comoute mc addresses' hash value ,and put it into hash table */ 2938c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 2948c2ecf20Sopenharmony_ci hash_value = atl1e_hash_mc_addr(hw, ha->addr); 2958c2ecf20Sopenharmony_ci atl1e_hash_set(hw, hash_value); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void __atl1e_rx_mode(netdev_features_t features, u32 *mac_ctrl_data) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (features & NETIF_F_RXALL) { 3038c2ecf20Sopenharmony_ci /* enable RX of ALL frames */ 3048c2ecf20Sopenharmony_ci *mac_ctrl_data |= MAC_CTRL_DBG; 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci /* disable RX of ALL frames */ 3078c2ecf20Sopenharmony_ci *mac_ctrl_data &= ~MAC_CTRL_DBG; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic void atl1e_rx_mode(struct net_device *netdev, 3128c2ecf20Sopenharmony_ci netdev_features_t features) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 3158c2ecf20Sopenharmony_ci u32 mac_ctrl_data = 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, "%s\n", __func__); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci atl1e_irq_disable(adapter); 3208c2ecf20Sopenharmony_ci mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL); 3218c2ecf20Sopenharmony_ci __atl1e_rx_mode(features, &mac_ctrl_data); 3228c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); 3238c2ecf20Sopenharmony_ci atl1e_irq_enable(adapter); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic void __atl1e_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) { 3308c2ecf20Sopenharmony_ci /* enable VLAN tag insert/strip */ 3318c2ecf20Sopenharmony_ci *mac_ctrl_data |= MAC_CTRL_RMV_VLAN; 3328c2ecf20Sopenharmony_ci } else { 3338c2ecf20Sopenharmony_ci /* disable VLAN tag insert/strip */ 3348c2ecf20Sopenharmony_ci *mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void atl1e_vlan_mode(struct net_device *netdev, 3398c2ecf20Sopenharmony_ci netdev_features_t features) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 3428c2ecf20Sopenharmony_ci u32 mac_ctrl_data = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, "%s\n", __func__); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci atl1e_irq_disable(adapter); 3478c2ecf20Sopenharmony_ci mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL); 3488c2ecf20Sopenharmony_ci __atl1e_vlan_mode(features, &mac_ctrl_data); 3498c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); 3508c2ecf20Sopenharmony_ci atl1e_irq_enable(adapter); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void atl1e_restore_vlan(struct atl1e_adapter *adapter) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, "%s\n", __func__); 3568c2ecf20Sopenharmony_ci atl1e_vlan_mode(adapter->netdev, adapter->netdev->features); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * atl1e_set_mac - Change the Ethernet Address of the NIC 3618c2ecf20Sopenharmony_ci * @netdev: network interface device structure 3628c2ecf20Sopenharmony_ci * @p: pointer to an address structure 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic int atl1e_set_mac_addr(struct net_device *netdev, void *p) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 3698c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 3728c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (netif_running(netdev)) 3758c2ecf20Sopenharmony_ci return -EBUSY; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); 3788c2ecf20Sopenharmony_ci memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci atl1e_hw_set_mac_addr(&adapter->hw); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic netdev_features_t atl1e_fix_features(struct net_device *netdev, 3868c2ecf20Sopenharmony_ci netdev_features_t features) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * Since there is no support for separate rx/tx vlan accel 3908c2ecf20Sopenharmony_ci * enable/disable make sure tx flag is always in same state as rx. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 3938c2ecf20Sopenharmony_ci features |= NETIF_F_HW_VLAN_CTAG_TX; 3948c2ecf20Sopenharmony_ci else 3958c2ecf20Sopenharmony_ci features &= ~NETIF_F_HW_VLAN_CTAG_TX; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return features; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int atl1e_set_features(struct net_device *netdev, 4018c2ecf20Sopenharmony_ci netdev_features_t features) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci netdev_features_t changed = netdev->features ^ features; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_RX) 4068c2ecf20Sopenharmony_ci atl1e_vlan_mode(netdev, features); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (changed & NETIF_F_RXALL) 4098c2ecf20Sopenharmony_ci atl1e_rx_mode(netdev, features); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/** 4168c2ecf20Sopenharmony_ci * atl1e_change_mtu - Change the Maximum Transfer Unit 4178c2ecf20Sopenharmony_ci * @netdev: network interface device structure 4188c2ecf20Sopenharmony_ci * @new_mtu: new value for maximum frame size 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic int atl1e_change_mtu(struct net_device *netdev, int new_mtu) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 4258c2ecf20Sopenharmony_ci int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* set MTU */ 4288c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 4298c2ecf20Sopenharmony_ci while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) 4308c2ecf20Sopenharmony_ci msleep(1); 4318c2ecf20Sopenharmony_ci netdev->mtu = new_mtu; 4328c2ecf20Sopenharmony_ci adapter->hw.max_frame_size = new_mtu; 4338c2ecf20Sopenharmony_ci adapter->hw.rx_jumbo_th = (max_frame + 7) >> 3; 4348c2ecf20Sopenharmony_ci atl1e_down(adapter); 4358c2ecf20Sopenharmony_ci atl1e_up(adapter); 4368c2ecf20Sopenharmony_ci clear_bit(__AT_RESETTING, &adapter->flags); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* 4428c2ecf20Sopenharmony_ci * caller should hold mdio_lock 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_cistatic int atl1e_mdio_read(struct net_device *netdev, int phy_id, int reg_num) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 4478c2ecf20Sopenharmony_ci u16 result; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci atl1e_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result); 4508c2ecf20Sopenharmony_ci return result; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void atl1e_mdio_write(struct net_device *netdev, int phy_id, 4548c2ecf20Sopenharmony_ci int reg_num, int val) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (atl1e_write_phy_reg(&adapter->hw, 4598c2ecf20Sopenharmony_ci reg_num & MDIO_REG_ADDR_MASK, val)) 4608c2ecf20Sopenharmony_ci netdev_err(netdev, "write phy register failed\n"); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int atl1e_mii_ioctl(struct net_device *netdev, 4648c2ecf20Sopenharmony_ci struct ifreq *ifr, int cmd) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 4678c2ecf20Sopenharmony_ci struct mii_ioctl_data *data = if_mii(ifr); 4688c2ecf20Sopenharmony_ci unsigned long flags; 4698c2ecf20Sopenharmony_ci int retval = 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 4728c2ecf20Sopenharmony_ci return -EINVAL; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->mdio_lock, flags); 4758c2ecf20Sopenharmony_ci switch (cmd) { 4768c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 4778c2ecf20Sopenharmony_ci data->phy_id = 0; 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci case SIOCGMIIREG: 4818c2ecf20Sopenharmony_ci if (atl1e_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, 4828c2ecf20Sopenharmony_ci &data->val_out)) { 4838c2ecf20Sopenharmony_ci retval = -EIO; 4848c2ecf20Sopenharmony_ci goto out; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci break; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci case SIOCSMIIREG: 4898c2ecf20Sopenharmony_ci if (data->reg_num & ~(0x1F)) { 4908c2ecf20Sopenharmony_ci retval = -EFAULT; 4918c2ecf20Sopenharmony_ci goto out; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, "<atl1e_mii_ioctl> write %x %x\n", 4958c2ecf20Sopenharmony_ci data->reg_num, data->val_in); 4968c2ecf20Sopenharmony_ci if (atl1e_write_phy_reg(&adapter->hw, 4978c2ecf20Sopenharmony_ci data->reg_num, data->val_in)) { 4988c2ecf20Sopenharmony_ci retval = -EIO; 4998c2ecf20Sopenharmony_ci goto out; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci default: 5048c2ecf20Sopenharmony_ci retval = -EOPNOTSUPP; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ciout: 5088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->mdio_lock, flags); 5098c2ecf20Sopenharmony_ci return retval; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int atl1e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci switch (cmd) { 5168c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 5178c2ecf20Sopenharmony_ci case SIOCGMIIREG: 5188c2ecf20Sopenharmony_ci case SIOCSMIIREG: 5198c2ecf20Sopenharmony_ci return atl1e_mii_ioctl(netdev, ifr, cmd); 5208c2ecf20Sopenharmony_ci default: 5218c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic void atl1e_setup_pcicmd(struct pci_dev *pdev) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci u16 cmd; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &cmd); 5308c2ecf20Sopenharmony_ci cmd &= ~(PCI_COMMAND_INTX_DISABLE | PCI_COMMAND_IO); 5318c2ecf20Sopenharmony_ci cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); 5328c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * some motherboards BIOS(PXE/EFI) driver may set PME 5368c2ecf20Sopenharmony_ci * while they transfer control to OS (Windows/Linux) 5378c2ecf20Sopenharmony_ci * so we should clear this bit before NIC work normally 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); 5408c2ecf20Sopenharmony_ci msleep(1); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/** 5448c2ecf20Sopenharmony_ci * atl1e_alloc_queues - Allocate memory for all rings 5458c2ecf20Sopenharmony_ci * @adapter: board private structure to initialize 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_cistatic int atl1e_alloc_queues(struct atl1e_adapter *adapter) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/** 5548c2ecf20Sopenharmony_ci * atl1e_sw_init - Initialize general software structures (struct atl1e_adapter) 5558c2ecf20Sopenharmony_ci * @adapter: board private structure to initialize 5568c2ecf20Sopenharmony_ci * 5578c2ecf20Sopenharmony_ci * atl1e_sw_init initializes the Adapter private data structure. 5588c2ecf20Sopenharmony_ci * Fields are initialized based on PCI device information and 5598c2ecf20Sopenharmony_ci * OS network device settings (MTU size). 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_cistatic int atl1e_sw_init(struct atl1e_adapter *adapter) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 5648c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 5658c2ecf20Sopenharmony_ci u32 phy_status_data = 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci adapter->wol = 0; 5688c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; /* hardware init */ 5698c2ecf20Sopenharmony_ci adapter->link_duplex = FULL_DUPLEX; 5708c2ecf20Sopenharmony_ci adapter->num_rx_queues = 1; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* PCI config space info */ 5738c2ecf20Sopenharmony_ci hw->vendor_id = pdev->vendor; 5748c2ecf20Sopenharmony_ci hw->device_id = pdev->device; 5758c2ecf20Sopenharmony_ci hw->subsystem_vendor_id = pdev->subsystem_vendor; 5768c2ecf20Sopenharmony_ci hw->subsystem_id = pdev->subsystem_device; 5778c2ecf20Sopenharmony_ci hw->revision_id = pdev->revision; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); 5828c2ecf20Sopenharmony_ci /* nic type */ 5838c2ecf20Sopenharmony_ci if (hw->revision_id >= 0xF0) { 5848c2ecf20Sopenharmony_ci hw->nic_type = athr_l2e_revB; 5858c2ecf20Sopenharmony_ci } else { 5868c2ecf20Sopenharmony_ci if (phy_status_data & PHY_STATUS_100M) 5878c2ecf20Sopenharmony_ci hw->nic_type = athr_l1e; 5888c2ecf20Sopenharmony_ci else 5898c2ecf20Sopenharmony_ci hw->nic_type = athr_l2e_revA; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (phy_status_data & PHY_STATUS_EMI_CA) 5958c2ecf20Sopenharmony_ci hw->emi_ca = true; 5968c2ecf20Sopenharmony_ci else 5978c2ecf20Sopenharmony_ci hw->emi_ca = false; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci hw->phy_configured = false; 6008c2ecf20Sopenharmony_ci hw->preamble_len = 7; 6018c2ecf20Sopenharmony_ci hw->max_frame_size = adapter->netdev->mtu; 6028c2ecf20Sopenharmony_ci hw->rx_jumbo_th = (hw->max_frame_size + ETH_HLEN + 6038c2ecf20Sopenharmony_ci VLAN_HLEN + ETH_FCS_LEN + 7) >> 3; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci hw->rrs_type = atl1e_rrs_disable; 6068c2ecf20Sopenharmony_ci hw->indirect_tab = 0; 6078c2ecf20Sopenharmony_ci hw->base_cpu = 0; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* need confirm */ 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci hw->ict = 50000; /* 100ms */ 6128c2ecf20Sopenharmony_ci hw->smb_timer = 200000; /* 200ms */ 6138c2ecf20Sopenharmony_ci hw->tpd_burst = 5; 6148c2ecf20Sopenharmony_ci hw->rrd_thresh = 1; 6158c2ecf20Sopenharmony_ci hw->tpd_thresh = adapter->tx_ring.count / 2; 6168c2ecf20Sopenharmony_ci hw->rx_count_down = 4; /* 2us resolution */ 6178c2ecf20Sopenharmony_ci hw->tx_count_down = hw->imt * 4 / 3; 6188c2ecf20Sopenharmony_ci hw->dmar_block = atl1e_dma_req_1024; 6198c2ecf20Sopenharmony_ci hw->dmaw_block = atl1e_dma_req_1024; 6208c2ecf20Sopenharmony_ci hw->dmar_dly_cnt = 15; 6218c2ecf20Sopenharmony_ci hw->dmaw_dly_cnt = 4; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (atl1e_alloc_queues(adapter)) { 6248c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, "Unable to allocate memory for queues\n"); 6258c2ecf20Sopenharmony_ci return -ENOMEM; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci atomic_set(&adapter->irq_sem, 1); 6298c2ecf20Sopenharmony_ci spin_lock_init(&adapter->mdio_lock); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci set_bit(__AT_DOWN, &adapter->flags); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci/** 6378c2ecf20Sopenharmony_ci * atl1e_clean_tx_ring - Free Tx-skb 6388c2ecf20Sopenharmony_ci * @adapter: board private structure 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_cistatic void atl1e_clean_tx_ring(struct atl1e_adapter *adapter) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 6438c2ecf20Sopenharmony_ci struct atl1e_tx_buffer *tx_buffer = NULL; 6448c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 6458c2ecf20Sopenharmony_ci u16 index, ring_count; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (tx_ring->desc == NULL || tx_ring->tx_buffer == NULL) 6488c2ecf20Sopenharmony_ci return; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci ring_count = tx_ring->count; 6518c2ecf20Sopenharmony_ci /* first unmmap dma */ 6528c2ecf20Sopenharmony_ci for (index = 0; index < ring_count; index++) { 6538c2ecf20Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer[index]; 6548c2ecf20Sopenharmony_ci if (tx_buffer->dma) { 6558c2ecf20Sopenharmony_ci if (tx_buffer->flags & ATL1E_TX_PCIMAP_SINGLE) 6568c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, tx_buffer->dma, 6578c2ecf20Sopenharmony_ci tx_buffer->length, 6588c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6598c2ecf20Sopenharmony_ci else if (tx_buffer->flags & ATL1E_TX_PCIMAP_PAGE) 6608c2ecf20Sopenharmony_ci dma_unmap_page(&pdev->dev, tx_buffer->dma, 6618c2ecf20Sopenharmony_ci tx_buffer->length, 6628c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6638c2ecf20Sopenharmony_ci tx_buffer->dma = 0; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci /* second free skb */ 6678c2ecf20Sopenharmony_ci for (index = 0; index < ring_count; index++) { 6688c2ecf20Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer[index]; 6698c2ecf20Sopenharmony_ci if (tx_buffer->skb) { 6708c2ecf20Sopenharmony_ci dev_kfree_skb_any(tx_buffer->skb); 6718c2ecf20Sopenharmony_ci tx_buffer->skb = NULL; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci /* Zero out Tx-buffers */ 6758c2ecf20Sopenharmony_ci memset(tx_ring->desc, 0, sizeof(struct atl1e_tpd_desc) * 6768c2ecf20Sopenharmony_ci ring_count); 6778c2ecf20Sopenharmony_ci memset(tx_ring->tx_buffer, 0, sizeof(struct atl1e_tx_buffer) * 6788c2ecf20Sopenharmony_ci ring_count); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/** 6828c2ecf20Sopenharmony_ci * atl1e_clean_rx_ring - Free rx-reservation skbs 6838c2ecf20Sopenharmony_ci * @adapter: board private structure 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_cistatic void atl1e_clean_rx_ring(struct atl1e_adapter *adapter) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct atl1e_rx_ring *rx_ring = 6888c2ecf20Sopenharmony_ci &adapter->rx_ring; 6898c2ecf20Sopenharmony_ci struct atl1e_rx_page_desc *rx_page_desc = rx_ring->rx_page_desc; 6908c2ecf20Sopenharmony_ci u16 i, j; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (adapter->ring_vir_addr == NULL) 6948c2ecf20Sopenharmony_ci return; 6958c2ecf20Sopenharmony_ci /* Zero out the descriptor ring */ 6968c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 6978c2ecf20Sopenharmony_ci for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { 6988c2ecf20Sopenharmony_ci if (rx_page_desc[i].rx_page[j].addr != NULL) { 6998c2ecf20Sopenharmony_ci memset(rx_page_desc[i].rx_page[j].addr, 0, 7008c2ecf20Sopenharmony_ci rx_ring->real_page_size); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic void atl1e_cal_ring_size(struct atl1e_adapter *adapter, u32 *ring_size) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci *ring_size = ((u32)(adapter->tx_ring.count * 7098c2ecf20Sopenharmony_ci sizeof(struct atl1e_tpd_desc) + 7 7108c2ecf20Sopenharmony_ci /* tx ring, qword align */ 7118c2ecf20Sopenharmony_ci + adapter->rx_ring.real_page_size * AT_PAGE_NUM_PER_QUEUE * 7128c2ecf20Sopenharmony_ci adapter->num_rx_queues + 31 7138c2ecf20Sopenharmony_ci /* rx ring, 32 bytes align */ 7148c2ecf20Sopenharmony_ci + (1 + AT_PAGE_NUM_PER_QUEUE * adapter->num_rx_queues) * 7158c2ecf20Sopenharmony_ci sizeof(u32) + 3)); 7168c2ecf20Sopenharmony_ci /* tx, rx cmd, dword align */ 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void atl1e_init_ring_resources(struct atl1e_adapter *adapter) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct atl1e_rx_ring *rx_ring = NULL; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci rx_ring = &adapter->rx_ring; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci rx_ring->real_page_size = adapter->rx_ring.page_size 7268c2ecf20Sopenharmony_ci + adapter->hw.max_frame_size 7278c2ecf20Sopenharmony_ci + ETH_HLEN + VLAN_HLEN 7288c2ecf20Sopenharmony_ci + ETH_FCS_LEN; 7298c2ecf20Sopenharmony_ci rx_ring->real_page_size = roundup(rx_ring->real_page_size, 32); 7308c2ecf20Sopenharmony_ci atl1e_cal_ring_size(adapter, &adapter->ring_size); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci adapter->ring_vir_addr = NULL; 7338c2ecf20Sopenharmony_ci adapter->rx_ring.desc = NULL; 7348c2ecf20Sopenharmony_ci rwlock_init(&adapter->tx_ring.tx_lock); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci/* 7388c2ecf20Sopenharmony_ci * Read / Write Ptr Initialize: 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_cistatic void atl1e_init_ring_ptrs(struct atl1e_adapter *adapter) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = NULL; 7438c2ecf20Sopenharmony_ci struct atl1e_rx_ring *rx_ring = NULL; 7448c2ecf20Sopenharmony_ci struct atl1e_rx_page_desc *rx_page_desc = NULL; 7458c2ecf20Sopenharmony_ci int i, j; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring; 7488c2ecf20Sopenharmony_ci rx_ring = &adapter->rx_ring; 7498c2ecf20Sopenharmony_ci rx_page_desc = rx_ring->rx_page_desc; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci tx_ring->next_to_use = 0; 7528c2ecf20Sopenharmony_ci atomic_set(&tx_ring->next_to_clean, 0); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 7558c2ecf20Sopenharmony_ci rx_page_desc[i].rx_using = 0; 7568c2ecf20Sopenharmony_ci rx_page_desc[i].rx_nxseq = 0; 7578c2ecf20Sopenharmony_ci for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { 7588c2ecf20Sopenharmony_ci *rx_page_desc[i].rx_page[j].write_offset_addr = 0; 7598c2ecf20Sopenharmony_ci rx_page_desc[i].rx_page[j].read_offset = 0; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/** 7658c2ecf20Sopenharmony_ci * atl1e_free_ring_resources - Free Tx / RX descriptor Resources 7668c2ecf20Sopenharmony_ci * @adapter: board private structure 7678c2ecf20Sopenharmony_ci * 7688c2ecf20Sopenharmony_ci * Free all transmit software resources 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic void atl1e_free_ring_resources(struct atl1e_adapter *adapter) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci atl1e_clean_tx_ring(adapter); 7758c2ecf20Sopenharmony_ci atl1e_clean_rx_ring(adapter); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (adapter->ring_vir_addr) { 7788c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, adapter->ring_size, 7798c2ecf20Sopenharmony_ci adapter->ring_vir_addr, adapter->ring_dma); 7808c2ecf20Sopenharmony_ci adapter->ring_vir_addr = NULL; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (adapter->tx_ring.tx_buffer) { 7848c2ecf20Sopenharmony_ci kfree(adapter->tx_ring.tx_buffer); 7858c2ecf20Sopenharmony_ci adapter->tx_ring.tx_buffer = NULL; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/** 7908c2ecf20Sopenharmony_ci * atl1e_setup_mem_resources - allocate Tx / RX descriptor resources 7918c2ecf20Sopenharmony_ci * @adapter: board private structure 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_cistatic int atl1e_setup_ring_resources(struct atl1e_adapter *adapter) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 7988c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring; 7998c2ecf20Sopenharmony_ci struct atl1e_rx_ring *rx_ring; 8008c2ecf20Sopenharmony_ci struct atl1e_rx_page_desc *rx_page_desc; 8018c2ecf20Sopenharmony_ci int size, i, j; 8028c2ecf20Sopenharmony_ci u32 offset = 0; 8038c2ecf20Sopenharmony_ci int err = 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (adapter->ring_vir_addr != NULL) 8068c2ecf20Sopenharmony_ci return 0; /* alloced already */ 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring; 8098c2ecf20Sopenharmony_ci rx_ring = &adapter->rx_ring; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* real ring DMA buffer */ 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci size = adapter->ring_size; 8148c2ecf20Sopenharmony_ci adapter->ring_vir_addr = dma_alloc_coherent(&pdev->dev, 8158c2ecf20Sopenharmony_ci adapter->ring_size, 8168c2ecf20Sopenharmony_ci &adapter->ring_dma, GFP_KERNEL); 8178c2ecf20Sopenharmony_ci if (adapter->ring_vir_addr == NULL) { 8188c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 8198c2ecf20Sopenharmony_ci "dma_alloc_coherent failed, size = D%d\n", size); 8208c2ecf20Sopenharmony_ci return -ENOMEM; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci rx_page_desc = rx_ring->rx_page_desc; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* Init TPD Ring */ 8268c2ecf20Sopenharmony_ci tx_ring->dma = roundup(adapter->ring_dma, 8); 8278c2ecf20Sopenharmony_ci offset = tx_ring->dma - adapter->ring_dma; 8288c2ecf20Sopenharmony_ci tx_ring->desc = adapter->ring_vir_addr + offset; 8298c2ecf20Sopenharmony_ci size = sizeof(struct atl1e_tx_buffer) * (tx_ring->count); 8308c2ecf20Sopenharmony_ci tx_ring->tx_buffer = kzalloc(size, GFP_KERNEL); 8318c2ecf20Sopenharmony_ci if (tx_ring->tx_buffer == NULL) { 8328c2ecf20Sopenharmony_ci err = -ENOMEM; 8338c2ecf20Sopenharmony_ci goto failed; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* Init RXF-Pages */ 8378c2ecf20Sopenharmony_ci offset += (sizeof(struct atl1e_tpd_desc) * tx_ring->count); 8388c2ecf20Sopenharmony_ci offset = roundup(offset, 32); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 8418c2ecf20Sopenharmony_ci for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { 8428c2ecf20Sopenharmony_ci rx_page_desc[i].rx_page[j].dma = 8438c2ecf20Sopenharmony_ci adapter->ring_dma + offset; 8448c2ecf20Sopenharmony_ci rx_page_desc[i].rx_page[j].addr = 8458c2ecf20Sopenharmony_ci adapter->ring_vir_addr + offset; 8468c2ecf20Sopenharmony_ci offset += rx_ring->real_page_size; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* Init CMB dma address */ 8518c2ecf20Sopenharmony_ci tx_ring->cmb_dma = adapter->ring_dma + offset; 8528c2ecf20Sopenharmony_ci tx_ring->cmb = adapter->ring_vir_addr + offset; 8538c2ecf20Sopenharmony_ci offset += sizeof(u32); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 8568c2ecf20Sopenharmony_ci for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { 8578c2ecf20Sopenharmony_ci rx_page_desc[i].rx_page[j].write_offset_dma = 8588c2ecf20Sopenharmony_ci adapter->ring_dma + offset; 8598c2ecf20Sopenharmony_ci rx_page_desc[i].rx_page[j].write_offset_addr = 8608c2ecf20Sopenharmony_ci adapter->ring_vir_addr + offset; 8618c2ecf20Sopenharmony_ci offset += sizeof(u32); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (unlikely(offset > adapter->ring_size)) { 8668c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, "offset(%d) > ring size(%d) !!\n", 8678c2ecf20Sopenharmony_ci offset, adapter->ring_size); 8688c2ecf20Sopenharmony_ci err = -1; 8698c2ecf20Sopenharmony_ci goto free_buffer; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_cifree_buffer: 8748c2ecf20Sopenharmony_ci kfree(tx_ring->tx_buffer); 8758c2ecf20Sopenharmony_ci tx_ring->tx_buffer = NULL; 8768c2ecf20Sopenharmony_cifailed: 8778c2ecf20Sopenharmony_ci if (adapter->ring_vir_addr != NULL) { 8788c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, adapter->ring_size, 8798c2ecf20Sopenharmony_ci adapter->ring_vir_addr, adapter->ring_dma); 8808c2ecf20Sopenharmony_ci adapter->ring_vir_addr = NULL; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci return err; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic inline void atl1e_configure_des_ring(struct atl1e_adapter *adapter) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 8898c2ecf20Sopenharmony_ci struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; 8908c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 8918c2ecf20Sopenharmony_ci struct atl1e_rx_page_desc *rx_page_desc = NULL; 8928c2ecf20Sopenharmony_ci int i, j; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, 8958c2ecf20Sopenharmony_ci (u32)((adapter->ring_dma & AT_DMA_HI_ADDR_MASK) >> 32)); 8968c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_TPD_BASE_ADDR_LO, 8978c2ecf20Sopenharmony_ci (u32)((tx_ring->dma) & AT_DMA_LO_ADDR_MASK)); 8988c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_TPD_RING_SIZE, (u16)(tx_ring->count)); 8998c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_HOST_TX_CMB_LO, 9008c2ecf20Sopenharmony_ci (u32)((tx_ring->cmb_dma) & AT_DMA_LO_ADDR_MASK)); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci rx_page_desc = rx_ring->rx_page_desc; 9038c2ecf20Sopenharmony_ci /* RXF Page Physical address / Page Length */ 9048c2ecf20Sopenharmony_ci for (i = 0; i < AT_MAX_RECEIVE_QUEUE; i++) { 9058c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, atl1e_rx_page_hi_addr_regs[i], 9068c2ecf20Sopenharmony_ci (u32)((adapter->ring_dma & 9078c2ecf20Sopenharmony_ci AT_DMA_HI_ADDR_MASK) >> 32)); 9088c2ecf20Sopenharmony_ci for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { 9098c2ecf20Sopenharmony_ci u32 page_phy_addr; 9108c2ecf20Sopenharmony_ci u32 offset_phy_addr; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci page_phy_addr = rx_page_desc[i].rx_page[j].dma; 9138c2ecf20Sopenharmony_ci offset_phy_addr = 9148c2ecf20Sopenharmony_ci rx_page_desc[i].rx_page[j].write_offset_dma; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, atl1e_rx_page_lo_addr_regs[i][j], 9178c2ecf20Sopenharmony_ci page_phy_addr & AT_DMA_LO_ADDR_MASK); 9188c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, atl1e_rx_page_write_offset_regs[i][j], 9198c2ecf20Sopenharmony_ci offset_phy_addr & AT_DMA_LO_ADDR_MASK); 9208c2ecf20Sopenharmony_ci AT_WRITE_REGB(hw, atl1e_rx_page_vld_regs[i][j], 1); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci /* Page Length */ 9248c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_HOST_RXFPAGE_SIZE, rx_ring->page_size); 9258c2ecf20Sopenharmony_ci /* Load all of base address above */ 9268c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_LOAD_PTR, 1); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic inline void atl1e_configure_tx(struct atl1e_adapter *adapter) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 9328c2ecf20Sopenharmony_ci u32 dev_ctrl_data = 0; 9338c2ecf20Sopenharmony_ci u32 max_pay_load = 0; 9348c2ecf20Sopenharmony_ci u32 jumbo_thresh = 0; 9358c2ecf20Sopenharmony_ci u32 extra_size = 0; /* Jumbo frame threshold in QWORD unit */ 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* configure TXQ param */ 9388c2ecf20Sopenharmony_ci if (hw->nic_type != athr_l2e_revB) { 9398c2ecf20Sopenharmony_ci extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; 9408c2ecf20Sopenharmony_ci if (hw->max_frame_size <= 1500) { 9418c2ecf20Sopenharmony_ci jumbo_thresh = hw->max_frame_size + extra_size; 9428c2ecf20Sopenharmony_ci } else if (hw->max_frame_size < 6*1024) { 9438c2ecf20Sopenharmony_ci jumbo_thresh = 9448c2ecf20Sopenharmony_ci (hw->max_frame_size + extra_size) * 2 / 3; 9458c2ecf20Sopenharmony_ci } else { 9468c2ecf20Sopenharmony_ci jumbo_thresh = (hw->max_frame_size + extra_size) / 2; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_TX_EARLY_TH, (jumbo_thresh + 7) >> 3); 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci dev_ctrl_data = AT_READ_REG(hw, REG_DEVICE_CTRL); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT)) & 9548c2ecf20Sopenharmony_ci DEVICE_CTRL_MAX_PAYLOAD_MASK; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci hw->dmaw_block = min_t(u32, max_pay_load, hw->dmaw_block); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT)) & 9598c2ecf20Sopenharmony_ci DEVICE_CTRL_MAX_RREQ_SZ_MASK; 9608c2ecf20Sopenharmony_ci hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (hw->nic_type != athr_l2e_revB) 9638c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_TXQ_CTRL + 2, 9648c2ecf20Sopenharmony_ci atl1e_pay_load_size[hw->dmar_block]); 9658c2ecf20Sopenharmony_ci /* enable TXQ */ 9668c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_TXQ_CTRL, 9678c2ecf20Sopenharmony_ci (((u16)hw->tpd_burst & TXQ_CTRL_NUM_TPD_BURST_MASK) 9688c2ecf20Sopenharmony_ci << TXQ_CTRL_NUM_TPD_BURST_SHIFT) 9698c2ecf20Sopenharmony_ci | TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN); 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic inline void atl1e_configure_rx(struct atl1e_adapter *adapter) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 9758c2ecf20Sopenharmony_ci u32 rxf_len = 0; 9768c2ecf20Sopenharmony_ci u32 rxf_low = 0; 9778c2ecf20Sopenharmony_ci u32 rxf_high = 0; 9788c2ecf20Sopenharmony_ci u32 rxf_thresh_data = 0; 9798c2ecf20Sopenharmony_ci u32 rxq_ctrl_data = 0; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (hw->nic_type != athr_l2e_revB) { 9828c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_RXQ_JMBOSZ_RRDTIM, 9838c2ecf20Sopenharmony_ci (u16)((hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) << 9848c2ecf20Sopenharmony_ci RXQ_JMBOSZ_TH_SHIFT | 9858c2ecf20Sopenharmony_ci (1 & RXQ_JMBO_LKAH_MASK) << 9868c2ecf20Sopenharmony_ci RXQ_JMBO_LKAH_SHIFT)); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci rxf_len = AT_READ_REG(hw, REG_SRAM_RXF_LEN); 9898c2ecf20Sopenharmony_ci rxf_high = rxf_len * 4 / 5; 9908c2ecf20Sopenharmony_ci rxf_low = rxf_len / 5; 9918c2ecf20Sopenharmony_ci rxf_thresh_data = ((rxf_high & RXQ_RXF_PAUSE_TH_HI_MASK) 9928c2ecf20Sopenharmony_ci << RXQ_RXF_PAUSE_TH_HI_SHIFT) | 9938c2ecf20Sopenharmony_ci ((rxf_low & RXQ_RXF_PAUSE_TH_LO_MASK) 9948c2ecf20Sopenharmony_ci << RXQ_RXF_PAUSE_TH_LO_SHIFT); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_RXQ_RXF_PAUSE_THRESH, rxf_thresh_data); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* RRS */ 10008c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab); 10018c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (hw->rrs_type & atl1e_rrs_ipv4) 10048c2ecf20Sopenharmony_ci rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (hw->rrs_type & atl1e_rrs_ipv4_tcp) 10078c2ecf20Sopenharmony_ci rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4_TCP; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (hw->rrs_type & atl1e_rrs_ipv6) 10108c2ecf20Sopenharmony_ci rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (hw->rrs_type & atl1e_rrs_ipv6_tcp) 10138c2ecf20Sopenharmony_ci rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6_TCP; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (hw->rrs_type != atl1e_rrs_disable) 10168c2ecf20Sopenharmony_ci rxq_ctrl_data |= 10178c2ecf20Sopenharmony_ci (RXQ_CTRL_HASH_ENABLE | RXQ_CTRL_RSS_MODE_MQUESINT); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci rxq_ctrl_data |= RXQ_CTRL_IPV6_XSUM_VERIFY_EN | RXQ_CTRL_PBA_ALIGN_32 | 10208c2ecf20Sopenharmony_ci RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic inline void atl1e_configure_dma(struct atl1e_adapter *adapter) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 10288c2ecf20Sopenharmony_ci u32 dma_ctrl_data = 0; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci dma_ctrl_data = DMA_CTRL_RXCMB_EN; 10318c2ecf20Sopenharmony_ci dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) 10328c2ecf20Sopenharmony_ci << DMA_CTRL_DMAR_BURST_LEN_SHIFT; 10338c2ecf20Sopenharmony_ci dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) 10348c2ecf20Sopenharmony_ci << DMA_CTRL_DMAW_BURST_LEN_SHIFT; 10358c2ecf20Sopenharmony_ci dma_ctrl_data |= DMA_CTRL_DMAR_REQ_PRI | DMA_CTRL_DMAR_OUT_ORDER; 10368c2ecf20Sopenharmony_ci dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK) 10378c2ecf20Sopenharmony_ci << DMA_CTRL_DMAR_DLY_CNT_SHIFT; 10388c2ecf20Sopenharmony_ci dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK) 10398c2ecf20Sopenharmony_ci << DMA_CTRL_DMAW_DLY_CNT_SHIFT; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci u32 value; 10478c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 10488c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* Config MAC CTRL Register */ 10518c2ecf20Sopenharmony_ci value = MAC_CTRL_TX_EN | 10528c2ecf20Sopenharmony_ci MAC_CTRL_RX_EN ; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (FULL_DUPLEX == adapter->link_duplex) 10558c2ecf20Sopenharmony_ci value |= MAC_CTRL_DUPLX; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci value |= ((u32)((SPEED_1000 == adapter->link_speed) ? 10588c2ecf20Sopenharmony_ci MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << 10598c2ecf20Sopenharmony_ci MAC_CTRL_SPEED_SHIFT); 10608c2ecf20Sopenharmony_ci value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); 10638c2ecf20Sopenharmony_ci value |= (((u32)adapter->hw.preamble_len & 10648c2ecf20Sopenharmony_ci MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci __atl1e_vlan_mode(netdev->features, &value); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci value |= MAC_CTRL_BC_EN; 10698c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 10708c2ecf20Sopenharmony_ci value |= MAC_CTRL_PROMIS_EN; 10718c2ecf20Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) 10728c2ecf20Sopenharmony_ci value |= MAC_CTRL_MC_ALL_EN; 10738c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_RXALL) 10748c2ecf20Sopenharmony_ci value |= MAC_CTRL_DBG; 10758c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_MAC_CTRL, value); 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci/** 10798c2ecf20Sopenharmony_ci * atl1e_configure - Configure Transmit&Receive Unit after Reset 10808c2ecf20Sopenharmony_ci * @adapter: board private structure 10818c2ecf20Sopenharmony_ci * 10828c2ecf20Sopenharmony_ci * Configure the Tx /Rx unit of the MAC after a reset. 10838c2ecf20Sopenharmony_ci */ 10848c2ecf20Sopenharmony_cistatic int atl1e_configure(struct atl1e_adapter *adapter) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci u32 intr_status_data = 0; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* clear interrupt status */ 10918c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_ISR, ~0); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* 1. set MAC Address */ 10948c2ecf20Sopenharmony_ci atl1e_hw_set_mac_addr(hw); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* 2. Init the Multicast HASH table done by set_muti */ 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* 3. Clear any WOL status */ 10998c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_WOL_CTRL, 0); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* 4. Descripter Ring BaseMem/Length/Read ptr/Write ptr 11028c2ecf20Sopenharmony_ci * TPD Ring/SMB/RXF0 Page CMBs, they use the same 11038c2ecf20Sopenharmony_ci * High 32bits memory */ 11048c2ecf20Sopenharmony_ci atl1e_configure_des_ring(adapter); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* 5. set Interrupt Moderator Timer */ 11078c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, hw->imt); 11088c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER2_INIT, hw->imt); 11098c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_LED_MODE | 11108c2ecf20Sopenharmony_ci MASTER_CTRL_ITIMER_EN | MASTER_CTRL_ITIMER2_EN); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* 6. rx/tx threshold to trig interrupt */ 11138c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_TRIG_RRD_THRESH, hw->rrd_thresh); 11148c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_TRIG_TPD_THRESH, hw->tpd_thresh); 11158c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_TRIG_RXTIMER, hw->rx_count_down); 11168c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_TRIG_TXTIMER, hw->tx_count_down); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* 7. set Interrupt Clear Timer */ 11198c2ecf20Sopenharmony_ci AT_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, hw->ict); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* 8. set MTU */ 11228c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + 11238c2ecf20Sopenharmony_ci VLAN_HLEN + ETH_FCS_LEN); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* 9. config TXQ early tx threshold */ 11268c2ecf20Sopenharmony_ci atl1e_configure_tx(adapter); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* 10. config RXQ */ 11298c2ecf20Sopenharmony_ci atl1e_configure_rx(adapter); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* 11. config DMA Engine */ 11328c2ecf20Sopenharmony_ci atl1e_configure_dma(adapter); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* 12. smb timer to trig interrupt */ 11358c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, hw->smb_timer); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci intr_status_data = AT_READ_REG(hw, REG_ISR); 11388c2ecf20Sopenharmony_ci if (unlikely((intr_status_data & ISR_PHY_LINKDOWN) != 0)) { 11398c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 11408c2ecf20Sopenharmony_ci "atl1e_configure failed, PCIE phy link down\n"); 11418c2ecf20Sopenharmony_ci return -1; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_ISR, 0x7fffffff); 11458c2ecf20Sopenharmony_ci return 0; 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci/** 11498c2ecf20Sopenharmony_ci * atl1e_get_stats - Get System Network Statistics 11508c2ecf20Sopenharmony_ci * @netdev: network interface device structure 11518c2ecf20Sopenharmony_ci * 11528c2ecf20Sopenharmony_ci * Returns the address of the device statistics structure. 11538c2ecf20Sopenharmony_ci * The statistics are actually updated from the timer callback. 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_cistatic struct net_device_stats *atl1e_get_stats(struct net_device *netdev) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 11588c2ecf20Sopenharmony_ci struct atl1e_hw_stats *hw_stats = &adapter->hw_stats; 11598c2ecf20Sopenharmony_ci struct net_device_stats *net_stats = &netdev->stats; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci net_stats->rx_bytes = hw_stats->rx_byte_cnt; 11628c2ecf20Sopenharmony_ci net_stats->tx_bytes = hw_stats->tx_byte_cnt; 11638c2ecf20Sopenharmony_ci net_stats->multicast = hw_stats->rx_mcast; 11648c2ecf20Sopenharmony_ci net_stats->collisions = hw_stats->tx_1_col + 11658c2ecf20Sopenharmony_ci hw_stats->tx_2_col + 11668c2ecf20Sopenharmony_ci hw_stats->tx_late_col + 11678c2ecf20Sopenharmony_ci hw_stats->tx_abort_col; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci net_stats->rx_errors = hw_stats->rx_frag + 11708c2ecf20Sopenharmony_ci hw_stats->rx_fcs_err + 11718c2ecf20Sopenharmony_ci hw_stats->rx_len_err + 11728c2ecf20Sopenharmony_ci hw_stats->rx_sz_ov + 11738c2ecf20Sopenharmony_ci hw_stats->rx_rrd_ov + 11748c2ecf20Sopenharmony_ci hw_stats->rx_align_err + 11758c2ecf20Sopenharmony_ci hw_stats->rx_rxf_ov; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov; 11788c2ecf20Sopenharmony_ci net_stats->rx_length_errors = hw_stats->rx_len_err; 11798c2ecf20Sopenharmony_ci net_stats->rx_crc_errors = hw_stats->rx_fcs_err; 11808c2ecf20Sopenharmony_ci net_stats->rx_frame_errors = hw_stats->rx_align_err; 11818c2ecf20Sopenharmony_ci net_stats->rx_dropped = hw_stats->rx_rrd_ov; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci net_stats->tx_errors = hw_stats->tx_late_col + 11848c2ecf20Sopenharmony_ci hw_stats->tx_abort_col + 11858c2ecf20Sopenharmony_ci hw_stats->tx_underrun + 11868c2ecf20Sopenharmony_ci hw_stats->tx_trunc; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci net_stats->tx_fifo_errors = hw_stats->tx_underrun; 11898c2ecf20Sopenharmony_ci net_stats->tx_aborted_errors = hw_stats->tx_abort_col; 11908c2ecf20Sopenharmony_ci net_stats->tx_window_errors = hw_stats->tx_late_col; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors; 11938c2ecf20Sopenharmony_ci net_stats->tx_packets = hw_stats->tx_ok + net_stats->tx_errors; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return net_stats; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void atl1e_update_hw_stats(struct atl1e_adapter *adapter) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci u16 hw_reg_addr = 0; 12018c2ecf20Sopenharmony_ci unsigned long *stats_item = NULL; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* update rx status */ 12048c2ecf20Sopenharmony_ci hw_reg_addr = REG_MAC_RX_STATUS_BIN; 12058c2ecf20Sopenharmony_ci stats_item = &adapter->hw_stats.rx_ok; 12068c2ecf20Sopenharmony_ci while (hw_reg_addr <= REG_MAC_RX_STATUS_END) { 12078c2ecf20Sopenharmony_ci *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr); 12088c2ecf20Sopenharmony_ci stats_item++; 12098c2ecf20Sopenharmony_ci hw_reg_addr += 4; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci /* update tx status */ 12128c2ecf20Sopenharmony_ci hw_reg_addr = REG_MAC_TX_STATUS_BIN; 12138c2ecf20Sopenharmony_ci stats_item = &adapter->hw_stats.tx_ok; 12148c2ecf20Sopenharmony_ci while (hw_reg_addr <= REG_MAC_TX_STATUS_END) { 12158c2ecf20Sopenharmony_ci *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr); 12168c2ecf20Sopenharmony_ci stats_item++; 12178c2ecf20Sopenharmony_ci hw_reg_addr += 4; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic inline void atl1e_clear_phy_int(struct atl1e_adapter *adapter) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci u16 phy_data; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci spin_lock(&adapter->mdio_lock); 12268c2ecf20Sopenharmony_ci atl1e_read_phy_reg(&adapter->hw, MII_INT_STATUS, &phy_data); 12278c2ecf20Sopenharmony_ci spin_unlock(&adapter->mdio_lock); 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic bool atl1e_clean_tx_irq(struct atl1e_adapter *adapter) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 12338c2ecf20Sopenharmony_ci struct atl1e_tx_buffer *tx_buffer = NULL; 12348c2ecf20Sopenharmony_ci u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX); 12358c2ecf20Sopenharmony_ci u16 next_to_clean = atomic_read(&tx_ring->next_to_clean); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci while (next_to_clean != hw_next_to_clean) { 12388c2ecf20Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer[next_to_clean]; 12398c2ecf20Sopenharmony_ci if (tx_buffer->dma) { 12408c2ecf20Sopenharmony_ci if (tx_buffer->flags & ATL1E_TX_PCIMAP_SINGLE) 12418c2ecf20Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, 12428c2ecf20Sopenharmony_ci tx_buffer->dma, 12438c2ecf20Sopenharmony_ci tx_buffer->length, 12448c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12458c2ecf20Sopenharmony_ci else if (tx_buffer->flags & ATL1E_TX_PCIMAP_PAGE) 12468c2ecf20Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, 12478c2ecf20Sopenharmony_ci tx_buffer->dma, 12488c2ecf20Sopenharmony_ci tx_buffer->length, 12498c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12508c2ecf20Sopenharmony_ci tx_buffer->dma = 0; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (tx_buffer->skb) { 12548c2ecf20Sopenharmony_ci dev_consume_skb_irq(tx_buffer->skb); 12558c2ecf20Sopenharmony_ci tx_buffer->skb = NULL; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (++next_to_clean == tx_ring->count) 12598c2ecf20Sopenharmony_ci next_to_clean = 0; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci atomic_set(&tx_ring->next_to_clean, next_to_clean); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (netif_queue_stopped(adapter->netdev) && 12658c2ecf20Sopenharmony_ci netif_carrier_ok(adapter->netdev)) { 12668c2ecf20Sopenharmony_ci netif_wake_queue(adapter->netdev); 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci return true; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci/** 12738c2ecf20Sopenharmony_ci * atl1e_intr - Interrupt Handler 12748c2ecf20Sopenharmony_ci * @irq: interrupt number 12758c2ecf20Sopenharmony_ci * @data: pointer to a network interface device structure 12768c2ecf20Sopenharmony_ci */ 12778c2ecf20Sopenharmony_cistatic irqreturn_t atl1e_intr(int irq, void *data) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci struct net_device *netdev = data; 12808c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 12818c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 12828c2ecf20Sopenharmony_ci int max_ints = AT_MAX_INT_WORK; 12838c2ecf20Sopenharmony_ci int handled = IRQ_NONE; 12848c2ecf20Sopenharmony_ci u32 status; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci do { 12878c2ecf20Sopenharmony_ci status = AT_READ_REG(hw, REG_ISR); 12888c2ecf20Sopenharmony_ci if ((status & IMR_NORMAL_MASK) == 0 || 12898c2ecf20Sopenharmony_ci (status & ISR_DIS_INT) != 0) { 12908c2ecf20Sopenharmony_ci if (max_ints != AT_MAX_INT_WORK) 12918c2ecf20Sopenharmony_ci handled = IRQ_HANDLED; 12928c2ecf20Sopenharmony_ci break; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci /* link event */ 12958c2ecf20Sopenharmony_ci if (status & ISR_GPHY) 12968c2ecf20Sopenharmony_ci atl1e_clear_phy_int(adapter); 12978c2ecf20Sopenharmony_ci /* Ack ISR */ 12988c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci handled = IRQ_HANDLED; 13018c2ecf20Sopenharmony_ci /* check if PCIE PHY Link down */ 13028c2ecf20Sopenharmony_ci if (status & ISR_PHY_LINKDOWN) { 13038c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 13048c2ecf20Sopenharmony_ci "pcie phy linkdown %x\n", status); 13058c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) { 13068c2ecf20Sopenharmony_ci /* reset MAC */ 13078c2ecf20Sopenharmony_ci atl1e_irq_reset(adapter); 13088c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 13098c2ecf20Sopenharmony_ci break; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci /* check if DMA read/write error */ 13148c2ecf20Sopenharmony_ci if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { 13158c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 13168c2ecf20Sopenharmony_ci "PCIE DMA RW error (status = 0x%x)\n", 13178c2ecf20Sopenharmony_ci status); 13188c2ecf20Sopenharmony_ci atl1e_irq_reset(adapter); 13198c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 13208c2ecf20Sopenharmony_ci break; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (status & ISR_SMB) 13248c2ecf20Sopenharmony_ci atl1e_update_hw_stats(adapter); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci /* link event */ 13278c2ecf20Sopenharmony_ci if (status & (ISR_GPHY | ISR_MANUAL)) { 13288c2ecf20Sopenharmony_ci netdev->stats.tx_carrier_errors++; 13298c2ecf20Sopenharmony_ci atl1e_link_chg_event(adapter); 13308c2ecf20Sopenharmony_ci break; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* transmit event */ 13348c2ecf20Sopenharmony_ci if (status & ISR_TX_EVENT) 13358c2ecf20Sopenharmony_ci atl1e_clean_tx_irq(adapter); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (status & ISR_RX_EVENT) { 13388c2ecf20Sopenharmony_ci /* 13398c2ecf20Sopenharmony_ci * disable rx interrupts, without 13408c2ecf20Sopenharmony_ci * the synchronize_irq bit 13418c2ecf20Sopenharmony_ci */ 13428c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_IMR, 13438c2ecf20Sopenharmony_ci IMR_NORMAL_MASK & ~ISR_RX_EVENT); 13448c2ecf20Sopenharmony_ci AT_WRITE_FLUSH(hw); 13458c2ecf20Sopenharmony_ci if (likely(napi_schedule_prep( 13468c2ecf20Sopenharmony_ci &adapter->napi))) 13478c2ecf20Sopenharmony_ci __napi_schedule(&adapter->napi); 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci } while (--max_ints > 0); 13508c2ecf20Sopenharmony_ci /* re-enable Interrupt*/ 13518c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_ISR, 0); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci return handled; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic inline void atl1e_rx_checksum(struct atl1e_adapter *adapter, 13578c2ecf20Sopenharmony_ci struct sk_buff *skb, struct atl1e_recv_ret_status *prrs) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci u8 *packet = (u8 *)(prrs + 1); 13608c2ecf20Sopenharmony_ci struct iphdr *iph; 13618c2ecf20Sopenharmony_ci u16 head_len = ETH_HLEN; 13628c2ecf20Sopenharmony_ci u16 pkt_flags; 13638c2ecf20Sopenharmony_ci u16 err_flags; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 13668c2ecf20Sopenharmony_ci pkt_flags = prrs->pkt_flag; 13678c2ecf20Sopenharmony_ci err_flags = prrs->err_flag; 13688c2ecf20Sopenharmony_ci if (((pkt_flags & RRS_IS_IPV4) || (pkt_flags & RRS_IS_IPV6)) && 13698c2ecf20Sopenharmony_ci ((pkt_flags & RRS_IS_TCP) || (pkt_flags & RRS_IS_UDP))) { 13708c2ecf20Sopenharmony_ci if (pkt_flags & RRS_IS_IPV4) { 13718c2ecf20Sopenharmony_ci if (pkt_flags & RRS_IS_802_3) 13728c2ecf20Sopenharmony_ci head_len += 8; 13738c2ecf20Sopenharmony_ci iph = (struct iphdr *) (packet + head_len); 13748c2ecf20Sopenharmony_ci if (iph->frag_off != 0 && !(pkt_flags & RRS_IS_IP_DF)) 13758c2ecf20Sopenharmony_ci goto hw_xsum; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci if (!(err_flags & (RRS_ERR_IP_CSUM | RRS_ERR_L4_CSUM))) { 13788c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 13798c2ecf20Sopenharmony_ci return; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cihw_xsum : 13848c2ecf20Sopenharmony_ci return; 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic struct atl1e_rx_page *atl1e_get_rx_page(struct atl1e_adapter *adapter, 13888c2ecf20Sopenharmony_ci u8 que) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci struct atl1e_rx_page_desc *rx_page_desc = 13918c2ecf20Sopenharmony_ci (struct atl1e_rx_page_desc *) adapter->rx_ring.rx_page_desc; 13928c2ecf20Sopenharmony_ci u8 rx_using = rx_page_desc[que].rx_using; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci return &(rx_page_desc[que].rx_page[rx_using]); 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_cistatic void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, 13988c2ecf20Sopenharmony_ci int *work_done, int work_to_do) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 14018c2ecf20Sopenharmony_ci struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; 14028c2ecf20Sopenharmony_ci struct atl1e_rx_page_desc *rx_page_desc = 14038c2ecf20Sopenharmony_ci (struct atl1e_rx_page_desc *) rx_ring->rx_page_desc; 14048c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 14058c2ecf20Sopenharmony_ci struct atl1e_rx_page *rx_page = atl1e_get_rx_page(adapter, que); 14068c2ecf20Sopenharmony_ci u32 packet_size, write_offset; 14078c2ecf20Sopenharmony_ci struct atl1e_recv_ret_status *prrs; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci write_offset = *(rx_page->write_offset_addr); 14108c2ecf20Sopenharmony_ci if (likely(rx_page->read_offset < write_offset)) { 14118c2ecf20Sopenharmony_ci do { 14128c2ecf20Sopenharmony_ci if (*work_done >= work_to_do) 14138c2ecf20Sopenharmony_ci break; 14148c2ecf20Sopenharmony_ci (*work_done)++; 14158c2ecf20Sopenharmony_ci /* get new packet's rrs */ 14168c2ecf20Sopenharmony_ci prrs = (struct atl1e_recv_ret_status *) (rx_page->addr + 14178c2ecf20Sopenharmony_ci rx_page->read_offset); 14188c2ecf20Sopenharmony_ci /* check sequence number */ 14198c2ecf20Sopenharmony_ci if (prrs->seq_num != rx_page_desc[que].rx_nxseq) { 14208c2ecf20Sopenharmony_ci netdev_err(netdev, 14218c2ecf20Sopenharmony_ci "rx sequence number error (rx=%d) (expect=%d)\n", 14228c2ecf20Sopenharmony_ci prrs->seq_num, 14238c2ecf20Sopenharmony_ci rx_page_desc[que].rx_nxseq); 14248c2ecf20Sopenharmony_ci rx_page_desc[que].rx_nxseq++; 14258c2ecf20Sopenharmony_ci /* just for debug use */ 14268c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_DEBUG_DATA0, 14278c2ecf20Sopenharmony_ci (((u32)prrs->seq_num) << 16) | 14288c2ecf20Sopenharmony_ci rx_page_desc[que].rx_nxseq); 14298c2ecf20Sopenharmony_ci goto fatal_err; 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci rx_page_desc[que].rx_nxseq++; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci /* error packet */ 14348c2ecf20Sopenharmony_ci if ((prrs->pkt_flag & RRS_IS_ERR_FRAME) && 14358c2ecf20Sopenharmony_ci !(netdev->features & NETIF_F_RXALL)) { 14368c2ecf20Sopenharmony_ci if (prrs->err_flag & (RRS_ERR_BAD_CRC | 14378c2ecf20Sopenharmony_ci RRS_ERR_DRIBBLE | RRS_ERR_CODE | 14388c2ecf20Sopenharmony_ci RRS_ERR_TRUNC)) { 14398c2ecf20Sopenharmony_ci /* hardware error, discard this packet*/ 14408c2ecf20Sopenharmony_ci netdev_err(netdev, 14418c2ecf20Sopenharmony_ci "rx packet desc error %x\n", 14428c2ecf20Sopenharmony_ci *((u32 *)prrs + 1)); 14438c2ecf20Sopenharmony_ci goto skip_pkt; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & 14488c2ecf20Sopenharmony_ci RRS_PKT_SIZE_MASK); 14498c2ecf20Sopenharmony_ci if (likely(!(netdev->features & NETIF_F_RXFCS))) 14508c2ecf20Sopenharmony_ci packet_size -= 4; /* CRC */ 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, packet_size); 14538c2ecf20Sopenharmony_ci if (skb == NULL) 14548c2ecf20Sopenharmony_ci goto skip_pkt; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci memcpy(skb->data, (u8 *)(prrs + 1), packet_size); 14578c2ecf20Sopenharmony_ci skb_put(skb, packet_size); 14588c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 14598c2ecf20Sopenharmony_ci atl1e_rx_checksum(adapter, skb, prrs); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (prrs->pkt_flag & RRS_IS_VLAN_TAG) { 14628c2ecf20Sopenharmony_ci u16 vlan_tag = (prrs->vtag >> 4) | 14638c2ecf20Sopenharmony_ci ((prrs->vtag & 7) << 13) | 14648c2ecf20Sopenharmony_ci ((prrs->vtag & 8) << 9); 14658c2ecf20Sopenharmony_ci netdev_dbg(netdev, 14668c2ecf20Sopenharmony_ci "RXD VLAN TAG<RRD>=0x%04x\n", 14678c2ecf20Sopenharmony_ci prrs->vtag); 14688c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci napi_gro_receive(&adapter->napi, skb); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ciskip_pkt: 14738c2ecf20Sopenharmony_ci /* skip current packet whether it's ok or not. */ 14748c2ecf20Sopenharmony_ci rx_page->read_offset += 14758c2ecf20Sopenharmony_ci (((u32)((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & 14768c2ecf20Sopenharmony_ci RRS_PKT_SIZE_MASK) + 14778c2ecf20Sopenharmony_ci sizeof(struct atl1e_recv_ret_status) + 31) & 14788c2ecf20Sopenharmony_ci 0xFFFFFFE0); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (rx_page->read_offset >= rx_ring->page_size) { 14818c2ecf20Sopenharmony_ci /* mark this page clean */ 14828c2ecf20Sopenharmony_ci u16 reg_addr; 14838c2ecf20Sopenharmony_ci u8 rx_using; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci rx_page->read_offset = 14868c2ecf20Sopenharmony_ci *(rx_page->write_offset_addr) = 0; 14878c2ecf20Sopenharmony_ci rx_using = rx_page_desc[que].rx_using; 14888c2ecf20Sopenharmony_ci reg_addr = 14898c2ecf20Sopenharmony_ci atl1e_rx_page_vld_regs[que][rx_using]; 14908c2ecf20Sopenharmony_ci AT_WRITE_REGB(&adapter->hw, reg_addr, 1); 14918c2ecf20Sopenharmony_ci rx_page_desc[que].rx_using ^= 1; 14928c2ecf20Sopenharmony_ci rx_page = atl1e_get_rx_page(adapter, que); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci write_offset = *(rx_page->write_offset_addr); 14958c2ecf20Sopenharmony_ci } while (rx_page->read_offset < write_offset); 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci return; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cifatal_err: 15018c2ecf20Sopenharmony_ci if (!test_bit(__AT_DOWN, &adapter->flags)) 15028c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci/** 15068c2ecf20Sopenharmony_ci * atl1e_clean - NAPI Rx polling callback 15078c2ecf20Sopenharmony_ci * @napi: napi info 15088c2ecf20Sopenharmony_ci * @budget: number of packets to clean 15098c2ecf20Sopenharmony_ci */ 15108c2ecf20Sopenharmony_cistatic int atl1e_clean(struct napi_struct *napi, int budget) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = 15138c2ecf20Sopenharmony_ci container_of(napi, struct atl1e_adapter, napi); 15148c2ecf20Sopenharmony_ci u32 imr_data; 15158c2ecf20Sopenharmony_ci int work_done = 0; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci /* Keep link state information with original netdev */ 15188c2ecf20Sopenharmony_ci if (!netif_carrier_ok(adapter->netdev)) 15198c2ecf20Sopenharmony_ci goto quit_polling; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci atl1e_clean_rx_irq(adapter, 0, &work_done, budget); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci /* If no Tx and not enough Rx work done, exit the polling mode */ 15248c2ecf20Sopenharmony_ci if (work_done < budget) { 15258c2ecf20Sopenharmony_ciquit_polling: 15268c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 15278c2ecf20Sopenharmony_ci imr_data = AT_READ_REG(&adapter->hw, REG_IMR); 15288c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT); 15298c2ecf20Sopenharmony_ci /* test debug */ 15308c2ecf20Sopenharmony_ci if (test_bit(__AT_DOWN, &adapter->flags)) { 15318c2ecf20Sopenharmony_ci atomic_dec(&adapter->irq_sem); 15328c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 15338c2ecf20Sopenharmony_ci "atl1e_clean is called when AT_DOWN\n"); 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci /* reenable RX intr */ 15368c2ecf20Sopenharmony_ci /*atl1e_irq_enable(adapter); */ 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci return work_done; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci/* 15458c2ecf20Sopenharmony_ci * Polling 'interrupt' - used by things like netconsole to send skbs 15468c2ecf20Sopenharmony_ci * without having to re-enable interrupts. It's not called while 15478c2ecf20Sopenharmony_ci * the interrupt routine is executing. 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_cistatic void atl1e_netpoll(struct net_device *netdev) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci disable_irq(adapter->pdev->irq); 15548c2ecf20Sopenharmony_ci atl1e_intr(adapter->pdev->irq, netdev); 15558c2ecf20Sopenharmony_ci enable_irq(adapter->pdev->irq); 15568c2ecf20Sopenharmony_ci} 15578c2ecf20Sopenharmony_ci#endif 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic inline u16 atl1e_tpd_avail(struct atl1e_adapter *adapter) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 15628c2ecf20Sopenharmony_ci u16 next_to_use = 0; 15638c2ecf20Sopenharmony_ci u16 next_to_clean = 0; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci next_to_clean = atomic_read(&tx_ring->next_to_clean); 15668c2ecf20Sopenharmony_ci next_to_use = tx_ring->next_to_use; 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci return (u16)(next_to_clean > next_to_use) ? 15698c2ecf20Sopenharmony_ci (next_to_clean - next_to_use - 1) : 15708c2ecf20Sopenharmony_ci (tx_ring->count + next_to_clean - next_to_use - 1); 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci/* 15748c2ecf20Sopenharmony_ci * get next usable tpd 15758c2ecf20Sopenharmony_ci * Note: should call atl1e_tdp_avail to make sure 15768c2ecf20Sopenharmony_ci * there is enough tpd to use 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_cistatic struct atl1e_tpd_desc *atl1e_get_tpd(struct atl1e_adapter *adapter) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 15818c2ecf20Sopenharmony_ci u16 next_to_use = 0; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci next_to_use = tx_ring->next_to_use; 15848c2ecf20Sopenharmony_ci if (++tx_ring->next_to_use == tx_ring->count) 15858c2ecf20Sopenharmony_ci tx_ring->next_to_use = 0; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci memset(&tx_ring->desc[next_to_use], 0, sizeof(struct atl1e_tpd_desc)); 15888c2ecf20Sopenharmony_ci return &tx_ring->desc[next_to_use]; 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic struct atl1e_tx_buffer * 15928c2ecf20Sopenharmony_ciatl1e_get_tx_buffer(struct atl1e_adapter *adapter, struct atl1e_tpd_desc *tpd) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci return &tx_ring->tx_buffer[tpd - tx_ring->desc]; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci/* Calculate the transmit packet descript needed*/ 16008c2ecf20Sopenharmony_cistatic u16 atl1e_cal_tdp_req(const struct sk_buff *skb) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci int i = 0; 16038c2ecf20Sopenharmony_ci u16 tpd_req = 1; 16048c2ecf20Sopenharmony_ci u16 fg_size = 0; 16058c2ecf20Sopenharmony_ci u16 proto_hdr_len = 0; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 16088c2ecf20Sopenharmony_ci fg_size = skb_frag_size(&skb_shinfo(skb)->frags[i]); 16098c2ecf20Sopenharmony_ci tpd_req += ((fg_size + MAX_TX_BUF_LEN - 1) >> MAX_TX_BUF_SHIFT); 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 16138c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP) || 16148c2ecf20Sopenharmony_ci (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6)) { 16158c2ecf20Sopenharmony_ci proto_hdr_len = skb_transport_offset(skb) + 16168c2ecf20Sopenharmony_ci tcp_hdrlen(skb); 16178c2ecf20Sopenharmony_ci if (proto_hdr_len < skb_headlen(skb)) { 16188c2ecf20Sopenharmony_ci tpd_req += ((skb_headlen(skb) - proto_hdr_len + 16198c2ecf20Sopenharmony_ci MAX_TX_BUF_LEN - 1) >> 16208c2ecf20Sopenharmony_ci MAX_TX_BUF_SHIFT); 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci return tpd_req; 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_cistatic int atl1e_tso_csum(struct atl1e_adapter *adapter, 16298c2ecf20Sopenharmony_ci struct sk_buff *skb, struct atl1e_tpd_desc *tpd) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci unsigned short offload_type; 16328c2ecf20Sopenharmony_ci u8 hdr_len; 16338c2ecf20Sopenharmony_ci u32 real_len; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 16368c2ecf20Sopenharmony_ci int err; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci err = skb_cow_head(skb, 0); 16398c2ecf20Sopenharmony_ci if (err < 0) 16408c2ecf20Sopenharmony_ci return err; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci offload_type = skb_shinfo(skb)->gso_type; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (offload_type & SKB_GSO_TCPV4) { 16458c2ecf20Sopenharmony_ci real_len = (((unsigned char *)ip_hdr(skb) - skb->data) 16468c2ecf20Sopenharmony_ci + ntohs(ip_hdr(skb)->tot_len)); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci if (real_len < skb->len) { 16498c2ecf20Sopenharmony_ci err = pskb_trim(skb, real_len); 16508c2ecf20Sopenharmony_ci if (err) 16518c2ecf20Sopenharmony_ci return err; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); 16558c2ecf20Sopenharmony_ci if (unlikely(skb->len == hdr_len)) { 16568c2ecf20Sopenharmony_ci /* only xsum need */ 16578c2ecf20Sopenharmony_ci netdev_warn(adapter->netdev, 16588c2ecf20Sopenharmony_ci "IPV4 tso with zero data??\n"); 16598c2ecf20Sopenharmony_ci goto check_sum; 16608c2ecf20Sopenharmony_ci } else { 16618c2ecf20Sopenharmony_ci ip_hdr(skb)->check = 0; 16628c2ecf20Sopenharmony_ci ip_hdr(skb)->tot_len = 0; 16638c2ecf20Sopenharmony_ci tcp_hdr(skb)->check = ~csum_tcpudp_magic( 16648c2ecf20Sopenharmony_ci ip_hdr(skb)->saddr, 16658c2ecf20Sopenharmony_ci ip_hdr(skb)->daddr, 16668c2ecf20Sopenharmony_ci 0, IPPROTO_TCP, 0); 16678c2ecf20Sopenharmony_ci tpd->word3 |= (ip_hdr(skb)->ihl & 16688c2ecf20Sopenharmony_ci TDP_V4_IPHL_MASK) << 16698c2ecf20Sopenharmony_ci TPD_V4_IPHL_SHIFT; 16708c2ecf20Sopenharmony_ci tpd->word3 |= ((tcp_hdrlen(skb) >> 2) & 16718c2ecf20Sopenharmony_ci TPD_TCPHDRLEN_MASK) << 16728c2ecf20Sopenharmony_ci TPD_TCPHDRLEN_SHIFT; 16738c2ecf20Sopenharmony_ci tpd->word3 |= ((skb_shinfo(skb)->gso_size) & 16748c2ecf20Sopenharmony_ci TPD_MSS_MASK) << TPD_MSS_SHIFT; 16758c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci return 0; 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_cicheck_sum: 16828c2ecf20Sopenharmony_ci if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { 16838c2ecf20Sopenharmony_ci u8 css, cso; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci cso = skb_checksum_start_offset(skb); 16868c2ecf20Sopenharmony_ci if (unlikely(cso & 0x1)) { 16878c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 16888c2ecf20Sopenharmony_ci "payload offset should not ant event number\n"); 16898c2ecf20Sopenharmony_ci return -1; 16908c2ecf20Sopenharmony_ci } else { 16918c2ecf20Sopenharmony_ci css = cso + skb->csum_offset; 16928c2ecf20Sopenharmony_ci tpd->word3 |= (cso & TPD_PLOADOFFSET_MASK) << 16938c2ecf20Sopenharmony_ci TPD_PLOADOFFSET_SHIFT; 16948c2ecf20Sopenharmony_ci tpd->word3 |= (css & TPD_CCSUMOFFSET_MASK) << 16958c2ecf20Sopenharmony_ci TPD_CCSUMOFFSET_SHIFT; 16968c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_CC_SEGMENT_EN_SHIFT; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci return 0; 17018c2ecf20Sopenharmony_ci} 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_cistatic int atl1e_tx_map(struct atl1e_adapter *adapter, 17048c2ecf20Sopenharmony_ci struct sk_buff *skb, struct atl1e_tpd_desc *tpd) 17058c2ecf20Sopenharmony_ci{ 17068c2ecf20Sopenharmony_ci struct atl1e_tpd_desc *use_tpd = NULL; 17078c2ecf20Sopenharmony_ci struct atl1e_tx_buffer *tx_buffer = NULL; 17088c2ecf20Sopenharmony_ci u16 buf_len = skb_headlen(skb); 17098c2ecf20Sopenharmony_ci u16 map_len = 0; 17108c2ecf20Sopenharmony_ci u16 mapped_len = 0; 17118c2ecf20Sopenharmony_ci u16 hdr_len = 0; 17128c2ecf20Sopenharmony_ci u16 nr_frags; 17138c2ecf20Sopenharmony_ci u16 f; 17148c2ecf20Sopenharmony_ci int segment; 17158c2ecf20Sopenharmony_ci int ring_start = adapter->tx_ring.next_to_use; 17168c2ecf20Sopenharmony_ci int ring_end; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci nr_frags = skb_shinfo(skb)->nr_frags; 17198c2ecf20Sopenharmony_ci segment = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK; 17208c2ecf20Sopenharmony_ci if (segment) { 17218c2ecf20Sopenharmony_ci /* TSO */ 17228c2ecf20Sopenharmony_ci map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 17238c2ecf20Sopenharmony_ci use_tpd = tpd; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); 17268c2ecf20Sopenharmony_ci tx_buffer->length = map_len; 17278c2ecf20Sopenharmony_ci tx_buffer->dma = dma_map_single(&adapter->pdev->dev, 17288c2ecf20Sopenharmony_ci skb->data, hdr_len, 17298c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 17308c2ecf20Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, tx_buffer->dma)) 17318c2ecf20Sopenharmony_ci return -ENOSPC; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_SINGLE); 17348c2ecf20Sopenharmony_ci mapped_len += map_len; 17358c2ecf20Sopenharmony_ci use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); 17368c2ecf20Sopenharmony_ci use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | 17378c2ecf20Sopenharmony_ci ((cpu_to_le32(tx_buffer->length) & 17388c2ecf20Sopenharmony_ci TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci while (mapped_len < buf_len) { 17428c2ecf20Sopenharmony_ci /* mapped_len == 0, means we should use the first tpd, 17438c2ecf20Sopenharmony_ci which is given by caller */ 17448c2ecf20Sopenharmony_ci if (mapped_len == 0) { 17458c2ecf20Sopenharmony_ci use_tpd = tpd; 17468c2ecf20Sopenharmony_ci } else { 17478c2ecf20Sopenharmony_ci use_tpd = atl1e_get_tpd(adapter); 17488c2ecf20Sopenharmony_ci memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc)); 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); 17518c2ecf20Sopenharmony_ci tx_buffer->skb = NULL; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci tx_buffer->length = map_len = 17548c2ecf20Sopenharmony_ci ((buf_len - mapped_len) >= MAX_TX_BUF_LEN) ? 17558c2ecf20Sopenharmony_ci MAX_TX_BUF_LEN : (buf_len - mapped_len); 17568c2ecf20Sopenharmony_ci tx_buffer->dma = 17578c2ecf20Sopenharmony_ci dma_map_single(&adapter->pdev->dev, 17588c2ecf20Sopenharmony_ci skb->data + mapped_len, map_len, 17598c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, tx_buffer->dma)) { 17628c2ecf20Sopenharmony_ci /* We need to unwind the mappings we've done */ 17638c2ecf20Sopenharmony_ci ring_end = adapter->tx_ring.next_to_use; 17648c2ecf20Sopenharmony_ci adapter->tx_ring.next_to_use = ring_start; 17658c2ecf20Sopenharmony_ci while (adapter->tx_ring.next_to_use != ring_end) { 17668c2ecf20Sopenharmony_ci tpd = atl1e_get_tpd(adapter); 17678c2ecf20Sopenharmony_ci tx_buffer = atl1e_get_tx_buffer(adapter, tpd); 17688c2ecf20Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, 17698c2ecf20Sopenharmony_ci tx_buffer->dma, 17708c2ecf20Sopenharmony_ci tx_buffer->length, 17718c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci /* Reset the tx rings next pointer */ 17748c2ecf20Sopenharmony_ci adapter->tx_ring.next_to_use = ring_start; 17758c2ecf20Sopenharmony_ci return -ENOSPC; 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_SINGLE); 17798c2ecf20Sopenharmony_ci mapped_len += map_len; 17808c2ecf20Sopenharmony_ci use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); 17818c2ecf20Sopenharmony_ci use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | 17828c2ecf20Sopenharmony_ci ((cpu_to_le32(tx_buffer->length) & 17838c2ecf20Sopenharmony_ci TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci for (f = 0; f < nr_frags; f++) { 17878c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 17888c2ecf20Sopenharmony_ci u16 i; 17898c2ecf20Sopenharmony_ci u16 seg_num; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci buf_len = skb_frag_size(frag); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci seg_num = (buf_len + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; 17948c2ecf20Sopenharmony_ci for (i = 0; i < seg_num; i++) { 17958c2ecf20Sopenharmony_ci use_tpd = atl1e_get_tpd(adapter); 17968c2ecf20Sopenharmony_ci memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc)); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); 17998c2ecf20Sopenharmony_ci BUG_ON(tx_buffer->skb); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci tx_buffer->skb = NULL; 18028c2ecf20Sopenharmony_ci tx_buffer->length = 18038c2ecf20Sopenharmony_ci (buf_len > MAX_TX_BUF_LEN) ? 18048c2ecf20Sopenharmony_ci MAX_TX_BUF_LEN : buf_len; 18058c2ecf20Sopenharmony_ci buf_len -= tx_buffer->length; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci tx_buffer->dma = skb_frag_dma_map(&adapter->pdev->dev, 18088c2ecf20Sopenharmony_ci frag, 18098c2ecf20Sopenharmony_ci (i * MAX_TX_BUF_LEN), 18108c2ecf20Sopenharmony_ci tx_buffer->length, 18118c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, tx_buffer->dma)) { 18148c2ecf20Sopenharmony_ci /* We need to unwind the mappings we've done */ 18158c2ecf20Sopenharmony_ci ring_end = adapter->tx_ring.next_to_use; 18168c2ecf20Sopenharmony_ci adapter->tx_ring.next_to_use = ring_start; 18178c2ecf20Sopenharmony_ci while (adapter->tx_ring.next_to_use != ring_end) { 18188c2ecf20Sopenharmony_ci tpd = atl1e_get_tpd(adapter); 18198c2ecf20Sopenharmony_ci tx_buffer = atl1e_get_tx_buffer(adapter, tpd); 18208c2ecf20Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, tx_buffer->dma, 18218c2ecf20Sopenharmony_ci tx_buffer->length, DMA_TO_DEVICE); 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci /* Reset the ring next to use pointer */ 18258c2ecf20Sopenharmony_ci adapter->tx_ring.next_to_use = ring_start; 18268c2ecf20Sopenharmony_ci return -ENOSPC; 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_PAGE); 18308c2ecf20Sopenharmony_ci use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); 18318c2ecf20Sopenharmony_ci use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | 18328c2ecf20Sopenharmony_ci ((cpu_to_le32(tx_buffer->length) & 18338c2ecf20Sopenharmony_ci TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci if ((tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK) 18388c2ecf20Sopenharmony_ci /* note this one is a tcp header */ 18398c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT; 18408c2ecf20Sopenharmony_ci /* The last tpd */ 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci use_tpd->word3 |= 1 << TPD_EOP_SHIFT; 18438c2ecf20Sopenharmony_ci /* The last buffer info contain the skb address, 18448c2ecf20Sopenharmony_ci so it will be free after unmap */ 18458c2ecf20Sopenharmony_ci tx_buffer->skb = skb; 18468c2ecf20Sopenharmony_ci return 0; 18478c2ecf20Sopenharmony_ci} 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_cistatic void atl1e_tx_queue(struct atl1e_adapter *adapter, u16 count, 18508c2ecf20Sopenharmony_ci struct atl1e_tpd_desc *tpd) 18518c2ecf20Sopenharmony_ci{ 18528c2ecf20Sopenharmony_ci struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; 18538c2ecf20Sopenharmony_ci /* Force memory writes to complete before letting h/w 18548c2ecf20Sopenharmony_ci * know there are new descriptors to fetch. (Only 18558c2ecf20Sopenharmony_ci * applicable for weak-ordered memory model archs, 18568c2ecf20Sopenharmony_ci * such as IA-64). */ 18578c2ecf20Sopenharmony_ci wmb(); 18588c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_MB_TPD_PROD_IDX, tx_ring->next_to_use); 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_cistatic netdev_tx_t atl1e_xmit_frame(struct sk_buff *skb, 18628c2ecf20Sopenharmony_ci struct net_device *netdev) 18638c2ecf20Sopenharmony_ci{ 18648c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 18658c2ecf20Sopenharmony_ci u16 tpd_req = 1; 18668c2ecf20Sopenharmony_ci struct atl1e_tpd_desc *tpd; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if (test_bit(__AT_DOWN, &adapter->flags)) { 18698c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 18708c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (unlikely(skb->len <= 0)) { 18748c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 18758c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci tpd_req = atl1e_cal_tdp_req(skb); 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci if (atl1e_tpd_avail(adapter) < tpd_req) { 18808c2ecf20Sopenharmony_ci /* no enough descriptor, just stop queue */ 18818c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 18828c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci tpd = atl1e_get_tpd(adapter); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 18888c2ecf20Sopenharmony_ci u16 vlan_tag = skb_vlan_tag_get(skb); 18898c2ecf20Sopenharmony_ci u16 atl1e_vlan_tag; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; 18928c2ecf20Sopenharmony_ci AT_VLAN_TAG_TO_TPD_TAG(vlan_tag, atl1e_vlan_tag); 18938c2ecf20Sopenharmony_ci tpd->word2 |= (atl1e_vlan_tag & TPD_VLANTAG_MASK) << 18948c2ecf20Sopenharmony_ci TPD_VLAN_SHIFT; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_8021Q)) 18988c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_VL_TAGGED_SHIFT; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci if (skb_network_offset(skb) != ETH_HLEN) 19018c2ecf20Sopenharmony_ci tpd->word3 |= 1 << TPD_ETHTYPE_SHIFT; /* 802.3 frame */ 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci /* do TSO and check sum */ 19048c2ecf20Sopenharmony_ci if (atl1e_tso_csum(adapter, skb, tpd) != 0) { 19058c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 19068c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci if (atl1e_tx_map(adapter, skb, tpd)) { 19108c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 19118c2ecf20Sopenharmony_ci goto out; 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci atl1e_tx_queue(adapter, tpd_req, tpd); 19158c2ecf20Sopenharmony_ciout: 19168c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 19178c2ecf20Sopenharmony_ci} 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_cistatic void atl1e_free_irq(struct atl1e_adapter *adapter) 19208c2ecf20Sopenharmony_ci{ 19218c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci free_irq(adapter->pdev->irq, netdev); 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_cistatic int atl1e_request_irq(struct atl1e_adapter *adapter) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 19298c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 19308c2ecf20Sopenharmony_ci int err = 0; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci err = request_irq(pdev->irq, atl1e_intr, IRQF_SHARED, netdev->name, 19338c2ecf20Sopenharmony_ci netdev); 19348c2ecf20Sopenharmony_ci if (err) { 19358c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, 19368c2ecf20Sopenharmony_ci "Unable to allocate interrupt Error: %d\n", err); 19378c2ecf20Sopenharmony_ci return err; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci netdev_dbg(netdev, "atl1e_request_irq OK\n"); 19408c2ecf20Sopenharmony_ci return err; 19418c2ecf20Sopenharmony_ci} 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ciint atl1e_up(struct atl1e_adapter *adapter) 19448c2ecf20Sopenharmony_ci{ 19458c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 19468c2ecf20Sopenharmony_ci int err = 0; 19478c2ecf20Sopenharmony_ci u32 val; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* hardware has been reset, we need to reload some things */ 19508c2ecf20Sopenharmony_ci err = atl1e_init_hw(&adapter->hw); 19518c2ecf20Sopenharmony_ci if (err) { 19528c2ecf20Sopenharmony_ci err = -EIO; 19538c2ecf20Sopenharmony_ci return err; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci atl1e_init_ring_ptrs(adapter); 19568c2ecf20Sopenharmony_ci atl1e_set_multi(netdev); 19578c2ecf20Sopenharmony_ci atl1e_restore_vlan(adapter); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci if (atl1e_configure(adapter)) { 19608c2ecf20Sopenharmony_ci err = -EIO; 19618c2ecf20Sopenharmony_ci goto err_up; 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci clear_bit(__AT_DOWN, &adapter->flags); 19658c2ecf20Sopenharmony_ci napi_enable(&adapter->napi); 19668c2ecf20Sopenharmony_ci atl1e_irq_enable(adapter); 19678c2ecf20Sopenharmony_ci val = AT_READ_REG(&adapter->hw, REG_MASTER_CTRL); 19688c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, 19698c2ecf20Sopenharmony_ci val | MASTER_CTRL_MANUAL_INT); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_cierr_up: 19728c2ecf20Sopenharmony_ci return err; 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_civoid atl1e_down(struct atl1e_adapter *adapter) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci /* signal that we're down so the interrupt handler does not 19808c2ecf20Sopenharmony_ci * reschedule our watchdog timer */ 19818c2ecf20Sopenharmony_ci set_bit(__AT_DOWN, &adapter->flags); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* reset MAC to disable all RX/TX */ 19868c2ecf20Sopenharmony_ci atl1e_reset_hw(&adapter->hw); 19878c2ecf20Sopenharmony_ci msleep(1); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci napi_disable(&adapter->napi); 19908c2ecf20Sopenharmony_ci atl1e_del_timer(adapter); 19918c2ecf20Sopenharmony_ci atl1e_irq_disable(adapter); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 19948c2ecf20Sopenharmony_ci adapter->link_speed = SPEED_0; 19958c2ecf20Sopenharmony_ci adapter->link_duplex = -1; 19968c2ecf20Sopenharmony_ci atl1e_clean_tx_ring(adapter); 19978c2ecf20Sopenharmony_ci atl1e_clean_rx_ring(adapter); 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci/** 20018c2ecf20Sopenharmony_ci * atl1e_open - Called when a network interface is made active 20028c2ecf20Sopenharmony_ci * @netdev: network interface device structure 20038c2ecf20Sopenharmony_ci * 20048c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure 20058c2ecf20Sopenharmony_ci * 20068c2ecf20Sopenharmony_ci * The open entry point is called when a network interface is made 20078c2ecf20Sopenharmony_ci * active by the system (IFF_UP). At this point all resources needed 20088c2ecf20Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt 20098c2ecf20Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started, 20108c2ecf20Sopenharmony_ci * and the stack is notified that the interface is ready. 20118c2ecf20Sopenharmony_ci */ 20128c2ecf20Sopenharmony_cistatic int atl1e_open(struct net_device *netdev) 20138c2ecf20Sopenharmony_ci{ 20148c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 20158c2ecf20Sopenharmony_ci int err; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci /* disallow open during test */ 20188c2ecf20Sopenharmony_ci if (test_bit(__AT_TESTING, &adapter->flags)) 20198c2ecf20Sopenharmony_ci return -EBUSY; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* allocate rx/tx dma buffer & descriptors */ 20228c2ecf20Sopenharmony_ci atl1e_init_ring_resources(adapter); 20238c2ecf20Sopenharmony_ci err = atl1e_setup_ring_resources(adapter); 20248c2ecf20Sopenharmony_ci if (unlikely(err)) 20258c2ecf20Sopenharmony_ci return err; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci err = atl1e_request_irq(adapter); 20288c2ecf20Sopenharmony_ci if (unlikely(err)) 20298c2ecf20Sopenharmony_ci goto err_req_irq; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci err = atl1e_up(adapter); 20328c2ecf20Sopenharmony_ci if (unlikely(err)) 20338c2ecf20Sopenharmony_ci goto err_up; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci return 0; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_cierr_up: 20388c2ecf20Sopenharmony_ci atl1e_free_irq(adapter); 20398c2ecf20Sopenharmony_cierr_req_irq: 20408c2ecf20Sopenharmony_ci atl1e_free_ring_resources(adapter); 20418c2ecf20Sopenharmony_ci atl1e_reset_hw(&adapter->hw); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci return err; 20448c2ecf20Sopenharmony_ci} 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci/** 20478c2ecf20Sopenharmony_ci * atl1e_close - Disables a network interface 20488c2ecf20Sopenharmony_ci * @netdev: network interface device structure 20498c2ecf20Sopenharmony_ci * 20508c2ecf20Sopenharmony_ci * Returns 0, this is not allowed to fail 20518c2ecf20Sopenharmony_ci * 20528c2ecf20Sopenharmony_ci * The close entry point is called when an interface is de-activated 20538c2ecf20Sopenharmony_ci * by the OS. The hardware is still under the drivers control, but 20548c2ecf20Sopenharmony_ci * needs to be disabled. A global MAC reset is issued to stop the 20558c2ecf20Sopenharmony_ci * hardware, and all transmit and receive resources are freed. 20568c2ecf20Sopenharmony_ci */ 20578c2ecf20Sopenharmony_cistatic int atl1e_close(struct net_device *netdev) 20588c2ecf20Sopenharmony_ci{ 20598c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); 20628c2ecf20Sopenharmony_ci atl1e_down(adapter); 20638c2ecf20Sopenharmony_ci atl1e_free_irq(adapter); 20648c2ecf20Sopenharmony_ci atl1e_free_ring_resources(adapter); 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci return 0; 20678c2ecf20Sopenharmony_ci} 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_cistatic int atl1e_suspend(struct pci_dev *pdev, pm_message_t state) 20708c2ecf20Sopenharmony_ci{ 20718c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 20728c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 20738c2ecf20Sopenharmony_ci struct atl1e_hw *hw = &adapter->hw; 20748c2ecf20Sopenharmony_ci u32 ctrl = 0; 20758c2ecf20Sopenharmony_ci u32 mac_ctrl_data = 0; 20768c2ecf20Sopenharmony_ci u32 wol_ctrl_data = 0; 20778c2ecf20Sopenharmony_ci u16 mii_advertise_data = 0; 20788c2ecf20Sopenharmony_ci u16 mii_bmsr_data = 0; 20798c2ecf20Sopenharmony_ci u16 mii_intr_status_data = 0; 20808c2ecf20Sopenharmony_ci u32 wufc = adapter->wol; 20818c2ecf20Sopenharmony_ci u32 i; 20828c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 20838c2ecf20Sopenharmony_ci int retval = 0; 20848c2ecf20Sopenharmony_ci#endif 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 20878c2ecf20Sopenharmony_ci WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); 20888c2ecf20Sopenharmony_ci atl1e_down(adapter); 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci netif_device_detach(netdev); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 20938c2ecf20Sopenharmony_ci retval = pci_save_state(pdev); 20948c2ecf20Sopenharmony_ci if (retval) 20958c2ecf20Sopenharmony_ci return retval; 20968c2ecf20Sopenharmony_ci#endif 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci if (wufc) { 20998c2ecf20Sopenharmony_ci /* get link status */ 21008c2ecf20Sopenharmony_ci atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); 21018c2ecf20Sopenharmony_ci atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci mii_advertise_data = ADVERTISE_10HALF; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci if ((atl1e_write_phy_reg(hw, MII_CTRL1000, 0) != 0) || 21068c2ecf20Sopenharmony_ci (atl1e_write_phy_reg(hw, 21078c2ecf20Sopenharmony_ci MII_ADVERTISE, mii_advertise_data) != 0) || 21088c2ecf20Sopenharmony_ci (atl1e_phy_commit(hw)) != 0) { 21098c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, "set phy register failed\n"); 21108c2ecf20Sopenharmony_ci goto wol_dis; 21118c2ecf20Sopenharmony_ci } 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci hw->phy_configured = false; /* re-init PHY when resume */ 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci /* turn on magic packet wol */ 21168c2ecf20Sopenharmony_ci if (wufc & AT_WUFC_MAG) 21178c2ecf20Sopenharmony_ci wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci if (wufc & AT_WUFC_LNKC) { 21208c2ecf20Sopenharmony_ci /* if orignal link status is link, just wait for retrive link */ 21218c2ecf20Sopenharmony_ci if (mii_bmsr_data & BMSR_LSTATUS) { 21228c2ecf20Sopenharmony_ci for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { 21238c2ecf20Sopenharmony_ci msleep(100); 21248c2ecf20Sopenharmony_ci atl1e_read_phy_reg(hw, MII_BMSR, 21258c2ecf20Sopenharmony_ci &mii_bmsr_data); 21268c2ecf20Sopenharmony_ci if (mii_bmsr_data & BMSR_LSTATUS) 21278c2ecf20Sopenharmony_ci break; 21288c2ecf20Sopenharmony_ci } 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci if ((mii_bmsr_data & BMSR_LSTATUS) == 0) 21318c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, 21328c2ecf20Sopenharmony_ci "Link may change when suspend\n"); 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; 21358c2ecf20Sopenharmony_ci /* only link up can wake up */ 21368c2ecf20Sopenharmony_ci if (atl1e_write_phy_reg(hw, MII_INT_CTRL, 0x400) != 0) { 21378c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, 21388c2ecf20Sopenharmony_ci "read write phy register failed\n"); 21398c2ecf20Sopenharmony_ci goto wol_dis; 21408c2ecf20Sopenharmony_ci } 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci /* clear phy interrupt */ 21438c2ecf20Sopenharmony_ci atl1e_read_phy_reg(hw, MII_INT_STATUS, &mii_intr_status_data); 21448c2ecf20Sopenharmony_ci /* Config MAC Ctrl register */ 21458c2ecf20Sopenharmony_ci mac_ctrl_data = MAC_CTRL_RX_EN; 21468c2ecf20Sopenharmony_ci /* set to 10/100M halt duplex */ 21478c2ecf20Sopenharmony_ci mac_ctrl_data |= MAC_CTRL_SPEED_10_100 << MAC_CTRL_SPEED_SHIFT; 21488c2ecf20Sopenharmony_ci mac_ctrl_data |= (((u32)adapter->hw.preamble_len & 21498c2ecf20Sopenharmony_ci MAC_CTRL_PRMLEN_MASK) << 21508c2ecf20Sopenharmony_ci MAC_CTRL_PRMLEN_SHIFT); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci __atl1e_vlan_mode(netdev->features, &mac_ctrl_data); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci /* magic packet maybe Broadcast&multicast&Unicast frame */ 21558c2ecf20Sopenharmony_ci if (wufc & AT_WUFC_MAG) 21568c2ecf20Sopenharmony_ci mac_ctrl_data |= MAC_CTRL_BC_EN; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci netdev_dbg(adapter->netdev, "suspend MAC=0x%x\n", 21598c2ecf20Sopenharmony_ci mac_ctrl_data); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data); 21628c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); 21638c2ecf20Sopenharmony_ci /* pcie patch */ 21648c2ecf20Sopenharmony_ci ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC); 21658c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 21668c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); 21678c2ecf20Sopenharmony_ci pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); 21688c2ecf20Sopenharmony_ci goto suspend_exit; 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ciwol_dis: 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* WOL disabled */ 21738c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_WOL_CTRL, 0); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci /* pcie patch */ 21768c2ecf20Sopenharmony_ci ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC); 21778c2ecf20Sopenharmony_ci ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; 21788c2ecf20Sopenharmony_ci AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci atl1e_force_ps(hw); 21818c2ecf20Sopenharmony_ci hw->phy_configured = false; /* re-init PHY when resume */ 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_cisuspend_exit: 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci if (netif_running(netdev)) 21888c2ecf20Sopenharmony_ci atl1e_free_irq(adapter); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci pci_disable_device(pdev); 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci pci_set_power_state(pdev, pci_choose_state(pdev, state)); 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci return 0; 21958c2ecf20Sopenharmony_ci} 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 21988c2ecf20Sopenharmony_cistatic int atl1e_resume(struct pci_dev *pdev) 21998c2ecf20Sopenharmony_ci{ 22008c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 22018c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 22028c2ecf20Sopenharmony_ci u32 err; 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 22058c2ecf20Sopenharmony_ci pci_restore_state(pdev); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 22088c2ecf20Sopenharmony_ci if (err) { 22098c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 22108c2ecf20Sopenharmony_ci "Cannot enable PCI device from suspend\n"); 22118c2ecf20Sopenharmony_ci return err; 22128c2ecf20Sopenharmony_ci } 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci pci_set_master(pdev); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci AT_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D3hot, 0); 22198c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D3cold, 0); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 22248c2ecf20Sopenharmony_ci err = atl1e_request_irq(adapter); 22258c2ecf20Sopenharmony_ci if (err) 22268c2ecf20Sopenharmony_ci return err; 22278c2ecf20Sopenharmony_ci } 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci atl1e_reset_hw(&adapter->hw); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci if (netif_running(netdev)) 22328c2ecf20Sopenharmony_ci atl1e_up(adapter); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci netif_device_attach(netdev); 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci return 0; 22378c2ecf20Sopenharmony_ci} 22388c2ecf20Sopenharmony_ci#endif 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_cistatic void atl1e_shutdown(struct pci_dev *pdev) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci atl1e_suspend(pdev, PMSG_SUSPEND); 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic const struct net_device_ops atl1e_netdev_ops = { 22468c2ecf20Sopenharmony_ci .ndo_open = atl1e_open, 22478c2ecf20Sopenharmony_ci .ndo_stop = atl1e_close, 22488c2ecf20Sopenharmony_ci .ndo_start_xmit = atl1e_xmit_frame, 22498c2ecf20Sopenharmony_ci .ndo_get_stats = atl1e_get_stats, 22508c2ecf20Sopenharmony_ci .ndo_set_rx_mode = atl1e_set_multi, 22518c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 22528c2ecf20Sopenharmony_ci .ndo_set_mac_address = atl1e_set_mac_addr, 22538c2ecf20Sopenharmony_ci .ndo_fix_features = atl1e_fix_features, 22548c2ecf20Sopenharmony_ci .ndo_set_features = atl1e_set_features, 22558c2ecf20Sopenharmony_ci .ndo_change_mtu = atl1e_change_mtu, 22568c2ecf20Sopenharmony_ci .ndo_do_ioctl = atl1e_ioctl, 22578c2ecf20Sopenharmony_ci .ndo_tx_timeout = atl1e_tx_timeout, 22588c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 22598c2ecf20Sopenharmony_ci .ndo_poll_controller = atl1e_netpoll, 22608c2ecf20Sopenharmony_ci#endif 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci}; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_cistatic int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev) 22658c2ecf20Sopenharmony_ci{ 22668c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 22678c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, netdev); 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci netdev->netdev_ops = &atl1e_netdev_ops; 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci netdev->watchdog_timeo = AT_TX_WATCHDOG; 22728c2ecf20Sopenharmony_ci /* MTU range: 42 - 8170 */ 22738c2ecf20Sopenharmony_ci netdev->min_mtu = ETH_ZLEN - (ETH_HLEN + VLAN_HLEN); 22748c2ecf20Sopenharmony_ci netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - 22758c2ecf20Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); 22768c2ecf20Sopenharmony_ci atl1e_set_ethtool_ops(netdev); 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | 22798c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX; 22808c2ecf20Sopenharmony_ci netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_TX; 22818c2ecf20Sopenharmony_ci /* not enabled by default */ 22828c2ecf20Sopenharmony_ci netdev->hw_features |= NETIF_F_RXALL | NETIF_F_RXFCS; 22838c2ecf20Sopenharmony_ci return 0; 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci/** 22878c2ecf20Sopenharmony_ci * atl1e_probe - Device Initialization Routine 22888c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 22898c2ecf20Sopenharmony_ci * @ent: entry in atl1e_pci_tbl 22908c2ecf20Sopenharmony_ci * 22918c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 22928c2ecf20Sopenharmony_ci * 22938c2ecf20Sopenharmony_ci * atl1e_probe initializes an adapter identified by a pci_dev structure. 22948c2ecf20Sopenharmony_ci * The OS initialization, configuring of the adapter private structure, 22958c2ecf20Sopenharmony_ci * and a hardware reset occur. 22968c2ecf20Sopenharmony_ci */ 22978c2ecf20Sopenharmony_cistatic int atl1e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 22988c2ecf20Sopenharmony_ci{ 22998c2ecf20Sopenharmony_ci struct net_device *netdev; 23008c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = NULL; 23018c2ecf20Sopenharmony_ci static int cards_found; 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci int err = 0; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 23068c2ecf20Sopenharmony_ci if (err) { 23078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot enable PCI device\n"); 23088c2ecf20Sopenharmony_ci return err; 23098c2ecf20Sopenharmony_ci } 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci /* 23128c2ecf20Sopenharmony_ci * The atl1e chip can DMA to 64-bit addresses, but it uses a single 23138c2ecf20Sopenharmony_ci * shared register for the high 32 bits, so only a single, aligned, 23148c2ecf20Sopenharmony_ci * 4 GB physical address range can be used at a time. 23158c2ecf20Sopenharmony_ci * 23168c2ecf20Sopenharmony_ci * Supporting 64-bit DMA on this hardware is more trouble than it's 23178c2ecf20Sopenharmony_ci * worth. It is far easier to limit to 32-bit DMA than update 23188c2ecf20Sopenharmony_ci * various kernel subsystems to support the mechanics required by a 23198c2ecf20Sopenharmony_ci * fixed-high-32-bit system. 23208c2ecf20Sopenharmony_ci */ 23218c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 23228c2ecf20Sopenharmony_ci if (err) { 23238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No usable DMA configuration,aborting\n"); 23248c2ecf20Sopenharmony_ci goto err_dma; 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, atl1e_driver_name); 23288c2ecf20Sopenharmony_ci if (err) { 23298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot obtain PCI resources\n"); 23308c2ecf20Sopenharmony_ci goto err_pci_reg; 23318c2ecf20Sopenharmony_ci } 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci pci_set_master(pdev); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct atl1e_adapter)); 23368c2ecf20Sopenharmony_ci if (netdev == NULL) { 23378c2ecf20Sopenharmony_ci err = -ENOMEM; 23388c2ecf20Sopenharmony_ci goto err_alloc_etherdev; 23398c2ecf20Sopenharmony_ci } 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci err = atl1e_init_netdev(netdev, pdev); 23428c2ecf20Sopenharmony_ci if (err) { 23438c2ecf20Sopenharmony_ci netdev_err(netdev, "init netdevice failed\n"); 23448c2ecf20Sopenharmony_ci goto err_init_netdev; 23458c2ecf20Sopenharmony_ci } 23468c2ecf20Sopenharmony_ci adapter = netdev_priv(netdev); 23478c2ecf20Sopenharmony_ci adapter->bd_number = cards_found; 23488c2ecf20Sopenharmony_ci adapter->netdev = netdev; 23498c2ecf20Sopenharmony_ci adapter->pdev = pdev; 23508c2ecf20Sopenharmony_ci adapter->hw.adapter = adapter; 23518c2ecf20Sopenharmony_ci adapter->hw.hw_addr = pci_iomap(pdev, BAR_0, 0); 23528c2ecf20Sopenharmony_ci if (!adapter->hw.hw_addr) { 23538c2ecf20Sopenharmony_ci err = -EIO; 23548c2ecf20Sopenharmony_ci netdev_err(netdev, "cannot map device registers\n"); 23558c2ecf20Sopenharmony_ci goto err_ioremap; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci /* init mii data */ 23598c2ecf20Sopenharmony_ci adapter->mii.dev = netdev; 23608c2ecf20Sopenharmony_ci adapter->mii.mdio_read = atl1e_mdio_read; 23618c2ecf20Sopenharmony_ci adapter->mii.mdio_write = atl1e_mdio_write; 23628c2ecf20Sopenharmony_ci adapter->mii.phy_id_mask = 0x1f; 23638c2ecf20Sopenharmony_ci adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci timer_setup(&adapter->phy_config_timer, atl1e_phy_config, 0); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci /* get user settings */ 23708c2ecf20Sopenharmony_ci atl1e_check_options(adapter); 23718c2ecf20Sopenharmony_ci /* 23728c2ecf20Sopenharmony_ci * Mark all PCI regions associated with PCI device 23738c2ecf20Sopenharmony_ci * pdev as being reserved by owner atl1e_driver_name 23748c2ecf20Sopenharmony_ci * Enables bus-mastering on the device and calls 23758c2ecf20Sopenharmony_ci * pcibios_set_master to do the needed arch specific settings 23768c2ecf20Sopenharmony_ci */ 23778c2ecf20Sopenharmony_ci atl1e_setup_pcicmd(pdev); 23788c2ecf20Sopenharmony_ci /* setup the private structure */ 23798c2ecf20Sopenharmony_ci err = atl1e_sw_init(adapter); 23808c2ecf20Sopenharmony_ci if (err) { 23818c2ecf20Sopenharmony_ci netdev_err(netdev, "net device private data init failed\n"); 23828c2ecf20Sopenharmony_ci goto err_sw_init; 23838c2ecf20Sopenharmony_ci } 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci /* Init GPHY as early as possible due to power saving issue */ 23868c2ecf20Sopenharmony_ci atl1e_phy_init(&adapter->hw); 23878c2ecf20Sopenharmony_ci /* reset the controller to 23888c2ecf20Sopenharmony_ci * put the device in a known good starting state */ 23898c2ecf20Sopenharmony_ci err = atl1e_reset_hw(&adapter->hw); 23908c2ecf20Sopenharmony_ci if (err) { 23918c2ecf20Sopenharmony_ci err = -EIO; 23928c2ecf20Sopenharmony_ci goto err_reset; 23938c2ecf20Sopenharmony_ci } 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (atl1e_read_mac_addr(&adapter->hw) != 0) { 23968c2ecf20Sopenharmony_ci err = -EIO; 23978c2ecf20Sopenharmony_ci netdev_err(netdev, "get mac address failed\n"); 23988c2ecf20Sopenharmony_ci goto err_eeprom; 23998c2ecf20Sopenharmony_ci } 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); 24028c2ecf20Sopenharmony_ci netdev_dbg(netdev, "mac address : %pM\n", adapter->hw.mac_addr); 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci INIT_WORK(&adapter->reset_task, atl1e_reset_task); 24058c2ecf20Sopenharmony_ci INIT_WORK(&adapter->link_chg_task, atl1e_link_chg_task); 24068c2ecf20Sopenharmony_ci netif_set_gso_max_size(netdev, MAX_TSO_SEG_SIZE); 24078c2ecf20Sopenharmony_ci err = register_netdev(netdev); 24088c2ecf20Sopenharmony_ci if (err) { 24098c2ecf20Sopenharmony_ci netdev_err(netdev, "register netdevice failed\n"); 24108c2ecf20Sopenharmony_ci goto err_register; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci /* assume we have no link for now */ 24148c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 24158c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci cards_found++; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci return 0; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_cierr_reset: 24228c2ecf20Sopenharmony_cierr_register: 24238c2ecf20Sopenharmony_cierr_sw_init: 24248c2ecf20Sopenharmony_cierr_eeprom: 24258c2ecf20Sopenharmony_ci pci_iounmap(pdev, adapter->hw.hw_addr); 24268c2ecf20Sopenharmony_cierr_init_netdev: 24278c2ecf20Sopenharmony_cierr_ioremap: 24288c2ecf20Sopenharmony_ci free_netdev(netdev); 24298c2ecf20Sopenharmony_cierr_alloc_etherdev: 24308c2ecf20Sopenharmony_ci pci_release_regions(pdev); 24318c2ecf20Sopenharmony_cierr_pci_reg: 24328c2ecf20Sopenharmony_cierr_dma: 24338c2ecf20Sopenharmony_ci pci_disable_device(pdev); 24348c2ecf20Sopenharmony_ci return err; 24358c2ecf20Sopenharmony_ci} 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci/** 24388c2ecf20Sopenharmony_ci * atl1e_remove - Device Removal Routine 24398c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 24408c2ecf20Sopenharmony_ci * 24418c2ecf20Sopenharmony_ci * atl1e_remove is called by the PCI subsystem to alert the driver 24428c2ecf20Sopenharmony_ci * that it should release a PCI device. The could be caused by a 24438c2ecf20Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from 24448c2ecf20Sopenharmony_ci * memory. 24458c2ecf20Sopenharmony_ci */ 24468c2ecf20Sopenharmony_cistatic void atl1e_remove(struct pci_dev *pdev) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 24498c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci /* 24528c2ecf20Sopenharmony_ci * flush_scheduled work may reschedule our watchdog task, so 24538c2ecf20Sopenharmony_ci * explicitly disable watchdog tasks from being rescheduled 24548c2ecf20Sopenharmony_ci */ 24558c2ecf20Sopenharmony_ci set_bit(__AT_DOWN, &adapter->flags); 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci atl1e_del_timer(adapter); 24588c2ecf20Sopenharmony_ci atl1e_cancel_work(adapter); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci unregister_netdev(netdev); 24618c2ecf20Sopenharmony_ci atl1e_free_ring_resources(adapter); 24628c2ecf20Sopenharmony_ci atl1e_force_ps(&adapter->hw); 24638c2ecf20Sopenharmony_ci pci_iounmap(pdev, adapter->hw.hw_addr); 24648c2ecf20Sopenharmony_ci pci_release_regions(pdev); 24658c2ecf20Sopenharmony_ci free_netdev(netdev); 24668c2ecf20Sopenharmony_ci pci_disable_device(pdev); 24678c2ecf20Sopenharmony_ci} 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci/** 24708c2ecf20Sopenharmony_ci * atl1e_io_error_detected - called when PCI error is detected 24718c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device 24728c2ecf20Sopenharmony_ci * @state: The current pci connection state 24738c2ecf20Sopenharmony_ci * 24748c2ecf20Sopenharmony_ci * This function is called after a PCI bus error affecting 24758c2ecf20Sopenharmony_ci * this device has been detected. 24768c2ecf20Sopenharmony_ci */ 24778c2ecf20Sopenharmony_cistatic pci_ers_result_t 24788c2ecf20Sopenharmony_ciatl1e_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 24818c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci netif_device_detach(netdev); 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci if (state == pci_channel_io_perm_failure) 24868c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci if (netif_running(netdev)) 24898c2ecf20Sopenharmony_ci atl1e_down(adapter); 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci pci_disable_device(pdev); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci /* Request a slot slot reset. */ 24948c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 24958c2ecf20Sopenharmony_ci} 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci/** 24988c2ecf20Sopenharmony_ci * atl1e_io_slot_reset - called after the pci bus has been reset. 24998c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device 25008c2ecf20Sopenharmony_ci * 25018c2ecf20Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot. Implementation 25028c2ecf20Sopenharmony_ci * resembles the first-half of the e1000_resume routine. 25038c2ecf20Sopenharmony_ci */ 25048c2ecf20Sopenharmony_cistatic pci_ers_result_t atl1e_io_slot_reset(struct pci_dev *pdev) 25058c2ecf20Sopenharmony_ci{ 25068c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 25078c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) { 25108c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 25118c2ecf20Sopenharmony_ci "Cannot re-enable PCI device after reset\n"); 25128c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 25138c2ecf20Sopenharmony_ci } 25148c2ecf20Sopenharmony_ci pci_set_master(pdev); 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D3hot, 0); 25178c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D3cold, 0); 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci atl1e_reset_hw(&adapter->hw); 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 25228c2ecf20Sopenharmony_ci} 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci/** 25258c2ecf20Sopenharmony_ci * atl1e_io_resume - called when traffic can start flowing again. 25268c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device 25278c2ecf20Sopenharmony_ci * 25288c2ecf20Sopenharmony_ci * This callback is called when the error recovery driver tells us that 25298c2ecf20Sopenharmony_ci * its OK to resume normal operation. Implementation resembles the 25308c2ecf20Sopenharmony_ci * second-half of the atl1e_resume routine. 25318c2ecf20Sopenharmony_ci */ 25328c2ecf20Sopenharmony_cistatic void atl1e_io_resume(struct pci_dev *pdev) 25338c2ecf20Sopenharmony_ci{ 25348c2ecf20Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 25358c2ecf20Sopenharmony_ci struct atl1e_adapter *adapter = netdev_priv(netdev); 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 25388c2ecf20Sopenharmony_ci if (atl1e_up(adapter)) { 25398c2ecf20Sopenharmony_ci netdev_err(adapter->netdev, 25408c2ecf20Sopenharmony_ci "can't bring device back up after reset\n"); 25418c2ecf20Sopenharmony_ci return; 25428c2ecf20Sopenharmony_ci } 25438c2ecf20Sopenharmony_ci } 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci netif_device_attach(netdev); 25468c2ecf20Sopenharmony_ci} 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_cistatic const struct pci_error_handlers atl1e_err_handler = { 25498c2ecf20Sopenharmony_ci .error_detected = atl1e_io_error_detected, 25508c2ecf20Sopenharmony_ci .slot_reset = atl1e_io_slot_reset, 25518c2ecf20Sopenharmony_ci .resume = atl1e_io_resume, 25528c2ecf20Sopenharmony_ci}; 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_cistatic struct pci_driver atl1e_driver = { 25558c2ecf20Sopenharmony_ci .name = atl1e_driver_name, 25568c2ecf20Sopenharmony_ci .id_table = atl1e_pci_tbl, 25578c2ecf20Sopenharmony_ci .probe = atl1e_probe, 25588c2ecf20Sopenharmony_ci .remove = atl1e_remove, 25598c2ecf20Sopenharmony_ci /* Power Management Hooks */ 25608c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 25618c2ecf20Sopenharmony_ci .suspend = atl1e_suspend, 25628c2ecf20Sopenharmony_ci .resume = atl1e_resume, 25638c2ecf20Sopenharmony_ci#endif 25648c2ecf20Sopenharmony_ci .shutdown = atl1e_shutdown, 25658c2ecf20Sopenharmony_ci .err_handler = &atl1e_err_handler 25668c2ecf20Sopenharmony_ci}; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_cimodule_pci_driver(atl1e_driver); 2569