Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/atheros/atlx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 78 kB image not shown  

Quelle  atl2.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright(c) 2006 - 2007 Atheros Corporation. All rights reserved.
 * Copyright(c) 2007 - 2008 Chris Snook <csnook@redhat.com>
 *
 * Derived from Intel e1000 driver
 * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
 */


#include <linux/atomic.h>
#include <linux/crc32.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/hardirq.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/interrupt.h>
#include <linux/ip.h>
#include <linux/irqflags.h>
#include <linux/irqreturn.h>
#include <linux/mii.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/pm.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/tcp.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/workqueue.h>

#include "atl2.h"

static const char atl2_driver_name[] = "atl2";
static const struct ethtool_ops atl2_ethtool_ops;

MODULE_AUTHOR("Atheros Corporation , Chris Snook ");
MODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver");
MODULE_LICENSE("GPL");

/*
 * atl2_pci_tbl - PCI Device ID Table
 */

static const struct pci_device_id atl2_pci_tbl[] = {
 {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)},
 /* required last entry */
 {0,}
};
MODULE_DEVICE_TABLE(pci, atl2_pci_tbl);

static void atl2_check_options(struct atl2_adapter *adapter);

/**
 * atl2_sw_init - Initialize general software structures (struct atl2_adapter)
 * @adapter: board private structure to initialize
 *
 * atl2_sw_init initializes the Adapter private data structure.
 * Fields are initialized based on PCI device information and
 * OS network device settings (MTU size).
 */

static int atl2_sw_init(struct atl2_adapter *adapter)
{
 struct atl2_hw *hw = &adapter->hw;
 struct pci_dev *pdev = adapter->pdev;

 /* PCI config space info */
 hw->vendor_id = pdev->vendor;
 hw->device_id = pdev->device;
 hw->subsystem_vendor_id = pdev->subsystem_vendor;
 hw->subsystem_id = pdev->subsystem_device;
 hw->revision_id  = pdev->revision;

 pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);

 adapter->wol = 0;
 adapter->ict = 50000;  /* ~100ms */
 adapter->link_speed = SPEED_0;   /* hardware init */
 adapter->link_duplex = FULL_DUPLEX;

 hw->phy_configured = false;
 hw->preamble_len = 7;
 hw->ipgt = 0x60;
 hw->min_ifg = 0x50;
 hw->ipgr1 = 0x40;
 hw->ipgr2 = 0x60;
 hw->retry_buf = 2;
 hw->max_retry = 0xf;
 hw->lcol = 0x37;
 hw->jam_ipg = 7;
 hw->fc_rxd_hi = 0;
 hw->fc_rxd_lo = 0;
 hw->max_frame_size = adapter->netdev->mtu;

 spin_lock_init(&adapter->stats_lock);

 set_bit(__ATL2_DOWN, &adapter->flags);

 return 0;
}

/**
 * atl2_set_multi - Multicast and Promiscuous mode set
 * @netdev: network interface device structure
 *
 * The set_multi entry point is called whenever the multicast address
 * list or the network interface flags are updated.  This routine is
 * responsible for configuring the hardware for proper multicast,
 * promiscuous mode, and all-multi behavior.
 */

static void atl2_set_multi(struct net_device *netdev)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 struct netdev_hw_addr *ha;
 u32 rctl;
 u32 hash_value;

 /* Check for Promiscuous and All Multicast modes */
 rctl = ATL2_READ_REG(hw, REG_MAC_CTRL);

 if (netdev->flags & IFF_PROMISC) {
  rctl |= MAC_CTRL_PROMIS_EN;
 } else if (netdev->flags & IFF_ALLMULTI) {
  rctl |= MAC_CTRL_MC_ALL_EN;
  rctl &= ~MAC_CTRL_PROMIS_EN;
 } else
  rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);

 ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl);

 /* clear the old settings from the multicast hash table */
 ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
 ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);

 /* comoute mc addresses' hash value ,and put it into hash table */
 netdev_for_each_mc_addr(ha, netdev) {
  hash_value = atl2_hash_mc_addr(hw, ha->addr);
  atl2_hash_set(hw, hash_value);
 }
}

static void init_ring_ptrs(struct atl2_adapter *adapter)
{
 /* Read / Write Ptr Initialize: */
 adapter->txd_write_ptr = 0;
 atomic_set(&adapter->txd_read_ptr, 0);

 adapter->rxd_read_ptr = 0;
 adapter->rxd_write_ptr = 0;

 atomic_set(&adapter->txs_write_ptr, 0);
 adapter->txs_next_clear = 0;
}

/**
 * atl2_configure - Configure Transmit&Receive Unit after Reset
 * @adapter: board private structure
 *
 * Configure the Tx /Rx unit of the MAC after a reset.
 */

static int atl2_configure(struct atl2_adapter *adapter)
{
 struct atl2_hw *hw = &adapter->hw;
 u32 value;

 /* clear interrupt status */
 ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff);

 /* set MAC Address */
 value = (((u32)hw->mac_addr[2]) << 24) |
  (((u32)hw->mac_addr[3]) << 16) |
  (((u32)hw->mac_addr[4]) << 8) |
  (((u32)hw->mac_addr[5]));
 ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value);
 value = (((u32)hw->mac_addr[0]) << 8) |
  (((u32)hw->mac_addr[1]));
 ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value);

 /* HI base address */
 ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI,
  (u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32));

 /* LO base address */
 ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO,
  (u32)(adapter->txd_dma & 0x00000000ffffffffULL));
 ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO,
  (u32)(adapter->txs_dma & 0x00000000ffffffffULL));
 ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO,
  (u32)(adapter->rxd_dma & 0x00000000ffffffffULL));

 /* element count */
 ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4));
 ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size);
 ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM,  (u16)adapter->rxd_ring_size);

 /* config Internal SRAM */
/*
    ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end);
    ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end);
*/


 /* config IPG/IFG */
 value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) <<
  MAC_IPG_IFG_IPGT_SHIFT) |
  (((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) <<
  MAC_IPG_IFG_MIFG_SHIFT) |
  (((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) <<
  MAC_IPG_IFG_IPGR1_SHIFT)|
  (((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) <<
  MAC_IPG_IFG_IPGR2_SHIFT);
 ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value);

 /* config  Half-Duplex Control */
 value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) |
  (((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) <<
  MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) |
  MAC_HALF_DUPLX_CTRL_EXC_DEF_EN |
  (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) |
  (((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) <<
  MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT);
 ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value);

 /* set Interrupt Moderator Timer */
 ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt);
 ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN);

 /* set Interrupt Clear Timer */
 ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict);

 /* set MTU */
 ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu +
  ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);

 /* 1590 */
 ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177);

 /* flow control */
 ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi);
 ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo);

 /* Init mailbox */
 ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr);
 ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr);

 /* enable DMA read/write */
 ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN);
 ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN);

 value = ATL2_READ_REG(&adapter->hw, REG_ISR);
 if ((value & ISR_PHY_LINKDOWN) != 0)
  value = 1; /* config failed */
 else
  value = 0;

 /* clear all interrupt status */
 ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff);
 ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0);
 return value;
}

/**
 * atl2_setup_ring_resources - allocate Tx / RX descriptor resources
 * @adapter: board private structure
 *
 * Return 0 on success, negative on failure
 */

static s32 atl2_setup_ring_resources(struct atl2_adapter *adapter)
{
 struct pci_dev *pdev = adapter->pdev;
 int size;
 u8 offset = 0;

 /* real ring DMA buffer */
 adapter->ring_size = size =
  adapter->txd_ring_size * 1 + 7 + /* dword align */
  adapter->txs_ring_size * 4 + 7 + /* dword align */
  adapter->rxd_ring_size * 1536 + 127; /* 128bytes align */

 adapter->ring_vir_addr = dma_alloc_coherent(&pdev->dev, size,
          &adapter->ring_dma, GFP_KERNEL);
 if (!adapter->ring_vir_addr)
  return -ENOMEM;

 /* Init TXD Ring */
 adapter->txd_dma = adapter->ring_dma ;
 offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0;
 adapter->txd_dma += offset;
 adapter->txd_ring = adapter->ring_vir_addr + offset;

 /* Init TXS Ring */
 adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size;
 offset = (adapter->txs_dma & 0x7) ? (8 - (adapter->txs_dma & 0x7)) : 0;
 adapter->txs_dma += offset;
 adapter->txs_ring = (struct tx_pkt_status *)
  (((u8 *)adapter->txd_ring) + (adapter->txd_ring_size + offset));

 /* Init RXD Ring */
 adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size * 4;
 offset = (adapter->rxd_dma & 127) ?
  (128 - (adapter->rxd_dma & 127)) : 0;
 if (offset > 7)
  offset -= 8;
 else
  offset += (128 - 8);

 adapter->rxd_dma += offset;
 adapter->rxd_ring = (struct rx_desc *) (((u8 *)adapter->txs_ring) +
  (adapter->txs_ring_size * 4 + offset));

/*
 * Read / Write Ptr Initialize:
 *      init_ring_ptrs(adapter);
 */

 return 0;
}

/**
 * atl2_irq_enable - Enable default interrupt generation settings
 * @adapter: board private structure
 */

static inline void atl2_irq_enable(struct atl2_adapter *adapter)
{
 ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK);
 ATL2_WRITE_FLUSH(&adapter->hw);
}

/**
 * atl2_irq_disable - Mask off interrupt generation on the NIC
 * @adapter: board private structure
 */

static inline void atl2_irq_disable(struct atl2_adapter *adapter)
{
    ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0);
    ATL2_WRITE_FLUSH(&adapter->hw);
    synchronize_irq(adapter->pdev->irq);
}

static void __atl2_vlan_mode(netdev_features_t features, u32 *ctrl)
{
 if (features & NETIF_F_HW_VLAN_CTAG_RX) {
  /* enable VLAN tag insert/strip */
  *ctrl |= MAC_CTRL_RMV_VLAN;
 } else {
  /* disable VLAN tag insert/strip */
  *ctrl &= ~MAC_CTRL_RMV_VLAN;
 }
}

static void atl2_vlan_mode(struct net_device *netdev,
 netdev_features_t features)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 u32 ctrl;

 atl2_irq_disable(adapter);

 ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
 __atl2_vlan_mode(features, &ctrl);
 ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);

 atl2_irq_enable(adapter);
}

static void atl2_restore_vlan(struct atl2_adapter *adapter)
{
 atl2_vlan_mode(adapter->netdev, adapter->netdev->features);
}

static netdev_features_t atl2_fix_features(struct net_device *netdev,
 netdev_features_t features)
{
 /*
 * Since there is no support for separate rx/tx vlan accel
 * enable/disable make sure tx flag is always in same state as rx.
 */

 if (features & NETIF_F_HW_VLAN_CTAG_RX)
  features |= NETIF_F_HW_VLAN_CTAG_TX;
 else
  features &= ~NETIF_F_HW_VLAN_CTAG_TX;

 return features;
}

static int atl2_set_features(struct net_device *netdev,
 netdev_features_t features)
{
 netdev_features_t changed = netdev->features ^ features;

 if (changed & NETIF_F_HW_VLAN_CTAG_RX)
  atl2_vlan_mode(netdev, features);

 return 0;
}

static void atl2_intr_rx(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 struct rx_desc *rxd;
 struct sk_buff *skb;

 do {
  rxd = adapter->rxd_ring+adapter->rxd_write_ptr;
  if (!rxd->status.update)
   break/* end of tx */

  /* clear this flag at once */
  rxd->status.update = 0;

  if (rxd->status.ok && rxd->status.pkt_size >= 60) {
   int rx_size = (int)(rxd->status.pkt_size - 4);
   /* alloc new buffer */
   skb = netdev_alloc_skb_ip_align(netdev, rx_size);
   if (NULL == skb) {
    /*
 * Check that some rx space is free. If not,
 * free one and mark stats->rx_dropped++.
 */

    netdev->stats.rx_dropped++;
    break;
   }
   memcpy(skb->data, rxd->packet, rx_size);
   skb_put(skb, rx_size);
   skb->protocol = eth_type_trans(skb, netdev);
   if (rxd->status.vlan) {
    u16 vlan_tag = (rxd->status.vtag>>4) |
     ((rxd->status.vtag&7) << 13) |
     ((rxd->status.vtag&8) << 9);

    __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
   }
   netif_rx(skb);
   netdev->stats.rx_bytes += rx_size;
   netdev->stats.rx_packets++;
  } else {
   netdev->stats.rx_errors++;

   if (rxd->status.ok && rxd->status.pkt_size <= 60)
    netdev->stats.rx_length_errors++;
   if (rxd->status.mcast)
    netdev->stats.multicast++;
   if (rxd->status.crc)
    netdev->stats.rx_crc_errors++;
   if (rxd->status.align)
    netdev->stats.rx_frame_errors++;
  }

  /* advance write ptr */
  if (++adapter->rxd_write_ptr == adapter->rxd_ring_size)
   adapter->rxd_write_ptr = 0;
 } while (1);

 /* update mailbox? */
 adapter->rxd_read_ptr = adapter->rxd_write_ptr;
 ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr);
}

static void atl2_intr_tx(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 u32 txd_read_ptr;
 u32 txs_write_ptr;
 struct tx_pkt_status *txs;
 struct tx_pkt_header *txph;
 int free_hole = 0;

 do {
  txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr);
  txs = adapter->txs_ring + txs_write_ptr;
  if (!txs->update)
   break/* tx stop here */

  free_hole = 1;
  txs->update = 0;

  if (++txs_write_ptr == adapter->txs_ring_size)
   txs_write_ptr = 0;
  atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr);

  txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr);
  txph = (struct tx_pkt_header *)
   (((u8 *)adapter->txd_ring) + txd_read_ptr);

  if (txph->pkt_size != txs->pkt_size) {
   struct tx_pkt_status *old_txs = txs;
   printk(KERN_WARNING
    "%s: txs packet size not consistent with txd"
    " txd_:0x%08x, txs_:0x%08x!\n",
    adapter->netdev->name,
    *(u32 *)txph, *(u32 *)txs);
   printk(KERN_WARNING
    "txd read ptr: 0x%x\n",
    txd_read_ptr);
   txs = adapter->txs_ring + txs_write_ptr;
   printk(KERN_WARNING
    "txs-behind:0x%08x\n",
    *(u32 *)txs);
   if (txs_write_ptr < 2) {
    txs = adapter->txs_ring +
     (adapter->txs_ring_size +
     txs_write_ptr - 2);
   } else {
    txs = adapter->txs_ring + (txs_write_ptr - 2);
   }
   printk(KERN_WARNING
    "txs-before:0x%08x\n",
    *(u32 *)txs);
   txs = old_txs;
  }

   /* 4for TPH */
  txd_read_ptr += (((u32)(txph->pkt_size) + 7) & ~3);
  if (txd_read_ptr >= adapter->txd_ring_size)
   txd_read_ptr -= adapter->txd_ring_size;

  atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr);

  /* tx statistics: */
  if (txs->ok) {
   netdev->stats.tx_bytes += txs->pkt_size;
   netdev->stats.tx_packets++;
  }
  else
   netdev->stats.tx_errors++;

  if (txs->defer)
   netdev->stats.collisions++;
  if (txs->abort_col)
   netdev->stats.tx_aborted_errors++;
  if (txs->late_col)
   netdev->stats.tx_window_errors++;
  if (txs->underrun)
   netdev->stats.tx_fifo_errors++;
 } while (1);

 if (free_hole) {
  if (netif_queue_stopped(adapter->netdev) &&
   netif_carrier_ok(adapter->netdev))
   netif_wake_queue(adapter->netdev);
 }
}

static void atl2_check_for_link(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 u16 phy_data = 0;

 spin_lock(&adapter->stats_lock);
 atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
 atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
 spin_unlock(&adapter->stats_lock);

 /* notify upper layer link down ASAP */
 if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */
  if (netif_carrier_ok(netdev)) { /* old link state: Up */
  printk(KERN_INFO "%s: %s NIC Link is Down\n",
   atl2_driver_name, netdev->name);
  adapter->link_speed = SPEED_0;
  netif_carrier_off(netdev);
  netif_stop_queue(netdev);
  }
 }
 schedule_work(&adapter->link_chg_task);
}

static inline void atl2_clear_phy_int(struct atl2_adapter *adapter)
{
 u16 phy_data;
 spin_lock(&adapter->stats_lock);
 atl2_read_phy_reg(&adapter->hw, 19, &phy_data);
 spin_unlock(&adapter->stats_lock);
}

/**
 * atl2_intr - Interrupt Handler
 * @irq: interrupt number
 * @data: pointer to a network interface device structure
 */

static irqreturn_t atl2_intr(int irq, void *data)
{
 struct atl2_adapter *adapter = netdev_priv(data);
 struct atl2_hw *hw = &adapter->hw;
 u32 status;

 status = ATL2_READ_REG(hw, REG_ISR);
 if (0 == status)
  return IRQ_NONE;

 /* link event */
 if (status & ISR_PHY)
  atl2_clear_phy_int(adapter);

 /* clear ISR status, and Enable CMB DMA/Disable Interrupt */
 ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);

 /* check if PCIE PHY Link down */
 if (status & ISR_PHY_LINKDOWN) {
  if (netif_running(adapter->netdev)) { /* reset MAC */
   ATL2_WRITE_REG(hw, REG_ISR, 0);
   ATL2_WRITE_REG(hw, REG_IMR, 0);
   ATL2_WRITE_FLUSH(hw);
   schedule_work(&adapter->reset_task);
   return IRQ_HANDLED;
  }
 }

 /* check if DMA read/write error? */
 if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
  ATL2_WRITE_REG(hw, REG_ISR, 0);
  ATL2_WRITE_REG(hw, REG_IMR, 0);
  ATL2_WRITE_FLUSH(hw);
  schedule_work(&adapter->reset_task);
  return IRQ_HANDLED;
 }

 /* link event */
 if (status & (ISR_PHY | ISR_MANUAL)) {
  adapter->netdev->stats.tx_carrier_errors++;
  atl2_check_for_link(adapter);
 }

 /* transmit event */
 if (status & ISR_TX_EVENT)
  atl2_intr_tx(adapter);

 /* rx exception */
 if (status & ISR_RX_EVENT)
  atl2_intr_rx(adapter);

 /* re-enable Interrupt */
 ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0);
 return IRQ_HANDLED;
}

static int atl2_request_irq(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 int flags, err = 0;

 flags = IRQF_SHARED;
 adapter->have_msi = true;
 err = pci_enable_msi(adapter->pdev);
 if (err)
  adapter->have_msi = false;

 if (adapter->have_msi)
  flags &= ~IRQF_SHARED;

 return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name,
  netdev);
}

/**
 * atl2_free_ring_resources - Free Tx / RX descriptor Resources
 * @adapter: board private structure
 *
 * Free all transmit software resources
 */

static void atl2_free_ring_resources(struct atl2_adapter *adapter)
{
 struct pci_dev *pdev = adapter->pdev;
 dma_free_coherent(&pdev->dev, adapter->ring_size,
     adapter->ring_vir_addr, adapter->ring_dma);
}

/**
 * atl2_open - Called when a network interface is made active
 * @netdev: network interface device structure
 *
 * Returns 0 on success, negative value on failure
 *
 * The open entry point is called when a network interface is made
 * active by the system (IFF_UP).  At this point all resources needed
 * for transmit and receive operations are allocated, the interrupt
 * handler is registered with the OS, the watchdog timer is started,
 * and the stack is notified that the interface is ready.
 */

static int atl2_open(struct net_device *netdev)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 int err;
 u32 val;

 /* disallow open during test */
 if (test_bit(__ATL2_TESTING, &adapter->flags))
  return -EBUSY;

 /* allocate transmit descriptors */
 err = atl2_setup_ring_resources(adapter);
 if (err)
  return err;

 err = atl2_init_hw(&adapter->hw);
 if (err) {
  err = -EIO;
  goto err_init_hw;
 }

 /* hardware has been reset, we need to reload some things */
 atl2_set_multi(netdev);
 init_ring_ptrs(adapter);

 atl2_restore_vlan(adapter);

 if (atl2_configure(adapter)) {
  err = -EIO;
  goto err_config;
 }

 err = atl2_request_irq(adapter);
 if (err)
  goto err_req_irq;

 clear_bit(__ATL2_DOWN, &adapter->flags);

 mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4*HZ));

 val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
 ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL,
  val | MASTER_CTRL_MANUAL_INT);

 atl2_irq_enable(adapter);

 return 0;

err_init_hw:
err_req_irq:
err_config:
 atl2_free_ring_resources(adapter);
 atl2_reset_hw(&adapter->hw);

 return err;
}

static void atl2_down(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;

 /* signal that we're down so the interrupt handler does not
 * reschedule our watchdog timer */

 set_bit(__ATL2_DOWN, &adapter->flags);

 netif_tx_disable(netdev);

 /* reset MAC to disable all RX/TX */
 atl2_reset_hw(&adapter->hw);
 msleep(1);

 atl2_irq_disable(adapter);

 timer_delete_sync(&adapter->watchdog_timer);
 timer_delete_sync(&adapter->phy_config_timer);
 clear_bit(0, &adapter->cfg_phy);

 netif_carrier_off(netdev);
 adapter->link_speed = SPEED_0;
 adapter->link_duplex = -1;
}

static void atl2_free_irq(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;

 free_irq(adapter->pdev->irq, netdev);

#ifdef CONFIG_PCI_MSI
 if (adapter->have_msi)
  pci_disable_msi(adapter->pdev);
#endif
}

/**
 * atl2_close - Disables a network interface
 * @netdev: network interface device structure
 *
 * Returns 0, this is not allowed to fail
 *
 * The close entry point is called when an interface is de-activated
 * by the OS.  The hardware is still under the drivers control, but
 * needs to be disabled.  A global MAC reset is issued to stop the
 * hardware, and all transmit and receive resources are freed.
 */

static int atl2_close(struct net_device *netdev)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);

 WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags));

 atl2_down(adapter);
 atl2_free_irq(adapter);
 atl2_free_ring_resources(adapter);

 return 0;
}

static inline int TxsFreeUnit(struct atl2_adapter *adapter)
{
 u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr);

 return (adapter->txs_next_clear >= txs_write_ptr) ?
  (int) (adapter->txs_ring_size - adapter->txs_next_clear +
  txs_write_ptr - 1) :
  (int) (txs_write_ptr - adapter->txs_next_clear - 1);
}

static inline int TxdFreeBytes(struct atl2_adapter *adapter)
{
 u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr);

 return (adapter->txd_write_ptr >= txd_read_ptr) ?
  (int) (adapter->txd_ring_size - adapter->txd_write_ptr +
  txd_read_ptr - 1) :
  (int) (txd_read_ptr - adapter->txd_write_ptr - 1);
}

static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb,
      struct net_device *netdev)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct tx_pkt_header *txph;
 u32 offset, copy_len;
 int txs_unused;
 int txbuf_unused;

 if (test_bit(__ATL2_DOWN, &adapter->flags)) {
  dev_kfree_skb_any(skb);
  return NETDEV_TX_OK;
 }

 if (unlikely(skb->len <= 0)) {
  dev_kfree_skb_any(skb);
  return NETDEV_TX_OK;
 }

 txs_unused = TxsFreeUnit(adapter);
 txbuf_unused = TxdFreeBytes(adapter);

 if (skb->len + sizeof(struct tx_pkt_header) + 4  > txbuf_unused ||
  txs_unused < 1) {
  /* not enough resources */
  netif_stop_queue(netdev);
  return NETDEV_TX_BUSY;
 }

 offset = adapter->txd_write_ptr;

 txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + offset);

 *(u32 *)txph = 0;
 txph->pkt_size = skb->len;

 offset += 4;
 if (offset >= adapter->txd_ring_size)
  offset -= adapter->txd_ring_size;
 copy_len = adapter->txd_ring_size - offset;
 if (copy_len >= skb->len) {
  memcpy(((u8 *)adapter->txd_ring) + offset, skb->data, skb->len);
  offset += ((u32)(skb->len + 3) & ~3);
 } else {
  memcpy(((u8 *)adapter->txd_ring)+offset, skb->data, copy_len);
  memcpy((u8 *)adapter->txd_ring, skb->data+copy_len,
   skb->len-copy_len);
  offset = ((u32)(skb->len-copy_len + 3) & ~3);
 }
#ifdef NETIF_F_HW_VLAN_CTAG_TX
 if (skb_vlan_tag_present(skb)) {
  u16 vlan_tag = skb_vlan_tag_get(skb);
  vlan_tag = (vlan_tag << 4) |
   (vlan_tag >> 13) |
   ((vlan_tag >> 9) & 0x8);
  txph->ins_vlan = 1;
  txph->vlan = vlan_tag;
 }
#endif
 if (offset >= adapter->txd_ring_size)
  offset -= adapter->txd_ring_size;
 adapter->txd_write_ptr = offset;

 /* clear txs before send */
 adapter->txs_ring[adapter->txs_next_clear].update = 0;
 if (++adapter->txs_next_clear == adapter->txs_ring_size)
  adapter->txs_next_clear = 0;

 ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX,
  (adapter->txd_write_ptr >> 2));

 dev_consume_skb_any(skb);
 return NETDEV_TX_OK;
}

/**
 * atl2_change_mtu - Change the Maximum Transfer Unit
 * @netdev: network interface device structure
 * @new_mtu: new value for maximum frame size
 *
 * Returns 0 on success, negative on failure
 */

static int atl2_change_mtu(struct net_device *netdev, int new_mtu)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;

 /* set MTU */
 WRITE_ONCE(netdev->mtu, new_mtu);
 hw->max_frame_size = new_mtu;
 ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ETH_HLEN +
         VLAN_HLEN + ETH_FCS_LEN);

 return 0;
}

/**
 * atl2_set_mac - Change the Ethernet Address of the NIC
 * @netdev: network interface device structure
 * @p: pointer to an address structure
 *
 * Returns 0 on success, negative on failure
 */

static int atl2_set_mac(struct net_device *netdev, void *p)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct sockaddr *addr = p;

 if (!is_valid_ether_addr(addr->sa_data))
  return -EADDRNOTAVAIL;

 if (netif_running(netdev))
  return -EBUSY;

 eth_hw_addr_set(netdev, addr->sa_data);
 memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);

 atl2_set_mac_addr(&adapter->hw);

 return 0;
}

static int atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct mii_ioctl_data *data = if_mii(ifr);
 unsigned long flags;

 switch (cmd) {
 case SIOCGMIIPHY:
  data->phy_id = 0;
  break;
 case SIOCGMIIREG:
  spin_lock_irqsave(&adapter->stats_lock, flags);
  if (atl2_read_phy_reg(&adapter->hw,
   data->reg_num & 0x1F, &data->val_out)) {
   spin_unlock_irqrestore(&adapter->stats_lock, flags);
   return -EIO;
  }
  spin_unlock_irqrestore(&adapter->stats_lock, flags);
  break;
 case SIOCSMIIREG:
  if (data->reg_num & ~(0x1F))
   return -EFAULT;
  spin_lock_irqsave(&adapter->stats_lock, flags);
  if (atl2_write_phy_reg(&adapter->hw, data->reg_num,
   data->val_in)) {
   spin_unlock_irqrestore(&adapter->stats_lock, flags);
   return -EIO;
  }
  spin_unlock_irqrestore(&adapter->stats_lock, flags);
  break;
 default:
  return -EOPNOTSUPP;
 }
 return 0;
}

static int atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
 switch (cmd) {
 case SIOCGMIIPHY:
 case SIOCGMIIREG:
 case SIOCSMIIREG:
  return atl2_mii_ioctl(netdev, ifr, cmd);
#ifdef ETHTOOL_OPS_COMPAT
 case SIOCETHTOOL:
  return ethtool_ioctl(ifr);
#endif
 default:
  return -EOPNOTSUPP;
 }
}

/**
 * atl2_tx_timeout - Respond to a Tx Hang
 * @netdev: network interface device structure
 * @txqueue: index of the hanging transmit queue
 */

static void atl2_tx_timeout(struct net_device *netdev, unsigned int txqueue)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);

 /* Do the reset outside of interrupt context */
 schedule_work(&adapter->reset_task);
}

/**
 * atl2_watchdog - Timer Call-back
 * @t: timer list containing a pointer to netdev cast into an unsigned long
 */

static void atl2_watchdog(struct timer_list *t)
{
 struct atl2_adapter *adapter = timer_container_of(adapter, t,
         watchdog_timer);

 if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
  u32 drop_rxd, drop_rxs;
  unsigned long flags;

  spin_lock_irqsave(&adapter->stats_lock, flags);
  drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV);
  drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV);
  spin_unlock_irqrestore(&adapter->stats_lock, flags);

  adapter->netdev->stats.rx_over_errors += drop_rxd + drop_rxs;

  /* Reset the timer */
  mod_timer(&adapter->watchdog_timer,
     round_jiffies(jiffies + 4 * HZ));
 }
}

/**
 * atl2_phy_config - Timer Call-back
 * @t: timer list containing a pointer to netdev cast into an unsigned long
 */

static void atl2_phy_config(struct timer_list *t)
{
 struct atl2_adapter *adapter = timer_container_of(adapter, t,
         phy_config_timer);
 struct atl2_hw *hw = &adapter->hw;
 unsigned long flags;

 spin_lock_irqsave(&adapter->stats_lock, flags);
 atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
 atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN |
  MII_CR_RESTART_AUTO_NEG);
 spin_unlock_irqrestore(&adapter->stats_lock, flags);
 clear_bit(0, &adapter->cfg_phy);
}

static int atl2_up(struct atl2_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 int err = 0;
 u32 val;

 /* hardware has been reset, we need to reload some things */

 err = atl2_init_hw(&adapter->hw);
 if (err) {
  err = -EIO;
  return err;
 }

 atl2_set_multi(netdev);
 init_ring_ptrs(adapter);

 atl2_restore_vlan(adapter);

 if (atl2_configure(adapter)) {
  err = -EIO;
  goto err_up;
 }

 clear_bit(__ATL2_DOWN, &adapter->flags);

 val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
 ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val |
  MASTER_CTRL_MANUAL_INT);

 atl2_irq_enable(adapter);

err_up:
 return err;
}

static void atl2_reinit_locked(struct atl2_adapter *adapter)
{
 while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
  msleep(1);
 atl2_down(adapter);
 atl2_up(adapter);
 clear_bit(__ATL2_RESETTING, &adapter->flags);
}

static void atl2_reset_task(struct work_struct *work)
{
 struct atl2_adapter *adapter;
 adapter = container_of(work, struct atl2_adapter, reset_task);

 atl2_reinit_locked(adapter);
}

static void atl2_setup_mac_ctrl(struct atl2_adapter *adapter)
{
 u32 value;
 struct atl2_hw *hw = &adapter->hw;
 struct net_device *netdev = adapter->netdev;

 /* Config MAC CTRL Register */
 value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY;

 /* duplex */
 if (FULL_DUPLEX == adapter->link_duplex)
  value |= MAC_CTRL_DUPLX;

 /* flow control */
 value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);

 /* PAD & CRC */
 value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);

 /* preamble length */
 value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) <<
  MAC_CTRL_PRMLEN_SHIFT);

 /* vlan */
 __atl2_vlan_mode(netdev->features, &value);

 /* filter mode */
 value |= MAC_CTRL_BC_EN;
 if (netdev->flags & IFF_PROMISC)
  value |= MAC_CTRL_PROMIS_EN;
 else if (netdev->flags & IFF_ALLMULTI)
  value |= MAC_CTRL_MC_ALL_EN;

 /* half retry buffer */
 value |= (((u32)(adapter->hw.retry_buf &
  MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT);

 ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
}

static int atl2_check_link(struct atl2_adapter *adapter)
{
 struct atl2_hw *hw = &adapter->hw;
 struct net_device *netdev = adapter->netdev;
 int ret_val;
 u16 speed, duplex, phy_data;
 int reconfig = 0;

 /* MII_BMSR must read twise */
 atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
 atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
 if (!(phy_data&BMSR_LSTATUS)) { /* link down */
  if (netif_carrier_ok(netdev)) { /* old link state: Up */
   u32 value;
   /* disable rx */
   value = ATL2_READ_REG(hw, REG_MAC_CTRL);
   value &= ~MAC_CTRL_RX_EN;
   ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
   adapter->link_speed = SPEED_0;
   netif_carrier_off(netdev);
   netif_stop_queue(netdev);
  }
  return 0;
 }

 /* Link Up */
 ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex);
 if (ret_val)
  return ret_val;
 switch (hw->MediaType) {
 case MEDIA_TYPE_100M_FULL:
  if (speed  != SPEED_100 || duplex != FULL_DUPLEX)
   reconfig = 1;
  break;
 case MEDIA_TYPE_100M_HALF:
  if (speed  != SPEED_100 || duplex != HALF_DUPLEX)
   reconfig = 1;
  break;
 case MEDIA_TYPE_10M_FULL:
  if (speed != SPEED_10 || duplex != FULL_DUPLEX)
   reconfig = 1;
  break;
 case MEDIA_TYPE_10M_HALF:
  if (speed  != SPEED_10 || duplex != HALF_DUPLEX)
   reconfig = 1;
  break;
 }
 /* link result is our setting */
 if (reconfig == 0) {
  if (adapter->link_speed != speed ||
   adapter->link_duplex != duplex) {
   adapter->link_speed = speed;
   adapter->link_duplex = duplex;
   atl2_setup_mac_ctrl(adapter);
   printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n",
    atl2_driver_name, netdev->name,
    adapter->link_speed,
    adapter->link_duplex == FULL_DUPLEX ?
     "Full Duplex" : "Half Duplex");
  }

  if (!netif_carrier_ok(netdev)) { /* Link down -> Up */
   netif_carrier_on(netdev);
   netif_wake_queue(netdev);
  }
  return 0;
 }

 /* change original link status */
 if (netif_carrier_ok(netdev)) {
  u32 value;
  /* disable rx */
  value = ATL2_READ_REG(hw, REG_MAC_CTRL);
  value &= ~MAC_CTRL_RX_EN;
  ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);

  adapter->link_speed = SPEED_0;
  netif_carrier_off(netdev);
  netif_stop_queue(netdev);
 }

 /* auto-neg, insert timer to re-config phy
 * (if interval smaller than 5 seconds, something strange) */

 if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
  if (!test_and_set_bit(0, &adapter->cfg_phy))
   mod_timer(&adapter->phy_config_timer,
      round_jiffies(jiffies + 5 * HZ));
 }

 return 0;
}

/**
 * atl2_link_chg_task - deal with link change event Out of interrupt context
 * @work: pointer to work struct with private info
 */

static void atl2_link_chg_task(struct work_struct *work)
{
 struct atl2_adapter *adapter;
 unsigned long flags;

 adapter = container_of(work, struct atl2_adapter, link_chg_task);

 spin_lock_irqsave(&adapter->stats_lock, flags);
 atl2_check_link(adapter);
 spin_unlock_irqrestore(&adapter->stats_lock, flags);
}

static void atl2_setup_pcicmd(struct pci_dev *pdev)
{
 u16 cmd;

 pci_read_config_word(pdev, PCI_COMMAND, &cmd);

 if (cmd & PCI_COMMAND_INTX_DISABLE)
  cmd &= ~PCI_COMMAND_INTX_DISABLE;
 if (cmd & PCI_COMMAND_IO)
  cmd &= ~PCI_COMMAND_IO;
 if (0 == (cmd & PCI_COMMAND_MEMORY))
  cmd |= PCI_COMMAND_MEMORY;
 if (0 == (cmd & PCI_COMMAND_MASTER))
  cmd |= PCI_COMMAND_MASTER;
 pci_write_config_word(pdev, PCI_COMMAND, cmd);

 /*
 * some motherboards BIOS(PXE/EFI) driver may set PME
 * while they transfer control to OS (Windows/Linux)
 * so we should clear this bit before NIC work normally
 */

 pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0);
}

#ifdef CONFIG_NET_POLL_CONTROLLER
static void atl2_poll_controller(struct net_device *netdev)
{
 disable_irq(netdev->irq);
 atl2_intr(netdev->irq, netdev);
 enable_irq(netdev->irq);
}
#endif


static const struct net_device_ops atl2_netdev_ops = {
 .ndo_open  = atl2_open,
 .ndo_stop  = atl2_close,
 .ndo_start_xmit  = atl2_xmit_frame,
 .ndo_set_rx_mode = atl2_set_multi,
 .ndo_validate_addr = eth_validate_addr,
 .ndo_set_mac_address = atl2_set_mac,
 .ndo_change_mtu  = atl2_change_mtu,
 .ndo_fix_features = atl2_fix_features,
 .ndo_set_features = atl2_set_features,
 .ndo_eth_ioctl  = atl2_ioctl,
 .ndo_tx_timeout  = atl2_tx_timeout,
#ifdef CONFIG_NET_POLL_CONTROLLER
 .ndo_poll_controller = atl2_poll_controller,
#endif
};

/**
 * atl2_probe - Device Initialization Routine
 * @pdev: PCI device information struct
 * @ent: entry in atl2_pci_tbl
 *
 * Returns 0 on success, negative on failure
 *
 * atl2_probe initializes an adapter identified by a pci_dev structure.
 * The OS initialization, configuring of the adapter private structure,
 * and a hardware reset occur.
 */

static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
 struct net_device *netdev;
 struct atl2_adapter *adapter;
 static int cards_found = 0;
 unsigned long mmio_start;
 int mmio_len;
 int err;

 err = pci_enable_device(pdev);
 if (err)
  return err;

 /*
 * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA
 * until the kernel has the proper infrastructure to support 64-bit DMA
 * on these devices.
 */

 if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) &&
     dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) {
  printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n");
  err = -EIO;
  goto err_dma;
 }

 /* Mark all PCI regions associated with PCI device
 * pdev as being reserved by owner atl2_driver_name */

 err = pci_request_regions(pdev, atl2_driver_name);
 if (err)
  goto err_pci_reg;

 /* Enables bus-mastering on the device and calls
 * pcibios_set_master to do the needed arch specific settings */

 pci_set_master(pdev);

 netdev = alloc_etherdev(sizeof(struct atl2_adapter));
 if (!netdev) {
  err = -ENOMEM;
  goto err_alloc_etherdev;
 }

 SET_NETDEV_DEV(netdev, &pdev->dev);

 pci_set_drvdata(pdev, netdev);
 adapter = netdev_priv(netdev);
 adapter->netdev = netdev;
 adapter->pdev = pdev;
 adapter->hw.back = adapter;

 mmio_start = pci_resource_start(pdev, 0x0);
 mmio_len = pci_resource_len(pdev, 0x0);

 adapter->hw.mem_rang = (u32)mmio_len;
 adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);
 if (!adapter->hw.hw_addr) {
  err = -EIO;
  goto err_ioremap;
 }

 atl2_setup_pcicmd(pdev);

 netdev->netdev_ops = &atl2_netdev_ops;
 netdev->ethtool_ops = &atl2_ethtool_ops;
 netdev->watchdog_timeo = 5 * HZ;
 netdev->min_mtu = 40;
 netdev->max_mtu = ETH_DATA_LEN + VLAN_HLEN;
 strscpy(netdev->name, pci_name(pdev), sizeof(netdev->name));

 netdev->mem_start = mmio_start;
 netdev->mem_end = mmio_start + mmio_len;
 adapter->bd_number = cards_found;
 adapter->pci_using_64 = false;

 /* setup the private structure */
 err = atl2_sw_init(adapter);
 if (err)
  goto err_sw_init;

 netdev->hw_features = NETIF_F_HW_VLAN_CTAG_RX;
 netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);

 /* Init PHY as early as possible due to power saving issue  */
 atl2_phy_init(&adapter->hw);

 /* reset the controller to
 * put the device in a known good starting state */


 if (atl2_reset_hw(&adapter->hw)) {
  err = -EIO;
  goto err_reset;
 }

 /* copy the MAC address out of the EEPROM */
 atl2_read_mac_addr(&adapter->hw);
 eth_hw_addr_set(netdev, adapter->hw.mac_addr);
 if (!is_valid_ether_addr(netdev->dev_addr)) {
  err = -EIO;
  goto err_eeprom;
 }

 atl2_check_options(adapter);

 timer_setup(&adapter->watchdog_timer, atl2_watchdog, 0);

 timer_setup(&adapter->phy_config_timer, atl2_phy_config, 0);

 INIT_WORK(&adapter->reset_task, atl2_reset_task);
 INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task);

 strcpy(netdev->name, "eth%d"); /* ?? */
 err = register_netdev(netdev);
 if (err)
  goto err_register;

 /* assume we have no link for now */
 netif_carrier_off(netdev);
 netif_stop_queue(netdev);

 cards_found++;

 return 0;

err_reset:
err_register:
err_sw_init:
err_eeprom:
 iounmap(adapter->hw.hw_addr);
err_ioremap:
 free_netdev(netdev);
err_alloc_etherdev:
 pci_release_regions(pdev);
err_pci_reg:
err_dma:
 pci_disable_device(pdev);
 return err;
}

/**
 * atl2_remove - Device Removal Routine
 * @pdev: PCI device information struct
 *
 * atl2_remove is called by the PCI subsystem to alert the driver
 * that it should release a PCI device.  The could be caused by a
 * Hot-Plug event, or because the driver is going to be removed from
 * memory.
 */

/* FIXME: write the original MAC address back in case it was changed from a
 * BIOS-set value, as in atl1 -- CHS */

static void atl2_remove(struct pci_dev *pdev)
{
 struct net_device *netdev = pci_get_drvdata(pdev);
 struct atl2_adapter *adapter = netdev_priv(netdev);

 /* flush_scheduled work may reschedule our watchdog task, so
 * explicitly disable watchdog tasks from being rescheduled  */

 set_bit(__ATL2_DOWN, &adapter->flags);

 timer_delete_sync(&adapter->watchdog_timer);
 timer_delete_sync(&adapter->phy_config_timer);
 cancel_work_sync(&adapter->reset_task);
 cancel_work_sync(&adapter->link_chg_task);

 unregister_netdev(netdev);

 atl2_force_ps(&adapter->hw);

 iounmap(adapter->hw.hw_addr);
 pci_release_regions(pdev);

 free_netdev(netdev);

 pci_disable_device(pdev);
}

static int atl2_suspend(struct pci_dev *pdev, pm_message_t state)
{
 struct net_device *netdev = pci_get_drvdata(pdev);
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 u16 speed, duplex;
 u32 ctrl = 0;
 u32 wufc = adapter->wol;

#ifdef CONFIG_PM
 int retval = 0;
#endif

 netif_device_detach(netdev);

 if (netif_running(netdev)) {
  WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags));
  atl2_down(adapter);
 }

#ifdef CONFIG_PM
 retval = pci_save_state(pdev);
 if (retval)
  return retval;
#endif

 atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl);
 atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl);
 if (ctrl & BMSR_LSTATUS)
  wufc &= ~ATLX_WUFC_LNKC;

 if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) {
  u32 ret_val;
  /* get current link speed & duplex */
  ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex);
  if (ret_val) {
   printk(KERN_DEBUG
    "%s: get speed&duplex error while suspend\n",
    atl2_driver_name);
   goto wol_dis;
  }

  ctrl = 0;

  /* turn on magic packet wol */
  if (wufc & ATLX_WUFC_MAG)
   ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN);

  /* ignore Link Chg event when Link is up */
  ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);

  /* Config MAC CTRL Register */
  ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY;
  if (FULL_DUPLEX == adapter->link_duplex)
   ctrl |= MAC_CTRL_DUPLX;
  ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
  ctrl |= (((u32)adapter->hw.preamble_len &
   MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
  ctrl |= (((u32)(adapter->hw.retry_buf &
   MAC_CTRL_HALF_LEFT_BUF_MASK)) <<
   MAC_CTRL_HALF_LEFT_BUF_SHIFT);
  if (wufc & ATLX_WUFC_MAG) {
   /* magic packet maybe Broadcast&multicast&Unicast */
   ctrl |= MAC_CTRL_BC_EN;
  }

  ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl);

  /* pcie patch */
  ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
  ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
  ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
  ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
  ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
  ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);

  pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
  goto suspend_exit;
 }

 if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) {
  /* link is down, so only LINK CHG WOL event enable */
  ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
  ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);
  ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0);

  /* pcie patch */
  ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
  ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
  ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
  ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
  ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
  ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);

  hw->phy_configured = false/* re-init PHY when resume */

  pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);

  goto suspend_exit;
 }

wol_dis:
 /* WOL disabled */
 ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0);

 /* pcie patch */
 ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
 ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
 ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
 ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
 ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
 ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);

 atl2_force_ps(hw);
 hw->phy_configured = false/* re-init PHY when resume */

 pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);

suspend_exit:
 if (netif_running(netdev))
  atl2_free_irq(adapter);

 pci_disable_device(pdev);

 pci_set_power_state(pdev, pci_choose_state(pdev, state));

 return 0;
}

#ifdef CONFIG_PM
static int atl2_resume(struct pci_dev *pdev)
{
 struct net_device *netdev = pci_get_drvdata(pdev);
 struct atl2_adapter *adapter = netdev_priv(netdev);
 u32 err;

 pci_set_power_state(pdev, PCI_D0);
 pci_restore_state(pdev);

 err = pci_enable_device(pdev);
 if (err) {
  printk(KERN_ERR
   "atl2: Cannot enable PCI device from suspend\n");
  return err;
 }

 pci_set_master(pdev);

 ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */

 pci_enable_wake(pdev, PCI_D3hot, 0);
 pci_enable_wake(pdev, PCI_D3cold, 0);

 ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);

 if (netif_running(netdev)) {
  err = atl2_request_irq(adapter);
  if (err)
   return err;
 }

 atl2_reset_hw(&adapter->hw);

 if (netif_running(netdev))
  atl2_up(adapter);

 netif_device_attach(netdev);

 return 0;
}
#endif

static void atl2_shutdown(struct pci_dev *pdev)
{
 atl2_suspend(pdev, PMSG_SUSPEND);
}

static struct pci_driver atl2_driver = {
 .name     = atl2_driver_name,
 .id_table = atl2_pci_tbl,
 .probe    = atl2_probe,
 .remove   = atl2_remove,
 /* Power Management Hooks */
 .suspend  = atl2_suspend,
#ifdef CONFIG_PM
 .resume   = atl2_resume,
#endif
 .shutdown = atl2_shutdown,
};

module_pci_driver(atl2_driver);

static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value)
{
 struct atl2_adapter *adapter = hw->back;
 pci_read_config_word(adapter->pdev, reg, value);
}

static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value)
{
 struct atl2_adapter *adapter = hw->back;
 pci_write_config_word(adapter->pdev, reg, *value);
}

static int atl2_get_link_ksettings(struct net_device *netdev,
       struct ethtool_link_ksettings *cmd)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 u32 supported, advertising;

 supported = (SUPPORTED_10baseT_Half |
  SUPPORTED_10baseT_Full |
  SUPPORTED_100baseT_Half |
  SUPPORTED_100baseT_Full |
  SUPPORTED_Autoneg |
  SUPPORTED_TP);
 advertising = ADVERTISED_TP;

 advertising |= ADVERTISED_Autoneg;
 advertising |= hw->autoneg_advertised;

 cmd->base.port = PORT_TP;
 cmd->base.phy_address = 0;

 if (adapter->link_speed != SPEED_0) {
  cmd->base.speed = adapter->link_speed;
  if (adapter->link_duplex == FULL_DUPLEX)
   cmd->base.duplex = DUPLEX_FULL;
  else
   cmd->base.duplex = DUPLEX_HALF;
 } else {
  cmd->base.speed = SPEED_UNKNOWN;
  cmd->base.duplex = DUPLEX_UNKNOWN;
 }

 cmd->base.autoneg = AUTONEG_ENABLE;

 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
      supported);
 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
      advertising);

 return 0;
}

static int atl2_set_link_ksettings(struct net_device *netdev,
       const struct ethtool_link_ksettings *cmd)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 u32 advertising;

 ethtool_convert_link_mode_to_legacy_u32(&advertising,
      cmd->link_modes.advertising);

 while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
  msleep(1);

 if (cmd->base.autoneg == AUTONEG_ENABLE) {
#define MY_ADV_MASK (ADVERTISE_10_HALF | \
    ADVERTISE_10_FULL | \
    ADVERTISE_100_HALF| \
    ADVERTISE_100_FULL)

  if ((advertising & MY_ADV_MASK) == MY_ADV_MASK) {
   hw->MediaType = MEDIA_TYPE_AUTO_SENSOR;
   hw->autoneg_advertised =  MY_ADV_MASK;
  } else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_FULL) {
   hw->MediaType = MEDIA_TYPE_100M_FULL;
   hw->autoneg_advertised = ADVERTISE_100_FULL;
  } else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_HALF) {
   hw->MediaType = MEDIA_TYPE_100M_HALF;
   hw->autoneg_advertised = ADVERTISE_100_HALF;
  } else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_FULL) {
   hw->MediaType = MEDIA_TYPE_10M_FULL;
   hw->autoneg_advertised = ADVERTISE_10_FULL;
  }  else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_HALF) {
   hw->MediaType = MEDIA_TYPE_10M_HALF;
   hw->autoneg_advertised = ADVERTISE_10_HALF;
  } else {
   clear_bit(__ATL2_RESETTING, &adapter->flags);
   return -EINVAL;
  }
  advertising = hw->autoneg_advertised |
   ADVERTISED_TP | ADVERTISED_Autoneg;
 } else {
  clear_bit(__ATL2_RESETTING, &adapter->flags);
  return -EINVAL;
 }

 /* reset the link */
 if (netif_running(adapter->netdev)) {
  atl2_down(adapter);
  atl2_up(adapter);
 } else
  atl2_reset_hw(&adapter->hw);

 clear_bit(__ATL2_RESETTING, &adapter->flags);
 return 0;
}

static u32 atl2_get_msglevel(struct net_device *netdev)
{
 return 0;
}

/*
 * It's sane for this to be empty, but we might want to take advantage of this.
 */

static void atl2_set_msglevel(struct net_device *netdev, u32 data)
{
}

static int atl2_get_regs_len(struct net_device *netdev)
{
#define ATL2_REGS_LEN 42
 return sizeof(u32) * ATL2_REGS_LEN;
}

static void atl2_get_regs(struct net_device *netdev,
 struct ethtool_regs *regs, void *p)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 u32 *regs_buff = p;
 u16 phy_data;

 memset(p, 0, sizeof(u32) * ATL2_REGS_LEN);

 regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;

 regs_buff[0]  = ATL2_READ_REG(hw, REG_VPD_CAP);
 regs_buff[1]  = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
 regs_buff[2]  = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG);
 regs_buff[3]  = ATL2_READ_REG(hw, REG_TWSI_CTRL);
 regs_buff[4]  = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL);
 regs_buff[5]  = ATL2_READ_REG(hw, REG_MASTER_CTRL);
 regs_buff[6]  = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT);
 regs_buff[7]  = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT);
 regs_buff[8]  = ATL2_READ_REG(hw, REG_PHY_ENABLE);
 regs_buff[9]  = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER);
 regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS);
 regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL);
 regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK);
 regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL);
 regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG);
 regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
 regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4);
 regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE);
 regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4);
 regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL);
 regs_buff[20] = ATL2_READ_REG(hw, REG_MTU);
 regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL);
 regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END);
 regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI);
 regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO);
 regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE);
 regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO);
 regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE);
 regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO);
 regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM);
 regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR);
 regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH);
 regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW);
 regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH);
 regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH);
 regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX);
 regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX);
 regs_buff[38] = ATL2_READ_REG(hw, REG_ISR);
 regs_buff[39] = ATL2_READ_REG(hw, REG_IMR);

 atl2_read_phy_reg(hw, MII_BMCR, &phy_data);
 regs_buff[40] = (u32)phy_data;
 atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
 regs_buff[41] = (u32)phy_data;
}

static int atl2_get_eeprom_len(struct net_device *netdev)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);

 if (!atl2_check_eeprom_exist(&adapter->hw))
  return 512;
 else
  return 0;
}

static int atl2_get_eeprom(struct net_device *netdev,
 struct ethtool_eeprom *eeprom, u8 *bytes)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 u32 *eeprom_buff;
 int first_dword, last_dword;
 int ret_val = 0;
 int i;

 if (eeprom->len == 0)
  return -EINVAL;

 if (atl2_check_eeprom_exist(hw))
  return -EINVAL;

 eeprom->magic = hw->vendor_id | (hw->device_id << 16);

 first_dword = eeprom->offset >> 2;
 last_dword = (eeprom->offset + eeprom->len - 1) >> 2;

 eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32),
        GFP_KERNEL);
 if (!eeprom_buff)
  return -ENOMEM;

 for (i = first_dword; i < last_dword; i++) {
  if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) {
   ret_val = -EIO;
   goto free;
  }
 }

 memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
  eeprom->len);
free:
 kfree(eeprom_buff);

 return ret_val;
}

static int atl2_set_eeprom(struct net_device *netdev,
 struct ethtool_eeprom *eeprom, u8 *bytes)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 struct atl2_hw *hw = &adapter->hw;
 u32 *eeprom_buff;
 u32 *ptr;
 int max_len, first_dword, last_dword, ret_val = 0;
 int i;

 if (eeprom->len == 0)
  return -EOPNOTSUPP;

 if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
  return -EFAULT;

 max_len = 512;

 first_dword = eeprom->offset >> 2;
 last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
 eeprom_buff = kmalloc(max_len, GFP_KERNEL);
 if (!eeprom_buff)
  return -ENOMEM;

 ptr = eeprom_buff;

 if (eeprom->offset & 3) {
  /* need read/modify/write of first changed EEPROM word */
  /* only the second byte of the word is being modified */
  if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) {
   ret_val = -EIO;
   goto out;
  }
  ptr++;
 }
 if (((eeprom->offset + eeprom->len) & 3)) {
  /*
 * need read/modify/write of last changed EEPROM word
 * only the first byte of the word is being modified
 */

  if (!atl2_read_eeprom(hw, last_dword * 4,
     &(eeprom_buff[last_dword - first_dword]))) {
   ret_val = -EIO;
   goto out;
  }
 }

 /* Device's eeprom is always little-endian, word addressable */
 memcpy(ptr, bytes, eeprom->len);

 for (i = 0; i < last_dword - first_dword + 1; i++) {
  if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) {
   ret_val = -EIO;
   goto out;
  }
 }
 out:
 kfree(eeprom_buff);
 return ret_val;
}

static void atl2_get_drvinfo(struct net_device *netdev,
 struct ethtool_drvinfo *drvinfo)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);

 strscpy(drvinfo->driver,  atl2_driver_name, sizeof(drvinfo->driver));
 strscpy(drvinfo->fw_version, "L2"sizeof(drvinfo->fw_version));
 strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
  sizeof(drvinfo->bus_info));
}

static void atl2_get_wol(struct net_device *netdev,
 struct ethtool_wolinfo *wol)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);

 wol->supported = WAKE_MAGIC;
 wol->wolopts = 0;

 if (adapter->wol & ATLX_WUFC_EX)
  wol->wolopts |= WAKE_UCAST;
 if (adapter->wol & ATLX_WUFC_MC)
  wol->wolopts |= WAKE_MCAST;
 if (adapter->wol & ATLX_WUFC_BC)
  wol->wolopts |= WAKE_BCAST;
 if (adapter->wol & ATLX_WUFC_MAG)
  wol->wolopts |= WAKE_MAGIC;
 if (adapter->wol & ATLX_WUFC_LNKC)
  wol->wolopts |= WAKE_PHY;
}

static int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);

 if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
  return -EOPNOTSUPP;

 if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
  return -EOPNOTSUPP;

 /* these settings will always override what we currently have */
 adapter->wol = 0;

 if (wol->wolopts & WAKE_MAGIC)
  adapter->wol |= ATLX_WUFC_MAG;
 if (wol->wolopts & WAKE_PHY)
  adapter->wol |= ATLX_WUFC_LNKC;

 return 0;
}

static int atl2_nway_reset(struct net_device *netdev)
{
 struct atl2_adapter *adapter = netdev_priv(netdev);
 if (netif_running(netdev))
  atl2_reinit_locked(adapter);
 return 0;
}

static const struct ethtool_ops atl2_ethtool_ops = {
 .get_drvinfo  = atl2_get_drvinfo,
 .get_regs_len  = atl2_get_regs_len,
 .get_regs  = atl2_get_regs,
 .get_wol  = atl2_get_wol,
 .set_wol  = atl2_set_wol,
 .get_msglevel  = atl2_get_msglevel,
 .set_msglevel  = atl2_set_msglevel,
 .nway_reset  = atl2_nway_reset,
 .get_link  = ethtool_op_get_link,
 .get_eeprom_len  = atl2_get_eeprom_len,
 .get_eeprom  = atl2_get_eeprom,
 .set_eeprom  = atl2_set_eeprom,
 .get_link_ksettings = atl2_get_link_ksettings,
 .set_link_ksettings = atl2_set_link_ksettings,
};

#define LBYTESWAP(a)  ((((a) & 0x00ff00ff) << 8) | \
 (((a) & 0xff00ff00) >> 8))
#define LONGSWAP(a)   ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16))
#define SHORTSWAP(a)  (((a) << 8) | ((a) >> 8))

/*
 * Reset the transmit and receive units; mask and clear all interrupts.
 *
 * hw - Struct containing variables accessed by shared code
 * return : 0  or  idle status (if error)
 */

static s32 atl2_reset_hw(struct atl2_hw *hw)
{
 u32 icr;
 u16 pci_cfg_cmd_word;
 int i;

 /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
 atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word);
 if ((pci_cfg_cmd_word &
  (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) !=
  (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) {
  pci_cfg_cmd_word |=
   (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER);
  atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word);
 }

 /* Clear Interrupt mask to stop board from generating
 * interrupts & Clear any pending interrupt events
 */

 /* FIXME */
 /* ATL2_WRITE_REG(hw, REG_IMR, 0); */
 /* ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); */

 /* Issue Soft Reset to the MAC.  This will reset the chip's
 * transmit, receive, DMA.  It will not effect
 * the current PCI configuration.  The global reset bit is self-
 * clearing, and should clear within a microsecond.
 */

 ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST);
 wmb();
 msleep(1); /* delay about 1ms */

 /* Wait at least 10ms for All module to be Idle */
 for (i = 0; i < 10; i++) {
  icr = ATL2_READ_REG(hw, REG_IDLE_STATUS);
  if (!icr)
   break;
  msleep(1); /* delay 1 ms */
  cpu_relax();
 }

 if (icr)
  return icr;

 return 0;
}

#define CUSTOM_SPI_CS_SETUP        2
#define CUSTOM_SPI_CLK_HI          2
#define CUSTOM_SPI_CLK_LO          2
#define CUSTOM_SPI_CS_HOLD         2
#define CUSTOM_SPI_CS_HI           3

static struct atl2_spi_flash_dev flash_table[] =
{
/* MFR    WRSR  READ  PROGRAM WREN  WRDI  RDSR  RDID  SECTOR_ERASE CHIP_ERASE */
{"Atmel", 0x0,  0x03, 0x02,   0x06, 0x04, 0x05, 0x15, 0x52,        0x62 },
{"SST",   0x01, 0x03, 0x02,   0x06, 0x04, 0x05, 0x90, 0x20,        0x60 },
{"ST",    0x01, 0x03, 0x02,   0x06, 0x04, 0x05, 0xAB, 0xD8,        0xC7 },
};

static bool atl2_spi_read(struct atl2_hw *hw, u32 addr, u32 *buf)
{
 int i;
 u32 value;

 ATL2_WRITE_REG(hw, REG_SPI_DATA, 0);
 ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr);

 value = SPI_FLASH_CTRL_WAIT_READY |
  (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) <<
   SPI_FLASH_CTRL_CS_SETUP_SHIFT |
  (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) <<
   SPI_FLASH_CTRL_CLK_HI_SHIFT |
  (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) <<
   SPI_FLASH_CTRL_CLK_LO_SHIFT |
  (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) <<
   SPI_FLASH_CTRL_CS_HOLD_SHIFT |
  (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) <<
   SPI_FLASH_CTRL_CS_HI_SHIFT |
  (0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT;

 ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);

 value |= SPI_FLASH_CTRL_START;

 ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);

 for (i = 0; i < 10; i++) {
  msleep(1);
  value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
  if (!(value & SPI_FLASH_CTRL_START))
   break;
 }

 if (value & SPI_FLASH_CTRL_START)
  return false;

 *buf = ATL2_READ_REG(hw, REG_SPI_DATA);

 return true;
}

/*
 * get_permanent_address
 * return 0 if get valid mac address,
 */

static int get_permanent_address(struct atl2_hw *hw)
{
 u32 Addr[2];
 u32 i, Control;
 u16 Register;
 u8  EthAddr[ETH_ALEN];
 bool KeyValid;

 if (is_valid_ether_addr(hw->perm_mac_addr))
  return 0;

 Addr[0] = 0;
 Addr[1] = 0;

 if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */
  Register = 0;
  KeyValid = false;

  /* Read out all EEPROM content */
  i = 0;
  while (1) {
   if (atl2_read_eeprom(hw, i + 0x100, &Control)) {
    if (KeyValid) {
     if (Register == REG_MAC_STA_ADDR)
      Addr[0] = Control;
     else if (Register ==
      (REG_MAC_STA_ADDR + 4))
      Addr[1] = Control;
     KeyValid = false;
    } else if ((Control & 0xff) == 0x5A) {
     KeyValid = true;
     Register = (u16) (Control >> 16);
    } else {
   /* assume data end while encount an invalid KEYWORD */
     break;
    }
   } else {
    break/* read error */
   }
   i += 4;
  }

  *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
  *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);

  if (is_valid_ether_addr(EthAddr)) {
   memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
   return 0;
  }
  return 1;
 }

 /* see if SPI flash exists? */
 Addr[0] = 0;
 Addr[1] = 0;
 Register = 0;
 KeyValid = false;
 i = 0;
 while (1) {
  if (atl2_spi_read(hw, i + 0x1f000, &Control)) {
   if (KeyValid) {
    if (Register == REG_MAC_STA_ADDR)
     Addr[0] = Control;
    else if (Register == (REG_MAC_STA_ADDR + 4))
     Addr[1] = Control;
    KeyValid = false;
   } else if ((Control & 0xff) == 0x5A) {
    KeyValid = true;
    Register = (u16) (Control >> 16);
   } else {
    break/* data end */
   }
  } else {
   break/* read error */
  }
  i += 4;
 }

 *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
 *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]);
 if (is_valid_ether_addr(EthAddr)) {
  memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
  return 0;
 }
 /* maybe MAC-address is from BIOS */
 Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
 Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4);
 *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
 *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);

 if (is_valid_ether_addr(EthAddr)) {
  memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
  return 0;
 }

 return 1;
}

/*
 * Reads the adapter's MAC address from the EEPROM
 *
 * hw - Struct containing variables accessed by shared code
 */

static s32 atl2_read_mac_addr(struct atl2_hw *hw)
{
 if (get_permanent_address(hw)) {
  /* for test */
  /* FIXME: shouldn't we use eth_random_addr() here? */
  hw->perm_mac_addr[0] = 0x00;
  hw->perm_mac_addr[1] = 0x13;
  hw->perm_mac_addr[2] = 0x74;
  hw->perm_mac_addr[3] = 0x00;
  hw->perm_mac_addr[4] = 0x5c;
  hw->perm_mac_addr[5] = 0x38;
 }

 memcpy(hw->mac_addr, hw->perm_mac_addr, ETH_ALEN);

 return 0;
}

/*
 * Hashes an address to determine its location in the multicast table
 *
 * hw - Struct containing variables accessed by shared code
 * mc_addr - the multicast address to hash
 *
 * atl2_hash_mc_addr
 *  purpose
 *      set hash value for a multicast address
 *      hash calcu processing :
 *          1. calcu 32bit CRC for multicast address
 *          2. reverse crc with MSB to LSB
 */

static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr)
{
 u32 crc32, value;
 int i;

 value = 0;
 crc32 = ether_crc_le(6, mc_addr);

 for (i = 0; i < 32; i++)
  value |= (((crc32 >> i) & 1) << (31 - i));

 return value;
}

/*
 * Sets the bit in the multicast table corresponding to the hash value.
 *
 * hw - Struct containing variables accessed by shared code
 * hash_value - Multicast address hash value
 */

static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value)
{
 u32 hash_bit, hash_reg;
 u32 mta;

 /* The HASH Table  is a register array of 2 32-bit registers.
 * It is treated like an array of 64 bits.  We want to set
 * bit BitArray[hash_value]. So we figure out what register
 * the bit is in, read it, OR in the new bit, then write
 * back the new value.  The register is determined by the
 * upper 7 bits of the hash value and the bit within that
 * register are determined by the lower 5 bits of the value.
 */

 hash_reg = (hash_value >> 31) & 0x1;
 hash_bit = (hash_value >> 26) & 0x1F;

 mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);

 mta |= (1 << hash_bit);

 ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
}

/*
 * atl2_init_pcie - init PCIE module
 */

static void atl2_init_pcie(struct atl2_hw *hw)
{
    u32 value;
    value = LTSSM_TEST_MODE_DEF;
    ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value);

    value = PCIE_DLL_TX_CTRL1_DEF;
    ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value);
}

static void atl2_init_flash_opcode(struct atl2_hw *hw)
{
 if (hw->flash_vendor >= ARRAY_SIZE(flash_table))
  hw->flash_vendor = 0; /* ATMEL */

 /* Init OP table */
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM,
  flash_table[hw->flash_vendor].cmdPROGRAM);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE,
  flash_table[hw->flash_vendor].cmdSECTOR_ERASE);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE,
  flash_table[hw->flash_vendor].cmdCHIP_ERASE);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID,
  flash_table[hw->flash_vendor].cmdRDID);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN,
  flash_table[hw->flash_vendor].cmdWREN);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR,
  flash_table[hw->flash_vendor].cmdRDSR);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR,
  flash_table[hw->flash_vendor].cmdWRSR);
 ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ,
  flash_table[hw->flash_vendor].cmdREAD);
}

/********************************************************************
* Performs basic configuration of the adapter.
*
* hw - Struct containing variables accessed by shared code
* Assumes that the controller has previously been reset and is in a
* post-reset uninitialized state. Initializes multicast table,
* and  Calls routines to setup link
* Leaves the transmit and receive units disabled and uninitialized.
********************************************************************/

static s32 atl2_init_hw(struct atl2_hw *hw)
{
 u32 ret_val = 0;

 atl2_init_pcie(hw);

 /* Zero out the Multicast HASH table */
 /* clear the old settings from the multicast hash table */
 ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
 ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);

 atl2_init_flash_opcode(hw);

 ret_val = atl2_phy_init(hw);

 return ret_val;
}

/*
 * Detects the current speed and duplex settings of the hardware.
 *
 * hw - Struct containing variables accessed by shared code
 * speed - Speed of the connection
 * duplex - Duplex setting of the connection
 */

static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed,
 u16 *duplex)
{
 s32 ret_val;
 u16 phy_data;

 /* Read PHY Specific Status Register (17) */
 ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data);
 if (ret_val)
  return ret_val;

 if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED))
  return ATLX_ERR_PHY_RES;

 switch (phy_data & MII_ATLX_PSSR_SPEED) {
 case MII_ATLX_PSSR_100MBS:
  *speed = SPEED_100;
  break;
 case MII_ATLX_PSSR_10MBS:
  *speed = SPEED_10;
  break;
 default:
  return ATLX_ERR_PHY_SPEED;
 }

 if (phy_data & MII_ATLX_PSSR_DPLX)
  *duplex = FULL_DUPLEX;
 else
  *duplex = HALF_DUPLEX;

 return 0;
}

/*
 * Reads the value from a PHY register
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=92 G=91

¤ Dauer der Verarbeitung: 0.55 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.