162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2006 - 2007 Atheros Corporation. All rights reserved.
462306a36Sopenharmony_ci * Copyright(c) 2007 - 2008 Chris Snook <csnook@redhat.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Derived from Intel e1000 driver
762306a36Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/atomic.h>
1162306a36Sopenharmony_ci#include <linux/crc32.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/etherdevice.h>
1462306a36Sopenharmony_ci#include <linux/ethtool.h>
1562306a36Sopenharmony_ci#include <linux/hardirq.h>
1662306a36Sopenharmony_ci#include <linux/if_vlan.h>
1762306a36Sopenharmony_ci#include <linux/in.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/ip.h>
2062306a36Sopenharmony_ci#include <linux/irqflags.h>
2162306a36Sopenharmony_ci#include <linux/irqreturn.h>
2262306a36Sopenharmony_ci#include <linux/mii.h>
2362306a36Sopenharmony_ci#include <linux/net.h>
2462306a36Sopenharmony_ci#include <linux/netdevice.h>
2562306a36Sopenharmony_ci#include <linux/pci.h>
2662306a36Sopenharmony_ci#include <linux/pci_ids.h>
2762306a36Sopenharmony_ci#include <linux/pm.h>
2862306a36Sopenharmony_ci#include <linux/skbuff.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/spinlock.h>
3162306a36Sopenharmony_ci#include <linux/string.h>
3262306a36Sopenharmony_ci#include <linux/tcp.h>
3362306a36Sopenharmony_ci#include <linux/timer.h>
3462306a36Sopenharmony_ci#include <linux/types.h>
3562306a36Sopenharmony_ci#include <linux/workqueue.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "atl2.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const char atl2_driver_name[] = "atl2";
4062306a36Sopenharmony_cistatic const struct ethtool_ops atl2_ethtool_ops;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciMODULE_AUTHOR("Atheros Corporation <xiong.huang@atheros.com>, Chris Snook <csnook@redhat.com>");
4362306a36Sopenharmony_ciMODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver");
4462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * atl2_pci_tbl - PCI Device ID Table
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic const struct pci_device_id atl2_pci_tbl[] = {
5062306a36Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)},
5162306a36Sopenharmony_ci	/* required last entry */
5262306a36Sopenharmony_ci	{0,}
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, atl2_pci_tbl);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void atl2_check_options(struct atl2_adapter *adapter);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/**
5962306a36Sopenharmony_ci * atl2_sw_init - Initialize general software structures (struct atl2_adapter)
6062306a36Sopenharmony_ci * @adapter: board private structure to initialize
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * atl2_sw_init initializes the Adapter private data structure.
6362306a36Sopenharmony_ci * Fields are initialized based on PCI device information and
6462306a36Sopenharmony_ci * OS network device settings (MTU size).
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic int atl2_sw_init(struct atl2_adapter *adapter)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
6962306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* PCI config space info */
7262306a36Sopenharmony_ci	hw->vendor_id = pdev->vendor;
7362306a36Sopenharmony_ci	hw->device_id = pdev->device;
7462306a36Sopenharmony_ci	hw->subsystem_vendor_id = pdev->subsystem_vendor;
7562306a36Sopenharmony_ci	hw->subsystem_id = pdev->subsystem_device;
7662306a36Sopenharmony_ci	hw->revision_id  = pdev->revision;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	adapter->wol = 0;
8162306a36Sopenharmony_ci	adapter->ict = 50000;  /* ~100ms */
8262306a36Sopenharmony_ci	adapter->link_speed = SPEED_0;   /* hardware init */
8362306a36Sopenharmony_ci	adapter->link_duplex = FULL_DUPLEX;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	hw->phy_configured = false;
8662306a36Sopenharmony_ci	hw->preamble_len = 7;
8762306a36Sopenharmony_ci	hw->ipgt = 0x60;
8862306a36Sopenharmony_ci	hw->min_ifg = 0x50;
8962306a36Sopenharmony_ci	hw->ipgr1 = 0x40;
9062306a36Sopenharmony_ci	hw->ipgr2 = 0x60;
9162306a36Sopenharmony_ci	hw->retry_buf = 2;
9262306a36Sopenharmony_ci	hw->max_retry = 0xf;
9362306a36Sopenharmony_ci	hw->lcol = 0x37;
9462306a36Sopenharmony_ci	hw->jam_ipg = 7;
9562306a36Sopenharmony_ci	hw->fc_rxd_hi = 0;
9662306a36Sopenharmony_ci	hw->fc_rxd_lo = 0;
9762306a36Sopenharmony_ci	hw->max_frame_size = adapter->netdev->mtu;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	spin_lock_init(&adapter->stats_lock);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	set_bit(__ATL2_DOWN, &adapter->flags);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * atl2_set_multi - Multicast and Promiscuous mode set
10862306a36Sopenharmony_ci * @netdev: network interface device structure
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * The set_multi entry point is called whenever the multicast address
11162306a36Sopenharmony_ci * list or the network interface flags are updated.  This routine is
11262306a36Sopenharmony_ci * responsible for configuring the hardware for proper multicast,
11362306a36Sopenharmony_ci * promiscuous mode, and all-multi behavior.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic void atl2_set_multi(struct net_device *netdev)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
11862306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
11962306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
12062306a36Sopenharmony_ci	u32 rctl;
12162306a36Sopenharmony_ci	u32 hash_value;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Check for Promiscuous and All Multicast modes */
12462306a36Sopenharmony_ci	rctl = ATL2_READ_REG(hw, REG_MAC_CTRL);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC) {
12762306a36Sopenharmony_ci		rctl |= MAC_CTRL_PROMIS_EN;
12862306a36Sopenharmony_ci	} else if (netdev->flags & IFF_ALLMULTI) {
12962306a36Sopenharmony_ci		rctl |= MAC_CTRL_MC_ALL_EN;
13062306a36Sopenharmony_ci		rctl &= ~MAC_CTRL_PROMIS_EN;
13162306a36Sopenharmony_ci	} else
13262306a36Sopenharmony_ci		rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* clear the old settings from the multicast hash table */
13762306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
13862306a36Sopenharmony_ci	ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* comoute mc addresses' hash value ,and put it into hash table */
14162306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, netdev) {
14262306a36Sopenharmony_ci		hash_value = atl2_hash_mc_addr(hw, ha->addr);
14362306a36Sopenharmony_ci		atl2_hash_set(hw, hash_value);
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void init_ring_ptrs(struct atl2_adapter *adapter)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	/* Read / Write Ptr Initialize: */
15062306a36Sopenharmony_ci	adapter->txd_write_ptr = 0;
15162306a36Sopenharmony_ci	atomic_set(&adapter->txd_read_ptr, 0);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	adapter->rxd_read_ptr = 0;
15462306a36Sopenharmony_ci	adapter->rxd_write_ptr = 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	atomic_set(&adapter->txs_write_ptr, 0);
15762306a36Sopenharmony_ci	adapter->txs_next_clear = 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/**
16162306a36Sopenharmony_ci * atl2_configure - Configure Transmit&Receive Unit after Reset
16262306a36Sopenharmony_ci * @adapter: board private structure
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * Configure the Tx /Rx unit of the MAC after a reset.
16562306a36Sopenharmony_ci */
16662306a36Sopenharmony_cistatic int atl2_configure(struct atl2_adapter *adapter)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
16962306a36Sopenharmony_ci	u32 value;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* clear interrupt status */
17262306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* set MAC Address */
17562306a36Sopenharmony_ci	value = (((u32)hw->mac_addr[2]) << 24) |
17662306a36Sopenharmony_ci		(((u32)hw->mac_addr[3]) << 16) |
17762306a36Sopenharmony_ci		(((u32)hw->mac_addr[4]) << 8) |
17862306a36Sopenharmony_ci		(((u32)hw->mac_addr[5]));
17962306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value);
18062306a36Sopenharmony_ci	value = (((u32)hw->mac_addr[0]) << 8) |
18162306a36Sopenharmony_ci		(((u32)hw->mac_addr[1]));
18262306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* HI base address */
18562306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI,
18662306a36Sopenharmony_ci		(u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32));
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* LO base address */
18962306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO,
19062306a36Sopenharmony_ci		(u32)(adapter->txd_dma & 0x00000000ffffffffULL));
19162306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO,
19262306a36Sopenharmony_ci		(u32)(adapter->txs_dma & 0x00000000ffffffffULL));
19362306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO,
19462306a36Sopenharmony_ci		(u32)(adapter->rxd_dma & 0x00000000ffffffffULL));
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* element count */
19762306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4));
19862306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size);
19962306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM,  (u16)adapter->rxd_ring_size);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* config Internal SRAM */
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci    ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end);
20462306a36Sopenharmony_ci    ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end);
20562306a36Sopenharmony_ci*/
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* config IPG/IFG */
20862306a36Sopenharmony_ci	value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) <<
20962306a36Sopenharmony_ci		MAC_IPG_IFG_IPGT_SHIFT) |
21062306a36Sopenharmony_ci		(((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) <<
21162306a36Sopenharmony_ci		MAC_IPG_IFG_MIFG_SHIFT) |
21262306a36Sopenharmony_ci		(((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) <<
21362306a36Sopenharmony_ci		MAC_IPG_IFG_IPGR1_SHIFT)|
21462306a36Sopenharmony_ci		(((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) <<
21562306a36Sopenharmony_ci		MAC_IPG_IFG_IPGR2_SHIFT);
21662306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* config  Half-Duplex Control */
21962306a36Sopenharmony_ci	value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) |
22062306a36Sopenharmony_ci		(((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) <<
22162306a36Sopenharmony_ci		MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) |
22262306a36Sopenharmony_ci		MAC_HALF_DUPLX_CTRL_EXC_DEF_EN |
22362306a36Sopenharmony_ci		(0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) |
22462306a36Sopenharmony_ci		(((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) <<
22562306a36Sopenharmony_ci		MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT);
22662306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* set Interrupt Moderator Timer */
22962306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt);
23062306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* set Interrupt Clear Timer */
23362306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* set MTU */
23662306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu +
23762306a36Sopenharmony_ci		ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* 1590 */
24062306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* flow control */
24362306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi);
24462306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Init mailbox */
24762306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr);
24862306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* enable DMA read/write */
25162306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN);
25262306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	value = ATL2_READ_REG(&adapter->hw, REG_ISR);
25562306a36Sopenharmony_ci	if ((value & ISR_PHY_LINKDOWN) != 0)
25662306a36Sopenharmony_ci		value = 1; /* config failed */
25762306a36Sopenharmony_ci	else
25862306a36Sopenharmony_ci		value = 0;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* clear all interrupt status */
26162306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff);
26262306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0);
26362306a36Sopenharmony_ci	return value;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/**
26762306a36Sopenharmony_ci * atl2_setup_ring_resources - allocate Tx / RX descriptor resources
26862306a36Sopenharmony_ci * @adapter: board private structure
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * Return 0 on success, negative on failure
27162306a36Sopenharmony_ci */
27262306a36Sopenharmony_cistatic s32 atl2_setup_ring_resources(struct atl2_adapter *adapter)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
27562306a36Sopenharmony_ci	int size;
27662306a36Sopenharmony_ci	u8 offset = 0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* real ring DMA buffer */
27962306a36Sopenharmony_ci	adapter->ring_size = size =
28062306a36Sopenharmony_ci		adapter->txd_ring_size * 1 + 7 +	/* dword align */
28162306a36Sopenharmony_ci		adapter->txs_ring_size * 4 + 7 +	/* dword align */
28262306a36Sopenharmony_ci		adapter->rxd_ring_size * 1536 + 127;	/* 128bytes align */
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	adapter->ring_vir_addr = dma_alloc_coherent(&pdev->dev, size,
28562306a36Sopenharmony_ci						    &adapter->ring_dma, GFP_KERNEL);
28662306a36Sopenharmony_ci	if (!adapter->ring_vir_addr)
28762306a36Sopenharmony_ci		return -ENOMEM;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Init TXD Ring */
29062306a36Sopenharmony_ci	adapter->txd_dma = adapter->ring_dma ;
29162306a36Sopenharmony_ci	offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0;
29262306a36Sopenharmony_ci	adapter->txd_dma += offset;
29362306a36Sopenharmony_ci	adapter->txd_ring = adapter->ring_vir_addr + offset;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* Init TXS Ring */
29662306a36Sopenharmony_ci	adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size;
29762306a36Sopenharmony_ci	offset = (adapter->txs_dma & 0x7) ? (8 - (adapter->txs_dma & 0x7)) : 0;
29862306a36Sopenharmony_ci	adapter->txs_dma += offset;
29962306a36Sopenharmony_ci	adapter->txs_ring = (struct tx_pkt_status *)
30062306a36Sopenharmony_ci		(((u8 *)adapter->txd_ring) + (adapter->txd_ring_size + offset));
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Init RXD Ring */
30362306a36Sopenharmony_ci	adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size * 4;
30462306a36Sopenharmony_ci	offset = (adapter->rxd_dma & 127) ?
30562306a36Sopenharmony_ci		(128 - (adapter->rxd_dma & 127)) : 0;
30662306a36Sopenharmony_ci	if (offset > 7)
30762306a36Sopenharmony_ci		offset -= 8;
30862306a36Sopenharmony_ci	else
30962306a36Sopenharmony_ci		offset += (128 - 8);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	adapter->rxd_dma += offset;
31262306a36Sopenharmony_ci	adapter->rxd_ring = (struct rx_desc *) (((u8 *)adapter->txs_ring) +
31362306a36Sopenharmony_ci		(adapter->txs_ring_size * 4 + offset));
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci * Read / Write Ptr Initialize:
31762306a36Sopenharmony_ci *      init_ring_ptrs(adapter);
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_ci	return 0;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/**
32362306a36Sopenharmony_ci * atl2_irq_enable - Enable default interrupt generation settings
32462306a36Sopenharmony_ci * @adapter: board private structure
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_cistatic inline void atl2_irq_enable(struct atl2_adapter *adapter)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK);
32962306a36Sopenharmony_ci	ATL2_WRITE_FLUSH(&adapter->hw);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/**
33362306a36Sopenharmony_ci * atl2_irq_disable - Mask off interrupt generation on the NIC
33462306a36Sopenharmony_ci * @adapter: board private structure
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_cistatic inline void atl2_irq_disable(struct atl2_adapter *adapter)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci    ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0);
33962306a36Sopenharmony_ci    ATL2_WRITE_FLUSH(&adapter->hw);
34062306a36Sopenharmony_ci    synchronize_irq(adapter->pdev->irq);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void __atl2_vlan_mode(netdev_features_t features, u32 *ctrl)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX) {
34662306a36Sopenharmony_ci		/* enable VLAN tag insert/strip */
34762306a36Sopenharmony_ci		*ctrl |= MAC_CTRL_RMV_VLAN;
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		/* disable VLAN tag insert/strip */
35062306a36Sopenharmony_ci		*ctrl &= ~MAC_CTRL_RMV_VLAN;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void atl2_vlan_mode(struct net_device *netdev,
35562306a36Sopenharmony_ci	netdev_features_t features)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
35862306a36Sopenharmony_ci	u32 ctrl;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	atl2_irq_disable(adapter);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
36362306a36Sopenharmony_ci	__atl2_vlan_mode(features, &ctrl);
36462306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	atl2_irq_enable(adapter);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void atl2_restore_vlan(struct atl2_adapter *adapter)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	atl2_vlan_mode(adapter->netdev, adapter->netdev->features);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic netdev_features_t atl2_fix_features(struct net_device *netdev,
37562306a36Sopenharmony_ci	netdev_features_t features)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	/*
37862306a36Sopenharmony_ci	 * Since there is no support for separate rx/tx vlan accel
37962306a36Sopenharmony_ci	 * enable/disable make sure tx flag is always in same state as rx.
38062306a36Sopenharmony_ci	 */
38162306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
38262306a36Sopenharmony_ci		features |= NETIF_F_HW_VLAN_CTAG_TX;
38362306a36Sopenharmony_ci	else
38462306a36Sopenharmony_ci		features &= ~NETIF_F_HW_VLAN_CTAG_TX;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return features;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int atl2_set_features(struct net_device *netdev,
39062306a36Sopenharmony_ci	netdev_features_t features)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	netdev_features_t changed = netdev->features ^ features;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
39562306a36Sopenharmony_ci		atl2_vlan_mode(netdev, features);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void atl2_intr_rx(struct atl2_adapter *adapter)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
40362306a36Sopenharmony_ci	struct rx_desc *rxd;
40462306a36Sopenharmony_ci	struct sk_buff *skb;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	do {
40762306a36Sopenharmony_ci		rxd = adapter->rxd_ring+adapter->rxd_write_ptr;
40862306a36Sopenharmony_ci		if (!rxd->status.update)
40962306a36Sopenharmony_ci			break; /* end of tx */
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		/* clear this flag at once */
41262306a36Sopenharmony_ci		rxd->status.update = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		if (rxd->status.ok && rxd->status.pkt_size >= 60) {
41562306a36Sopenharmony_ci			int rx_size = (int)(rxd->status.pkt_size - 4);
41662306a36Sopenharmony_ci			/* alloc new buffer */
41762306a36Sopenharmony_ci			skb = netdev_alloc_skb_ip_align(netdev, rx_size);
41862306a36Sopenharmony_ci			if (NULL == skb) {
41962306a36Sopenharmony_ci				/*
42062306a36Sopenharmony_ci				 * Check that some rx space is free. If not,
42162306a36Sopenharmony_ci				 * free one and mark stats->rx_dropped++.
42262306a36Sopenharmony_ci				 */
42362306a36Sopenharmony_ci				netdev->stats.rx_dropped++;
42462306a36Sopenharmony_ci				break;
42562306a36Sopenharmony_ci			}
42662306a36Sopenharmony_ci			memcpy(skb->data, rxd->packet, rx_size);
42762306a36Sopenharmony_ci			skb_put(skb, rx_size);
42862306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, netdev);
42962306a36Sopenharmony_ci			if (rxd->status.vlan) {
43062306a36Sopenharmony_ci				u16 vlan_tag = (rxd->status.vtag>>4) |
43162306a36Sopenharmony_ci					((rxd->status.vtag&7) << 13) |
43262306a36Sopenharmony_ci					((rxd->status.vtag&8) << 9);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci				__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
43562306a36Sopenharmony_ci			}
43662306a36Sopenharmony_ci			netif_rx(skb);
43762306a36Sopenharmony_ci			netdev->stats.rx_bytes += rx_size;
43862306a36Sopenharmony_ci			netdev->stats.rx_packets++;
43962306a36Sopenharmony_ci		} else {
44062306a36Sopenharmony_ci			netdev->stats.rx_errors++;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci			if (rxd->status.ok && rxd->status.pkt_size <= 60)
44362306a36Sopenharmony_ci				netdev->stats.rx_length_errors++;
44462306a36Sopenharmony_ci			if (rxd->status.mcast)
44562306a36Sopenharmony_ci				netdev->stats.multicast++;
44662306a36Sopenharmony_ci			if (rxd->status.crc)
44762306a36Sopenharmony_ci				netdev->stats.rx_crc_errors++;
44862306a36Sopenharmony_ci			if (rxd->status.align)
44962306a36Sopenharmony_ci				netdev->stats.rx_frame_errors++;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		/* advance write ptr */
45362306a36Sopenharmony_ci		if (++adapter->rxd_write_ptr == adapter->rxd_ring_size)
45462306a36Sopenharmony_ci			adapter->rxd_write_ptr = 0;
45562306a36Sopenharmony_ci	} while (1);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* update mailbox? */
45862306a36Sopenharmony_ci	adapter->rxd_read_ptr = adapter->rxd_write_ptr;
45962306a36Sopenharmony_ci	ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void atl2_intr_tx(struct atl2_adapter *adapter)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
46562306a36Sopenharmony_ci	u32 txd_read_ptr;
46662306a36Sopenharmony_ci	u32 txs_write_ptr;
46762306a36Sopenharmony_ci	struct tx_pkt_status *txs;
46862306a36Sopenharmony_ci	struct tx_pkt_header *txph;
46962306a36Sopenharmony_ci	int free_hole = 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	do {
47262306a36Sopenharmony_ci		txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr);
47362306a36Sopenharmony_ci		txs = adapter->txs_ring + txs_write_ptr;
47462306a36Sopenharmony_ci		if (!txs->update)
47562306a36Sopenharmony_ci			break; /* tx stop here */
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		free_hole = 1;
47862306a36Sopenharmony_ci		txs->update = 0;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		if (++txs_write_ptr == adapter->txs_ring_size)
48162306a36Sopenharmony_ci			txs_write_ptr = 0;
48262306a36Sopenharmony_ci		atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr);
48562306a36Sopenharmony_ci		txph = (struct tx_pkt_header *)
48662306a36Sopenharmony_ci			(((u8 *)adapter->txd_ring) + txd_read_ptr);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		if (txph->pkt_size != txs->pkt_size) {
48962306a36Sopenharmony_ci			struct tx_pkt_status *old_txs = txs;
49062306a36Sopenharmony_ci			printk(KERN_WARNING
49162306a36Sopenharmony_ci				"%s: txs packet size not consistent with txd"
49262306a36Sopenharmony_ci				" txd_:0x%08x, txs_:0x%08x!\n",
49362306a36Sopenharmony_ci				adapter->netdev->name,
49462306a36Sopenharmony_ci				*(u32 *)txph, *(u32 *)txs);
49562306a36Sopenharmony_ci			printk(KERN_WARNING
49662306a36Sopenharmony_ci				"txd read ptr: 0x%x\n",
49762306a36Sopenharmony_ci				txd_read_ptr);
49862306a36Sopenharmony_ci			txs = adapter->txs_ring + txs_write_ptr;
49962306a36Sopenharmony_ci			printk(KERN_WARNING
50062306a36Sopenharmony_ci				"txs-behind:0x%08x\n",
50162306a36Sopenharmony_ci				*(u32 *)txs);
50262306a36Sopenharmony_ci			if (txs_write_ptr < 2) {
50362306a36Sopenharmony_ci				txs = adapter->txs_ring +
50462306a36Sopenharmony_ci					(adapter->txs_ring_size +
50562306a36Sopenharmony_ci					txs_write_ptr - 2);
50662306a36Sopenharmony_ci			} else {
50762306a36Sopenharmony_ci				txs = adapter->txs_ring + (txs_write_ptr - 2);
50862306a36Sopenharmony_ci			}
50962306a36Sopenharmony_ci			printk(KERN_WARNING
51062306a36Sopenharmony_ci				"txs-before:0x%08x\n",
51162306a36Sopenharmony_ci				*(u32 *)txs);
51262306a36Sopenharmony_ci			txs = old_txs;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		 /* 4for TPH */
51662306a36Sopenharmony_ci		txd_read_ptr += (((u32)(txph->pkt_size) + 7) & ~3);
51762306a36Sopenharmony_ci		if (txd_read_ptr >= adapter->txd_ring_size)
51862306a36Sopenharmony_ci			txd_read_ptr -= adapter->txd_ring_size;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		/* tx statistics: */
52362306a36Sopenharmony_ci		if (txs->ok) {
52462306a36Sopenharmony_ci			netdev->stats.tx_bytes += txs->pkt_size;
52562306a36Sopenharmony_ci			netdev->stats.tx_packets++;
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci		else
52862306a36Sopenharmony_ci			netdev->stats.tx_errors++;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		if (txs->defer)
53162306a36Sopenharmony_ci			netdev->stats.collisions++;
53262306a36Sopenharmony_ci		if (txs->abort_col)
53362306a36Sopenharmony_ci			netdev->stats.tx_aborted_errors++;
53462306a36Sopenharmony_ci		if (txs->late_col)
53562306a36Sopenharmony_ci			netdev->stats.tx_window_errors++;
53662306a36Sopenharmony_ci		if (txs->underrun)
53762306a36Sopenharmony_ci			netdev->stats.tx_fifo_errors++;
53862306a36Sopenharmony_ci	} while (1);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (free_hole) {
54162306a36Sopenharmony_ci		if (netif_queue_stopped(adapter->netdev) &&
54262306a36Sopenharmony_ci			netif_carrier_ok(adapter->netdev))
54362306a36Sopenharmony_ci			netif_wake_queue(adapter->netdev);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void atl2_check_for_link(struct atl2_adapter *adapter)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
55062306a36Sopenharmony_ci	u16 phy_data = 0;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	spin_lock(&adapter->stats_lock);
55362306a36Sopenharmony_ci	atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
55462306a36Sopenharmony_ci	atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
55562306a36Sopenharmony_ci	spin_unlock(&adapter->stats_lock);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* notify upper layer link down ASAP */
55862306a36Sopenharmony_ci	if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */
55962306a36Sopenharmony_ci		if (netif_carrier_ok(netdev)) { /* old link state: Up */
56062306a36Sopenharmony_ci		printk(KERN_INFO "%s: %s NIC Link is Down\n",
56162306a36Sopenharmony_ci			atl2_driver_name, netdev->name);
56262306a36Sopenharmony_ci		adapter->link_speed = SPEED_0;
56362306a36Sopenharmony_ci		netif_carrier_off(netdev);
56462306a36Sopenharmony_ci		netif_stop_queue(netdev);
56562306a36Sopenharmony_ci		}
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci	schedule_work(&adapter->link_chg_task);
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic inline void atl2_clear_phy_int(struct atl2_adapter *adapter)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	u16 phy_data;
57362306a36Sopenharmony_ci	spin_lock(&adapter->stats_lock);
57462306a36Sopenharmony_ci	atl2_read_phy_reg(&adapter->hw, 19, &phy_data);
57562306a36Sopenharmony_ci	spin_unlock(&adapter->stats_lock);
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/**
57962306a36Sopenharmony_ci * atl2_intr - Interrupt Handler
58062306a36Sopenharmony_ci * @irq: interrupt number
58162306a36Sopenharmony_ci * @data: pointer to a network interface device structure
58262306a36Sopenharmony_ci */
58362306a36Sopenharmony_cistatic irqreturn_t atl2_intr(int irq, void *data)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(data);
58662306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
58762306a36Sopenharmony_ci	u32 status;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	status = ATL2_READ_REG(hw, REG_ISR);
59062306a36Sopenharmony_ci	if (0 == status)
59162306a36Sopenharmony_ci		return IRQ_NONE;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* link event */
59462306a36Sopenharmony_ci	if (status & ISR_PHY)
59562306a36Sopenharmony_ci		atl2_clear_phy_int(adapter);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* clear ISR status, and Enable CMB DMA/Disable Interrupt */
59862306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* check if PCIE PHY Link down */
60162306a36Sopenharmony_ci	if (status & ISR_PHY_LINKDOWN) {
60262306a36Sopenharmony_ci		if (netif_running(adapter->netdev)) { /* reset MAC */
60362306a36Sopenharmony_ci			ATL2_WRITE_REG(hw, REG_ISR, 0);
60462306a36Sopenharmony_ci			ATL2_WRITE_REG(hw, REG_IMR, 0);
60562306a36Sopenharmony_ci			ATL2_WRITE_FLUSH(hw);
60662306a36Sopenharmony_ci			schedule_work(&adapter->reset_task);
60762306a36Sopenharmony_ci			return IRQ_HANDLED;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* check if DMA read/write error? */
61262306a36Sopenharmony_ci	if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
61362306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_ISR, 0);
61462306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_IMR, 0);
61562306a36Sopenharmony_ci		ATL2_WRITE_FLUSH(hw);
61662306a36Sopenharmony_ci		schedule_work(&adapter->reset_task);
61762306a36Sopenharmony_ci		return IRQ_HANDLED;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* link event */
62162306a36Sopenharmony_ci	if (status & (ISR_PHY | ISR_MANUAL)) {
62262306a36Sopenharmony_ci		adapter->netdev->stats.tx_carrier_errors++;
62362306a36Sopenharmony_ci		atl2_check_for_link(adapter);
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* transmit event */
62762306a36Sopenharmony_ci	if (status & ISR_TX_EVENT)
62862306a36Sopenharmony_ci		atl2_intr_tx(adapter);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* rx exception */
63162306a36Sopenharmony_ci	if (status & ISR_RX_EVENT)
63262306a36Sopenharmony_ci		atl2_intr_rx(adapter);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* re-enable Interrupt */
63562306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0);
63662306a36Sopenharmony_ci	return IRQ_HANDLED;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic int atl2_request_irq(struct atl2_adapter *adapter)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
64262306a36Sopenharmony_ci	int flags, err = 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	flags = IRQF_SHARED;
64562306a36Sopenharmony_ci	adapter->have_msi = true;
64662306a36Sopenharmony_ci	err = pci_enable_msi(adapter->pdev);
64762306a36Sopenharmony_ci	if (err)
64862306a36Sopenharmony_ci		adapter->have_msi = false;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (adapter->have_msi)
65162306a36Sopenharmony_ci		flags &= ~IRQF_SHARED;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name,
65462306a36Sopenharmony_ci		netdev);
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci/**
65862306a36Sopenharmony_ci * atl2_free_ring_resources - Free Tx / RX descriptor Resources
65962306a36Sopenharmony_ci * @adapter: board private structure
66062306a36Sopenharmony_ci *
66162306a36Sopenharmony_ci * Free all transmit software resources
66262306a36Sopenharmony_ci */
66362306a36Sopenharmony_cistatic void atl2_free_ring_resources(struct atl2_adapter *adapter)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
66662306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, adapter->ring_size,
66762306a36Sopenharmony_ci			  adapter->ring_vir_addr, adapter->ring_dma);
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/**
67162306a36Sopenharmony_ci * atl2_open - Called when a network interface is made active
67262306a36Sopenharmony_ci * @netdev: network interface device structure
67362306a36Sopenharmony_ci *
67462306a36Sopenharmony_ci * Returns 0 on success, negative value on failure
67562306a36Sopenharmony_ci *
67662306a36Sopenharmony_ci * The open entry point is called when a network interface is made
67762306a36Sopenharmony_ci * active by the system (IFF_UP).  At this point all resources needed
67862306a36Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt
67962306a36Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started,
68062306a36Sopenharmony_ci * and the stack is notified that the interface is ready.
68162306a36Sopenharmony_ci */
68262306a36Sopenharmony_cistatic int atl2_open(struct net_device *netdev)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
68562306a36Sopenharmony_ci	int err;
68662306a36Sopenharmony_ci	u32 val;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* disallow open during test */
68962306a36Sopenharmony_ci	if (test_bit(__ATL2_TESTING, &adapter->flags))
69062306a36Sopenharmony_ci		return -EBUSY;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* allocate transmit descriptors */
69362306a36Sopenharmony_ci	err = atl2_setup_ring_resources(adapter);
69462306a36Sopenharmony_ci	if (err)
69562306a36Sopenharmony_ci		return err;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	err = atl2_init_hw(&adapter->hw);
69862306a36Sopenharmony_ci	if (err) {
69962306a36Sopenharmony_ci		err = -EIO;
70062306a36Sopenharmony_ci		goto err_init_hw;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* hardware has been reset, we need to reload some things */
70462306a36Sopenharmony_ci	atl2_set_multi(netdev);
70562306a36Sopenharmony_ci	init_ring_ptrs(adapter);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	atl2_restore_vlan(adapter);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (atl2_configure(adapter)) {
71062306a36Sopenharmony_ci		err = -EIO;
71162306a36Sopenharmony_ci		goto err_config;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	err = atl2_request_irq(adapter);
71562306a36Sopenharmony_ci	if (err)
71662306a36Sopenharmony_ci		goto err_req_irq;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	clear_bit(__ATL2_DOWN, &adapter->flags);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4*HZ));
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
72362306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL,
72462306a36Sopenharmony_ci		val | MASTER_CTRL_MANUAL_INT);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	atl2_irq_enable(adapter);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return 0;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cierr_init_hw:
73162306a36Sopenharmony_cierr_req_irq:
73262306a36Sopenharmony_cierr_config:
73362306a36Sopenharmony_ci	atl2_free_ring_resources(adapter);
73462306a36Sopenharmony_ci	atl2_reset_hw(&adapter->hw);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return err;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic void atl2_down(struct atl2_adapter *adapter)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* signal that we're down so the interrupt handler does not
74462306a36Sopenharmony_ci	 * reschedule our watchdog timer */
74562306a36Sopenharmony_ci	set_bit(__ATL2_DOWN, &adapter->flags);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	netif_tx_disable(netdev);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	/* reset MAC to disable all RX/TX */
75062306a36Sopenharmony_ci	atl2_reset_hw(&adapter->hw);
75162306a36Sopenharmony_ci	msleep(1);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	atl2_irq_disable(adapter);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	del_timer_sync(&adapter->watchdog_timer);
75662306a36Sopenharmony_ci	del_timer_sync(&adapter->phy_config_timer);
75762306a36Sopenharmony_ci	clear_bit(0, &adapter->cfg_phy);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	netif_carrier_off(netdev);
76062306a36Sopenharmony_ci	adapter->link_speed = SPEED_0;
76162306a36Sopenharmony_ci	adapter->link_duplex = -1;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic void atl2_free_irq(struct atl2_adapter *adapter)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	free_irq(adapter->pdev->irq, netdev);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
77162306a36Sopenharmony_ci	if (adapter->have_msi)
77262306a36Sopenharmony_ci		pci_disable_msi(adapter->pdev);
77362306a36Sopenharmony_ci#endif
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci/**
77762306a36Sopenharmony_ci * atl2_close - Disables a network interface
77862306a36Sopenharmony_ci * @netdev: network interface device structure
77962306a36Sopenharmony_ci *
78062306a36Sopenharmony_ci * Returns 0, this is not allowed to fail
78162306a36Sopenharmony_ci *
78262306a36Sopenharmony_ci * The close entry point is called when an interface is de-activated
78362306a36Sopenharmony_ci * by the OS.  The hardware is still under the drivers control, but
78462306a36Sopenharmony_ci * needs to be disabled.  A global MAC reset is issued to stop the
78562306a36Sopenharmony_ci * hardware, and all transmit and receive resources are freed.
78662306a36Sopenharmony_ci */
78762306a36Sopenharmony_cistatic int atl2_close(struct net_device *netdev)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags));
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	atl2_down(adapter);
79462306a36Sopenharmony_ci	atl2_free_irq(adapter);
79562306a36Sopenharmony_ci	atl2_free_ring_resources(adapter);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return 0;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic inline int TxsFreeUnit(struct atl2_adapter *adapter)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return (adapter->txs_next_clear >= txs_write_ptr) ?
80562306a36Sopenharmony_ci		(int) (adapter->txs_ring_size - adapter->txs_next_clear +
80662306a36Sopenharmony_ci		txs_write_ptr - 1) :
80762306a36Sopenharmony_ci		(int) (txs_write_ptr - adapter->txs_next_clear - 1);
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic inline int TxdFreeBytes(struct atl2_adapter *adapter)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return (adapter->txd_write_ptr >= txd_read_ptr) ?
81562306a36Sopenharmony_ci		(int) (adapter->txd_ring_size - adapter->txd_write_ptr +
81662306a36Sopenharmony_ci		txd_read_ptr - 1) :
81762306a36Sopenharmony_ci		(int) (txd_read_ptr - adapter->txd_write_ptr - 1);
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic netdev_tx_t atl2_xmit_frame(struct sk_buff *skb,
82162306a36Sopenharmony_ci					 struct net_device *netdev)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
82462306a36Sopenharmony_ci	struct tx_pkt_header *txph;
82562306a36Sopenharmony_ci	u32 offset, copy_len;
82662306a36Sopenharmony_ci	int txs_unused;
82762306a36Sopenharmony_ci	int txbuf_unused;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	if (test_bit(__ATL2_DOWN, &adapter->flags)) {
83062306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
83162306a36Sopenharmony_ci		return NETDEV_TX_OK;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if (unlikely(skb->len <= 0)) {
83562306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
83662306a36Sopenharmony_ci		return NETDEV_TX_OK;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	txs_unused = TxsFreeUnit(adapter);
84062306a36Sopenharmony_ci	txbuf_unused = TxdFreeBytes(adapter);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (skb->len + sizeof(struct tx_pkt_header) + 4  > txbuf_unused ||
84362306a36Sopenharmony_ci		txs_unused < 1) {
84462306a36Sopenharmony_ci		/* not enough resources */
84562306a36Sopenharmony_ci		netif_stop_queue(netdev);
84662306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	offset = adapter->txd_write_ptr;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + offset);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	*(u32 *)txph = 0;
85462306a36Sopenharmony_ci	txph->pkt_size = skb->len;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	offset += 4;
85762306a36Sopenharmony_ci	if (offset >= adapter->txd_ring_size)
85862306a36Sopenharmony_ci		offset -= adapter->txd_ring_size;
85962306a36Sopenharmony_ci	copy_len = adapter->txd_ring_size - offset;
86062306a36Sopenharmony_ci	if (copy_len >= skb->len) {
86162306a36Sopenharmony_ci		memcpy(((u8 *)adapter->txd_ring) + offset, skb->data, skb->len);
86262306a36Sopenharmony_ci		offset += ((u32)(skb->len + 3) & ~3);
86362306a36Sopenharmony_ci	} else {
86462306a36Sopenharmony_ci		memcpy(((u8 *)adapter->txd_ring)+offset, skb->data, copy_len);
86562306a36Sopenharmony_ci		memcpy((u8 *)adapter->txd_ring, skb->data+copy_len,
86662306a36Sopenharmony_ci			skb->len-copy_len);
86762306a36Sopenharmony_ci		offset = ((u32)(skb->len-copy_len + 3) & ~3);
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci#ifdef NETIF_F_HW_VLAN_CTAG_TX
87062306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
87162306a36Sopenharmony_ci		u16 vlan_tag = skb_vlan_tag_get(skb);
87262306a36Sopenharmony_ci		vlan_tag = (vlan_tag << 4) |
87362306a36Sopenharmony_ci			(vlan_tag >> 13) |
87462306a36Sopenharmony_ci			((vlan_tag >> 9) & 0x8);
87562306a36Sopenharmony_ci		txph->ins_vlan = 1;
87662306a36Sopenharmony_ci		txph->vlan = vlan_tag;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci#endif
87962306a36Sopenharmony_ci	if (offset >= adapter->txd_ring_size)
88062306a36Sopenharmony_ci		offset -= adapter->txd_ring_size;
88162306a36Sopenharmony_ci	adapter->txd_write_ptr = offset;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* clear txs before send */
88462306a36Sopenharmony_ci	adapter->txs_ring[adapter->txs_next_clear].update = 0;
88562306a36Sopenharmony_ci	if (++adapter->txs_next_clear == adapter->txs_ring_size)
88662306a36Sopenharmony_ci		adapter->txs_next_clear = 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX,
88962306a36Sopenharmony_ci		(adapter->txd_write_ptr >> 2));
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	dev_consume_skb_any(skb);
89262306a36Sopenharmony_ci	return NETDEV_TX_OK;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/**
89662306a36Sopenharmony_ci * atl2_change_mtu - Change the Maximum Transfer Unit
89762306a36Sopenharmony_ci * @netdev: network interface device structure
89862306a36Sopenharmony_ci * @new_mtu: new value for maximum frame size
89962306a36Sopenharmony_ci *
90062306a36Sopenharmony_ci * Returns 0 on success, negative on failure
90162306a36Sopenharmony_ci */
90262306a36Sopenharmony_cistatic int atl2_change_mtu(struct net_device *netdev, int new_mtu)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
90562306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* set MTU */
90862306a36Sopenharmony_ci	netdev->mtu = new_mtu;
90962306a36Sopenharmony_ci	hw->max_frame_size = new_mtu;
91062306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ETH_HLEN +
91162306a36Sopenharmony_ci		       VLAN_HLEN + ETH_FCS_LEN);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	return 0;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/**
91762306a36Sopenharmony_ci * atl2_set_mac - Change the Ethernet Address of the NIC
91862306a36Sopenharmony_ci * @netdev: network interface device structure
91962306a36Sopenharmony_ci * @p: pointer to an address structure
92062306a36Sopenharmony_ci *
92162306a36Sopenharmony_ci * Returns 0 on success, negative on failure
92262306a36Sopenharmony_ci */
92362306a36Sopenharmony_cistatic int atl2_set_mac(struct net_device *netdev, void *p)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
92662306a36Sopenharmony_ci	struct sockaddr *addr = p;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
92962306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (netif_running(netdev))
93262306a36Sopenharmony_ci		return -EBUSY;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	eth_hw_addr_set(netdev, addr->sa_data);
93562306a36Sopenharmony_ci	memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	atl2_set_mac_addr(&adapter->hw);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	return 0;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic int atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
94562306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(ifr);
94662306a36Sopenharmony_ci	unsigned long flags;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	switch (cmd) {
94962306a36Sopenharmony_ci	case SIOCGMIIPHY:
95062306a36Sopenharmony_ci		data->phy_id = 0;
95162306a36Sopenharmony_ci		break;
95262306a36Sopenharmony_ci	case SIOCGMIIREG:
95362306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->stats_lock, flags);
95462306a36Sopenharmony_ci		if (atl2_read_phy_reg(&adapter->hw,
95562306a36Sopenharmony_ci			data->reg_num & 0x1F, &data->val_out)) {
95662306a36Sopenharmony_ci			spin_unlock_irqrestore(&adapter->stats_lock, flags);
95762306a36Sopenharmony_ci			return -EIO;
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->stats_lock, flags);
96062306a36Sopenharmony_ci		break;
96162306a36Sopenharmony_ci	case SIOCSMIIREG:
96262306a36Sopenharmony_ci		if (data->reg_num & ~(0x1F))
96362306a36Sopenharmony_ci			return -EFAULT;
96462306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->stats_lock, flags);
96562306a36Sopenharmony_ci		if (atl2_write_phy_reg(&adapter->hw, data->reg_num,
96662306a36Sopenharmony_ci			data->val_in)) {
96762306a36Sopenharmony_ci			spin_unlock_irqrestore(&adapter->stats_lock, flags);
96862306a36Sopenharmony_ci			return -EIO;
96962306a36Sopenharmony_ci		}
97062306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->stats_lock, flags);
97162306a36Sopenharmony_ci		break;
97262306a36Sopenharmony_ci	default:
97362306a36Sopenharmony_ci		return -EOPNOTSUPP;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci	return 0;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	switch (cmd) {
98162306a36Sopenharmony_ci	case SIOCGMIIPHY:
98262306a36Sopenharmony_ci	case SIOCGMIIREG:
98362306a36Sopenharmony_ci	case SIOCSMIIREG:
98462306a36Sopenharmony_ci		return atl2_mii_ioctl(netdev, ifr, cmd);
98562306a36Sopenharmony_ci#ifdef ETHTOOL_OPS_COMPAT
98662306a36Sopenharmony_ci	case SIOCETHTOOL:
98762306a36Sopenharmony_ci		return ethtool_ioctl(ifr);
98862306a36Sopenharmony_ci#endif
98962306a36Sopenharmony_ci	default:
99062306a36Sopenharmony_ci		return -EOPNOTSUPP;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci/**
99562306a36Sopenharmony_ci * atl2_tx_timeout - Respond to a Tx Hang
99662306a36Sopenharmony_ci * @netdev: network interface device structure
99762306a36Sopenharmony_ci * @txqueue: index of the hanging transmit queue
99862306a36Sopenharmony_ci */
99962306a36Sopenharmony_cistatic void atl2_tx_timeout(struct net_device *netdev, unsigned int txqueue)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	/* Do the reset outside of interrupt context */
100462306a36Sopenharmony_ci	schedule_work(&adapter->reset_task);
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/**
100862306a36Sopenharmony_ci * atl2_watchdog - Timer Call-back
100962306a36Sopenharmony_ci * @t: timer list containing a pointer to netdev cast into an unsigned long
101062306a36Sopenharmony_ci */
101162306a36Sopenharmony_cistatic void atl2_watchdog(struct timer_list *t)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct atl2_adapter *adapter = from_timer(adapter, t, watchdog_timer);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
101662306a36Sopenharmony_ci		u32 drop_rxd, drop_rxs;
101762306a36Sopenharmony_ci		unsigned long flags;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->stats_lock, flags);
102062306a36Sopenharmony_ci		drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV);
102162306a36Sopenharmony_ci		drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV);
102262306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->stats_lock, flags);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		adapter->netdev->stats.rx_over_errors += drop_rxd + drop_rxs;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		/* Reset the timer */
102762306a36Sopenharmony_ci		mod_timer(&adapter->watchdog_timer,
102862306a36Sopenharmony_ci			  round_jiffies(jiffies + 4 * HZ));
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci/**
103362306a36Sopenharmony_ci * atl2_phy_config - Timer Call-back
103462306a36Sopenharmony_ci * @t: timer list containing a pointer to netdev cast into an unsigned long
103562306a36Sopenharmony_ci */
103662306a36Sopenharmony_cistatic void atl2_phy_config(struct timer_list *t)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	struct atl2_adapter *adapter = from_timer(adapter, t,
103962306a36Sopenharmony_ci						  phy_config_timer);
104062306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
104162306a36Sopenharmony_ci	unsigned long flags;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->stats_lock, flags);
104462306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
104562306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN |
104662306a36Sopenharmony_ci		MII_CR_RESTART_AUTO_NEG);
104762306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->stats_lock, flags);
104862306a36Sopenharmony_ci	clear_bit(0, &adapter->cfg_phy);
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic int atl2_up(struct atl2_adapter *adapter)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
105462306a36Sopenharmony_ci	int err = 0;
105562306a36Sopenharmony_ci	u32 val;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* hardware has been reset, we need to reload some things */
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	err = atl2_init_hw(&adapter->hw);
106062306a36Sopenharmony_ci	if (err) {
106162306a36Sopenharmony_ci		err = -EIO;
106262306a36Sopenharmony_ci		return err;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	atl2_set_multi(netdev);
106662306a36Sopenharmony_ci	init_ring_ptrs(adapter);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	atl2_restore_vlan(adapter);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (atl2_configure(adapter)) {
107162306a36Sopenharmony_ci		err = -EIO;
107262306a36Sopenharmony_ci		goto err_up;
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	clear_bit(__ATL2_DOWN, &adapter->flags);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
107862306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val |
107962306a36Sopenharmony_ci		MASTER_CTRL_MANUAL_INT);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	atl2_irq_enable(adapter);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cierr_up:
108462306a36Sopenharmony_ci	return err;
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic void atl2_reinit_locked(struct atl2_adapter *adapter)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
109062306a36Sopenharmony_ci		msleep(1);
109162306a36Sopenharmony_ci	atl2_down(adapter);
109262306a36Sopenharmony_ci	atl2_up(adapter);
109362306a36Sopenharmony_ci	clear_bit(__ATL2_RESETTING, &adapter->flags);
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic void atl2_reset_task(struct work_struct *work)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	struct atl2_adapter *adapter;
109962306a36Sopenharmony_ci	adapter = container_of(work, struct atl2_adapter, reset_task);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	atl2_reinit_locked(adapter);
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic void atl2_setup_mac_ctrl(struct atl2_adapter *adapter)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	u32 value;
110762306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
110862306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* Config MAC CTRL Register */
111162306a36Sopenharmony_ci	value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* duplex */
111462306a36Sopenharmony_ci	if (FULL_DUPLEX == adapter->link_duplex)
111562306a36Sopenharmony_ci		value |= MAC_CTRL_DUPLX;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	/* flow control */
111862306a36Sopenharmony_ci	value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* PAD & CRC */
112162306a36Sopenharmony_ci	value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/* preamble length */
112462306a36Sopenharmony_ci	value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) <<
112562306a36Sopenharmony_ci		MAC_CTRL_PRMLEN_SHIFT);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	/* vlan */
112862306a36Sopenharmony_ci	__atl2_vlan_mode(netdev->features, &value);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* filter mode */
113162306a36Sopenharmony_ci	value |= MAC_CTRL_BC_EN;
113262306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC)
113362306a36Sopenharmony_ci		value |= MAC_CTRL_PROMIS_EN;
113462306a36Sopenharmony_ci	else if (netdev->flags & IFF_ALLMULTI)
113562306a36Sopenharmony_ci		value |= MAC_CTRL_MC_ALL_EN;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/* half retry buffer */
113862306a36Sopenharmony_ci	value |= (((u32)(adapter->hw.retry_buf &
113962306a36Sopenharmony_ci		MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic int atl2_check_link(struct atl2_adapter *adapter)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
114762306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
114862306a36Sopenharmony_ci	int ret_val;
114962306a36Sopenharmony_ci	u16 speed, duplex, phy_data;
115062306a36Sopenharmony_ci	int reconfig = 0;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	/* MII_BMSR must read twise */
115362306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
115462306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
115562306a36Sopenharmony_ci	if (!(phy_data&BMSR_LSTATUS)) { /* link down */
115662306a36Sopenharmony_ci		if (netif_carrier_ok(netdev)) { /* old link state: Up */
115762306a36Sopenharmony_ci			u32 value;
115862306a36Sopenharmony_ci			/* disable rx */
115962306a36Sopenharmony_ci			value = ATL2_READ_REG(hw, REG_MAC_CTRL);
116062306a36Sopenharmony_ci			value &= ~MAC_CTRL_RX_EN;
116162306a36Sopenharmony_ci			ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
116262306a36Sopenharmony_ci			adapter->link_speed = SPEED_0;
116362306a36Sopenharmony_ci			netif_carrier_off(netdev);
116462306a36Sopenharmony_ci			netif_stop_queue(netdev);
116562306a36Sopenharmony_ci		}
116662306a36Sopenharmony_ci		return 0;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	/* Link Up */
117062306a36Sopenharmony_ci	ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex);
117162306a36Sopenharmony_ci	if (ret_val)
117262306a36Sopenharmony_ci		return ret_val;
117362306a36Sopenharmony_ci	switch (hw->MediaType) {
117462306a36Sopenharmony_ci	case MEDIA_TYPE_100M_FULL:
117562306a36Sopenharmony_ci		if (speed  != SPEED_100 || duplex != FULL_DUPLEX)
117662306a36Sopenharmony_ci			reconfig = 1;
117762306a36Sopenharmony_ci		break;
117862306a36Sopenharmony_ci	case MEDIA_TYPE_100M_HALF:
117962306a36Sopenharmony_ci		if (speed  != SPEED_100 || duplex != HALF_DUPLEX)
118062306a36Sopenharmony_ci			reconfig = 1;
118162306a36Sopenharmony_ci		break;
118262306a36Sopenharmony_ci	case MEDIA_TYPE_10M_FULL:
118362306a36Sopenharmony_ci		if (speed != SPEED_10 || duplex != FULL_DUPLEX)
118462306a36Sopenharmony_ci			reconfig = 1;
118562306a36Sopenharmony_ci		break;
118662306a36Sopenharmony_ci	case MEDIA_TYPE_10M_HALF:
118762306a36Sopenharmony_ci		if (speed  != SPEED_10 || duplex != HALF_DUPLEX)
118862306a36Sopenharmony_ci			reconfig = 1;
118962306a36Sopenharmony_ci		break;
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci	/* link result is our setting */
119262306a36Sopenharmony_ci	if (reconfig == 0) {
119362306a36Sopenharmony_ci		if (adapter->link_speed != speed ||
119462306a36Sopenharmony_ci			adapter->link_duplex != duplex) {
119562306a36Sopenharmony_ci			adapter->link_speed = speed;
119662306a36Sopenharmony_ci			adapter->link_duplex = duplex;
119762306a36Sopenharmony_ci			atl2_setup_mac_ctrl(adapter);
119862306a36Sopenharmony_ci			printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n",
119962306a36Sopenharmony_ci				atl2_driver_name, netdev->name,
120062306a36Sopenharmony_ci				adapter->link_speed,
120162306a36Sopenharmony_ci				adapter->link_duplex == FULL_DUPLEX ?
120262306a36Sopenharmony_ci					"Full Duplex" : "Half Duplex");
120362306a36Sopenharmony_ci		}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		if (!netif_carrier_ok(netdev)) { /* Link down -> Up */
120662306a36Sopenharmony_ci			netif_carrier_on(netdev);
120762306a36Sopenharmony_ci			netif_wake_queue(netdev);
120862306a36Sopenharmony_ci		}
120962306a36Sopenharmony_ci		return 0;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/* change original link status */
121362306a36Sopenharmony_ci	if (netif_carrier_ok(netdev)) {
121462306a36Sopenharmony_ci		u32 value;
121562306a36Sopenharmony_ci		/* disable rx */
121662306a36Sopenharmony_ci		value = ATL2_READ_REG(hw, REG_MAC_CTRL);
121762306a36Sopenharmony_ci		value &= ~MAC_CTRL_RX_EN;
121862306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		adapter->link_speed = SPEED_0;
122162306a36Sopenharmony_ci		netif_carrier_off(netdev);
122262306a36Sopenharmony_ci		netif_stop_queue(netdev);
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* auto-neg, insert timer to re-config phy
122662306a36Sopenharmony_ci	 * (if interval smaller than 5 seconds, something strange) */
122762306a36Sopenharmony_ci	if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
122862306a36Sopenharmony_ci		if (!test_and_set_bit(0, &adapter->cfg_phy))
122962306a36Sopenharmony_ci			mod_timer(&adapter->phy_config_timer,
123062306a36Sopenharmony_ci				  round_jiffies(jiffies + 5 * HZ));
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	return 0;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/**
123762306a36Sopenharmony_ci * atl2_link_chg_task - deal with link change event Out of interrupt context
123862306a36Sopenharmony_ci * @work: pointer to work struct with private info
123962306a36Sopenharmony_ci */
124062306a36Sopenharmony_cistatic void atl2_link_chg_task(struct work_struct *work)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct atl2_adapter *adapter;
124362306a36Sopenharmony_ci	unsigned long flags;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	adapter = container_of(work, struct atl2_adapter, link_chg_task);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->stats_lock, flags);
124862306a36Sopenharmony_ci	atl2_check_link(adapter);
124962306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->stats_lock, flags);
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic void atl2_setup_pcicmd(struct pci_dev *pdev)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	u16 cmd;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	if (cmd & PCI_COMMAND_INTX_DISABLE)
125962306a36Sopenharmony_ci		cmd &= ~PCI_COMMAND_INTX_DISABLE;
126062306a36Sopenharmony_ci	if (cmd & PCI_COMMAND_IO)
126162306a36Sopenharmony_ci		cmd &= ~PCI_COMMAND_IO;
126262306a36Sopenharmony_ci	if (0 == (cmd & PCI_COMMAND_MEMORY))
126362306a36Sopenharmony_ci		cmd |= PCI_COMMAND_MEMORY;
126462306a36Sopenharmony_ci	if (0 == (cmd & PCI_COMMAND_MASTER))
126562306a36Sopenharmony_ci		cmd |= PCI_COMMAND_MASTER;
126662306a36Sopenharmony_ci	pci_write_config_word(pdev, PCI_COMMAND, cmd);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	/*
126962306a36Sopenharmony_ci	 * some motherboards BIOS(PXE/EFI) driver may set PME
127062306a36Sopenharmony_ci	 * while they transfer control to OS (Windows/Linux)
127162306a36Sopenharmony_ci	 * so we should clear this bit before NIC work normally
127262306a36Sopenharmony_ci	 */
127362306a36Sopenharmony_ci	pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0);
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
127762306a36Sopenharmony_cistatic void atl2_poll_controller(struct net_device *netdev)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	disable_irq(netdev->irq);
128062306a36Sopenharmony_ci	atl2_intr(netdev->irq, netdev);
128162306a36Sopenharmony_ci	enable_irq(netdev->irq);
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci#endif
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic const struct net_device_ops atl2_netdev_ops = {
128762306a36Sopenharmony_ci	.ndo_open		= atl2_open,
128862306a36Sopenharmony_ci	.ndo_stop		= atl2_close,
128962306a36Sopenharmony_ci	.ndo_start_xmit		= atl2_xmit_frame,
129062306a36Sopenharmony_ci	.ndo_set_rx_mode	= atl2_set_multi,
129162306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
129262306a36Sopenharmony_ci	.ndo_set_mac_address	= atl2_set_mac,
129362306a36Sopenharmony_ci	.ndo_change_mtu		= atl2_change_mtu,
129462306a36Sopenharmony_ci	.ndo_fix_features	= atl2_fix_features,
129562306a36Sopenharmony_ci	.ndo_set_features	= atl2_set_features,
129662306a36Sopenharmony_ci	.ndo_eth_ioctl		= atl2_ioctl,
129762306a36Sopenharmony_ci	.ndo_tx_timeout		= atl2_tx_timeout,
129862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
129962306a36Sopenharmony_ci	.ndo_poll_controller	= atl2_poll_controller,
130062306a36Sopenharmony_ci#endif
130162306a36Sopenharmony_ci};
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci/**
130462306a36Sopenharmony_ci * atl2_probe - Device Initialization Routine
130562306a36Sopenharmony_ci * @pdev: PCI device information struct
130662306a36Sopenharmony_ci * @ent: entry in atl2_pci_tbl
130762306a36Sopenharmony_ci *
130862306a36Sopenharmony_ci * Returns 0 on success, negative on failure
130962306a36Sopenharmony_ci *
131062306a36Sopenharmony_ci * atl2_probe initializes an adapter identified by a pci_dev structure.
131162306a36Sopenharmony_ci * The OS initialization, configuring of the adapter private structure,
131262306a36Sopenharmony_ci * and a hardware reset occur.
131362306a36Sopenharmony_ci */
131462306a36Sopenharmony_cistatic int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	struct net_device *netdev;
131762306a36Sopenharmony_ci	struct atl2_adapter *adapter;
131862306a36Sopenharmony_ci	static int cards_found = 0;
131962306a36Sopenharmony_ci	unsigned long mmio_start;
132062306a36Sopenharmony_ci	int mmio_len;
132162306a36Sopenharmony_ci	int err;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	err = pci_enable_device(pdev);
132462306a36Sopenharmony_ci	if (err)
132562306a36Sopenharmony_ci		return err;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	/*
132862306a36Sopenharmony_ci	 * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA
132962306a36Sopenharmony_ci	 * until the kernel has the proper infrastructure to support 64-bit DMA
133062306a36Sopenharmony_ci	 * on these devices.
133162306a36Sopenharmony_ci	 */
133262306a36Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) &&
133362306a36Sopenharmony_ci	    dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) {
133462306a36Sopenharmony_ci		printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n");
133562306a36Sopenharmony_ci		err = -EIO;
133662306a36Sopenharmony_ci		goto err_dma;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/* Mark all PCI regions associated with PCI device
134062306a36Sopenharmony_ci	 * pdev as being reserved by owner atl2_driver_name */
134162306a36Sopenharmony_ci	err = pci_request_regions(pdev, atl2_driver_name);
134262306a36Sopenharmony_ci	if (err)
134362306a36Sopenharmony_ci		goto err_pci_reg;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* Enables bus-mastering on the device and calls
134662306a36Sopenharmony_ci	 * pcibios_set_master to do the needed arch specific settings */
134762306a36Sopenharmony_ci	pci_set_master(pdev);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct atl2_adapter));
135062306a36Sopenharmony_ci	if (!netdev) {
135162306a36Sopenharmony_ci		err = -ENOMEM;
135262306a36Sopenharmony_ci		goto err_alloc_etherdev;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	pci_set_drvdata(pdev, netdev);
135862306a36Sopenharmony_ci	adapter = netdev_priv(netdev);
135962306a36Sopenharmony_ci	adapter->netdev = netdev;
136062306a36Sopenharmony_ci	adapter->pdev = pdev;
136162306a36Sopenharmony_ci	adapter->hw.back = adapter;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	mmio_start = pci_resource_start(pdev, 0x0);
136462306a36Sopenharmony_ci	mmio_len = pci_resource_len(pdev, 0x0);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	adapter->hw.mem_rang = (u32)mmio_len;
136762306a36Sopenharmony_ci	adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);
136862306a36Sopenharmony_ci	if (!adapter->hw.hw_addr) {
136962306a36Sopenharmony_ci		err = -EIO;
137062306a36Sopenharmony_ci		goto err_ioremap;
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	atl2_setup_pcicmd(pdev);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	netdev->netdev_ops = &atl2_netdev_ops;
137662306a36Sopenharmony_ci	netdev->ethtool_ops = &atl2_ethtool_ops;
137762306a36Sopenharmony_ci	netdev->watchdog_timeo = 5 * HZ;
137862306a36Sopenharmony_ci	netdev->min_mtu = 40;
137962306a36Sopenharmony_ci	netdev->max_mtu = ETH_DATA_LEN + VLAN_HLEN;
138062306a36Sopenharmony_ci	strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	netdev->mem_start = mmio_start;
138362306a36Sopenharmony_ci	netdev->mem_end = mmio_start + mmio_len;
138462306a36Sopenharmony_ci	adapter->bd_number = cards_found;
138562306a36Sopenharmony_ci	adapter->pci_using_64 = false;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	/* setup the private structure */
138862306a36Sopenharmony_ci	err = atl2_sw_init(adapter);
138962306a36Sopenharmony_ci	if (err)
139062306a36Sopenharmony_ci		goto err_sw_init;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	netdev->hw_features = NETIF_F_HW_VLAN_CTAG_RX;
139362306a36Sopenharmony_ci	netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	/* Init PHY as early as possible due to power saving issue  */
139662306a36Sopenharmony_ci	atl2_phy_init(&adapter->hw);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/* reset the controller to
139962306a36Sopenharmony_ci	 * put the device in a known good starting state */
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (atl2_reset_hw(&adapter->hw)) {
140262306a36Sopenharmony_ci		err = -EIO;
140362306a36Sopenharmony_ci		goto err_reset;
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	/* copy the MAC address out of the EEPROM */
140762306a36Sopenharmony_ci	atl2_read_mac_addr(&adapter->hw);
140862306a36Sopenharmony_ci	eth_hw_addr_set(netdev, adapter->hw.mac_addr);
140962306a36Sopenharmony_ci	if (!is_valid_ether_addr(netdev->dev_addr)) {
141062306a36Sopenharmony_ci		err = -EIO;
141162306a36Sopenharmony_ci		goto err_eeprom;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	atl2_check_options(adapter);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	timer_setup(&adapter->watchdog_timer, atl2_watchdog, 0);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	timer_setup(&adapter->phy_config_timer, atl2_phy_config, 0);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	INIT_WORK(&adapter->reset_task, atl2_reset_task);
142162306a36Sopenharmony_ci	INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	strcpy(netdev->name, "eth%d"); /* ?? */
142462306a36Sopenharmony_ci	err = register_netdev(netdev);
142562306a36Sopenharmony_ci	if (err)
142662306a36Sopenharmony_ci		goto err_register;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	/* assume we have no link for now */
142962306a36Sopenharmony_ci	netif_carrier_off(netdev);
143062306a36Sopenharmony_ci	netif_stop_queue(netdev);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	cards_found++;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	return 0;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cierr_reset:
143762306a36Sopenharmony_cierr_register:
143862306a36Sopenharmony_cierr_sw_init:
143962306a36Sopenharmony_cierr_eeprom:
144062306a36Sopenharmony_ci	iounmap(adapter->hw.hw_addr);
144162306a36Sopenharmony_cierr_ioremap:
144262306a36Sopenharmony_ci	free_netdev(netdev);
144362306a36Sopenharmony_cierr_alloc_etherdev:
144462306a36Sopenharmony_ci	pci_release_regions(pdev);
144562306a36Sopenharmony_cierr_pci_reg:
144662306a36Sopenharmony_cierr_dma:
144762306a36Sopenharmony_ci	pci_disable_device(pdev);
144862306a36Sopenharmony_ci	return err;
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci/**
145262306a36Sopenharmony_ci * atl2_remove - Device Removal Routine
145362306a36Sopenharmony_ci * @pdev: PCI device information struct
145462306a36Sopenharmony_ci *
145562306a36Sopenharmony_ci * atl2_remove is called by the PCI subsystem to alert the driver
145662306a36Sopenharmony_ci * that it should release a PCI device.  The could be caused by a
145762306a36Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from
145862306a36Sopenharmony_ci * memory.
145962306a36Sopenharmony_ci */
146062306a36Sopenharmony_ci/* FIXME: write the original MAC address back in case it was changed from a
146162306a36Sopenharmony_ci * BIOS-set value, as in atl1 -- CHS */
146262306a36Sopenharmony_cistatic void atl2_remove(struct pci_dev *pdev)
146362306a36Sopenharmony_ci{
146462306a36Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
146562306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	/* flush_scheduled work may reschedule our watchdog task, so
146862306a36Sopenharmony_ci	 * explicitly disable watchdog tasks from being rescheduled  */
146962306a36Sopenharmony_ci	set_bit(__ATL2_DOWN, &adapter->flags);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	del_timer_sync(&adapter->watchdog_timer);
147262306a36Sopenharmony_ci	del_timer_sync(&adapter->phy_config_timer);
147362306a36Sopenharmony_ci	cancel_work_sync(&adapter->reset_task);
147462306a36Sopenharmony_ci	cancel_work_sync(&adapter->link_chg_task);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	unregister_netdev(netdev);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	atl2_force_ps(&adapter->hw);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	iounmap(adapter->hw.hw_addr);
148162306a36Sopenharmony_ci	pci_release_regions(pdev);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	free_netdev(netdev);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	pci_disable_device(pdev);
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cistatic int atl2_suspend(struct pci_dev *pdev, pm_message_t state)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
149162306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
149262306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
149362306a36Sopenharmony_ci	u16 speed, duplex;
149462306a36Sopenharmony_ci	u32 ctrl = 0;
149562306a36Sopenharmony_ci	u32 wufc = adapter->wol;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci#ifdef CONFIG_PM
149862306a36Sopenharmony_ci	int retval = 0;
149962306a36Sopenharmony_ci#endif
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	netif_device_detach(netdev);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (netif_running(netdev)) {
150462306a36Sopenharmony_ci		WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags));
150562306a36Sopenharmony_ci		atl2_down(adapter);
150662306a36Sopenharmony_ci	}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci#ifdef CONFIG_PM
150962306a36Sopenharmony_ci	retval = pci_save_state(pdev);
151062306a36Sopenharmony_ci	if (retval)
151162306a36Sopenharmony_ci		return retval;
151262306a36Sopenharmony_ci#endif
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl);
151562306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl);
151662306a36Sopenharmony_ci	if (ctrl & BMSR_LSTATUS)
151762306a36Sopenharmony_ci		wufc &= ~ATLX_WUFC_LNKC;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) {
152062306a36Sopenharmony_ci		u32 ret_val;
152162306a36Sopenharmony_ci		/* get current link speed & duplex */
152262306a36Sopenharmony_ci		ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex);
152362306a36Sopenharmony_ci		if (ret_val) {
152462306a36Sopenharmony_ci			printk(KERN_DEBUG
152562306a36Sopenharmony_ci				"%s: get speed&duplex error while suspend\n",
152662306a36Sopenharmony_ci				atl2_driver_name);
152762306a36Sopenharmony_ci			goto wol_dis;
152862306a36Sopenharmony_ci		}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		ctrl = 0;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci		/* turn on magic packet wol */
153362306a36Sopenharmony_ci		if (wufc & ATLX_WUFC_MAG)
153462306a36Sopenharmony_ci			ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci		/* ignore Link Chg event when Link is up */
153762306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		/* Config MAC CTRL Register */
154062306a36Sopenharmony_ci		ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY;
154162306a36Sopenharmony_ci		if (FULL_DUPLEX == adapter->link_duplex)
154262306a36Sopenharmony_ci			ctrl |= MAC_CTRL_DUPLX;
154362306a36Sopenharmony_ci		ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
154462306a36Sopenharmony_ci		ctrl |= (((u32)adapter->hw.preamble_len &
154562306a36Sopenharmony_ci			MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
154662306a36Sopenharmony_ci		ctrl |= (((u32)(adapter->hw.retry_buf &
154762306a36Sopenharmony_ci			MAC_CTRL_HALF_LEFT_BUF_MASK)) <<
154862306a36Sopenharmony_ci			MAC_CTRL_HALF_LEFT_BUF_SHIFT);
154962306a36Sopenharmony_ci		if (wufc & ATLX_WUFC_MAG) {
155062306a36Sopenharmony_ci			/* magic packet maybe Broadcast&multicast&Unicast */
155162306a36Sopenharmony_ci			ctrl |= MAC_CTRL_BC_EN;
155262306a36Sopenharmony_ci		}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl);
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci		/* pcie patch */
155762306a36Sopenharmony_ci		ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
155862306a36Sopenharmony_ci		ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
155962306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
156062306a36Sopenharmony_ci		ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
156162306a36Sopenharmony_ci		ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
156262306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
156562306a36Sopenharmony_ci		goto suspend_exit;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) {
156962306a36Sopenharmony_ci		/* link is down, so only LINK CHG WOL event enable */
157062306a36Sopenharmony_ci		ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
157162306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);
157262306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci		/* pcie patch */
157562306a36Sopenharmony_ci		ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
157662306a36Sopenharmony_ci		ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
157762306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
157862306a36Sopenharmony_ci		ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
157962306a36Sopenharmony_ci		ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
158062306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci		hw->phy_configured = false; /* re-init PHY when resume */
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		goto suspend_exit;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ciwol_dis:
159062306a36Sopenharmony_ci	/* WOL disabled */
159162306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/* pcie patch */
159462306a36Sopenharmony_ci	ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC);
159562306a36Sopenharmony_ci	ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
159662306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
159762306a36Sopenharmony_ci	ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1);
159862306a36Sopenharmony_ci	ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK;
159962306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	atl2_force_ps(hw);
160262306a36Sopenharmony_ci	hw->phy_configured = false; /* re-init PHY when resume */
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_cisuspend_exit:
160762306a36Sopenharmony_ci	if (netif_running(netdev))
160862306a36Sopenharmony_ci		atl2_free_irq(adapter);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	pci_disable_device(pdev);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	pci_set_power_state(pdev, pci_choose_state(pdev, state));
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	return 0;
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci#ifdef CONFIG_PM
161862306a36Sopenharmony_cistatic int atl2_resume(struct pci_dev *pdev)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
162162306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
162262306a36Sopenharmony_ci	u32 err;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	pci_set_power_state(pdev, PCI_D0);
162562306a36Sopenharmony_ci	pci_restore_state(pdev);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	err = pci_enable_device(pdev);
162862306a36Sopenharmony_ci	if (err) {
162962306a36Sopenharmony_ci		printk(KERN_ERR
163062306a36Sopenharmony_ci			"atl2: Cannot enable PCI device from suspend\n");
163162306a36Sopenharmony_ci		return err;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	pci_set_master(pdev);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	pci_enable_wake(pdev, PCI_D3hot, 0);
163962306a36Sopenharmony_ci	pci_enable_wake(pdev, PCI_D3cold, 0);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	if (netif_running(netdev)) {
164462306a36Sopenharmony_ci		err = atl2_request_irq(adapter);
164562306a36Sopenharmony_ci		if (err)
164662306a36Sopenharmony_ci			return err;
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	atl2_reset_hw(&adapter->hw);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (netif_running(netdev))
165262306a36Sopenharmony_ci		atl2_up(adapter);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	netif_device_attach(netdev);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	return 0;
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci#endif
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic void atl2_shutdown(struct pci_dev *pdev)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	atl2_suspend(pdev, PMSG_SUSPEND);
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic struct pci_driver atl2_driver = {
166662306a36Sopenharmony_ci	.name     = atl2_driver_name,
166762306a36Sopenharmony_ci	.id_table = atl2_pci_tbl,
166862306a36Sopenharmony_ci	.probe    = atl2_probe,
166962306a36Sopenharmony_ci	.remove   = atl2_remove,
167062306a36Sopenharmony_ci	/* Power Management Hooks */
167162306a36Sopenharmony_ci	.suspend  = atl2_suspend,
167262306a36Sopenharmony_ci#ifdef CONFIG_PM
167362306a36Sopenharmony_ci	.resume   = atl2_resume,
167462306a36Sopenharmony_ci#endif
167562306a36Sopenharmony_ci	.shutdown = atl2_shutdown,
167662306a36Sopenharmony_ci};
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cimodule_pci_driver(atl2_driver);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value)
168162306a36Sopenharmony_ci{
168262306a36Sopenharmony_ci	struct atl2_adapter *adapter = hw->back;
168362306a36Sopenharmony_ci	pci_read_config_word(adapter->pdev, reg, value);
168462306a36Sopenharmony_ci}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_cistatic void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	struct atl2_adapter *adapter = hw->back;
168962306a36Sopenharmony_ci	pci_write_config_word(adapter->pdev, reg, *value);
169062306a36Sopenharmony_ci}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_cistatic int atl2_get_link_ksettings(struct net_device *netdev,
169362306a36Sopenharmony_ci				   struct ethtool_link_ksettings *cmd)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
169662306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
169762306a36Sopenharmony_ci	u32 supported, advertising;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	supported = (SUPPORTED_10baseT_Half |
170062306a36Sopenharmony_ci		SUPPORTED_10baseT_Full |
170162306a36Sopenharmony_ci		SUPPORTED_100baseT_Half |
170262306a36Sopenharmony_ci		SUPPORTED_100baseT_Full |
170362306a36Sopenharmony_ci		SUPPORTED_Autoneg |
170462306a36Sopenharmony_ci		SUPPORTED_TP);
170562306a36Sopenharmony_ci	advertising = ADVERTISED_TP;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	advertising |= ADVERTISED_Autoneg;
170862306a36Sopenharmony_ci	advertising |= hw->autoneg_advertised;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	cmd->base.port = PORT_TP;
171162306a36Sopenharmony_ci	cmd->base.phy_address = 0;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (adapter->link_speed != SPEED_0) {
171462306a36Sopenharmony_ci		cmd->base.speed = adapter->link_speed;
171562306a36Sopenharmony_ci		if (adapter->link_duplex == FULL_DUPLEX)
171662306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_FULL;
171762306a36Sopenharmony_ci		else
171862306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_HALF;
171962306a36Sopenharmony_ci	} else {
172062306a36Sopenharmony_ci		cmd->base.speed = SPEED_UNKNOWN;
172162306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
172262306a36Sopenharmony_ci	}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	cmd->base.autoneg = AUTONEG_ENABLE;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
172762306a36Sopenharmony_ci						supported);
172862306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
172962306a36Sopenharmony_ci						advertising);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	return 0;
173262306a36Sopenharmony_ci}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_cistatic int atl2_set_link_ksettings(struct net_device *netdev,
173562306a36Sopenharmony_ci				   const struct ethtool_link_ksettings *cmd)
173662306a36Sopenharmony_ci{
173762306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
173862306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
173962306a36Sopenharmony_ci	u32 advertising;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
174262306a36Sopenharmony_ci						cmd->link_modes.advertising);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
174562306a36Sopenharmony_ci		msleep(1);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
174862306a36Sopenharmony_ci#define MY_ADV_MASK	(ADVERTISE_10_HALF | \
174962306a36Sopenharmony_ci			 ADVERTISE_10_FULL | \
175062306a36Sopenharmony_ci			 ADVERTISE_100_HALF| \
175162306a36Sopenharmony_ci			 ADVERTISE_100_FULL)
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci		if ((advertising & MY_ADV_MASK) == MY_ADV_MASK) {
175462306a36Sopenharmony_ci			hw->MediaType = MEDIA_TYPE_AUTO_SENSOR;
175562306a36Sopenharmony_ci			hw->autoneg_advertised =  MY_ADV_MASK;
175662306a36Sopenharmony_ci		} else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_FULL) {
175762306a36Sopenharmony_ci			hw->MediaType = MEDIA_TYPE_100M_FULL;
175862306a36Sopenharmony_ci			hw->autoneg_advertised = ADVERTISE_100_FULL;
175962306a36Sopenharmony_ci		} else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_HALF) {
176062306a36Sopenharmony_ci			hw->MediaType = MEDIA_TYPE_100M_HALF;
176162306a36Sopenharmony_ci			hw->autoneg_advertised = ADVERTISE_100_HALF;
176262306a36Sopenharmony_ci		} else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_FULL) {
176362306a36Sopenharmony_ci			hw->MediaType = MEDIA_TYPE_10M_FULL;
176462306a36Sopenharmony_ci			hw->autoneg_advertised = ADVERTISE_10_FULL;
176562306a36Sopenharmony_ci		}  else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_HALF) {
176662306a36Sopenharmony_ci			hw->MediaType = MEDIA_TYPE_10M_HALF;
176762306a36Sopenharmony_ci			hw->autoneg_advertised = ADVERTISE_10_HALF;
176862306a36Sopenharmony_ci		} else {
176962306a36Sopenharmony_ci			clear_bit(__ATL2_RESETTING, &adapter->flags);
177062306a36Sopenharmony_ci			return -EINVAL;
177162306a36Sopenharmony_ci		}
177262306a36Sopenharmony_ci		advertising = hw->autoneg_advertised |
177362306a36Sopenharmony_ci			ADVERTISED_TP | ADVERTISED_Autoneg;
177462306a36Sopenharmony_ci	} else {
177562306a36Sopenharmony_ci		clear_bit(__ATL2_RESETTING, &adapter->flags);
177662306a36Sopenharmony_ci		return -EINVAL;
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	/* reset the link */
178062306a36Sopenharmony_ci	if (netif_running(adapter->netdev)) {
178162306a36Sopenharmony_ci		atl2_down(adapter);
178262306a36Sopenharmony_ci		atl2_up(adapter);
178362306a36Sopenharmony_ci	} else
178462306a36Sopenharmony_ci		atl2_reset_hw(&adapter->hw);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	clear_bit(__ATL2_RESETTING, &adapter->flags);
178762306a36Sopenharmony_ci	return 0;
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic u32 atl2_get_msglevel(struct net_device *netdev)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	return 0;
179362306a36Sopenharmony_ci}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci/*
179662306a36Sopenharmony_ci * It's sane for this to be empty, but we might want to take advantage of this.
179762306a36Sopenharmony_ci */
179862306a36Sopenharmony_cistatic void atl2_set_msglevel(struct net_device *netdev, u32 data)
179962306a36Sopenharmony_ci{
180062306a36Sopenharmony_ci}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_cistatic int atl2_get_regs_len(struct net_device *netdev)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci#define ATL2_REGS_LEN 42
180562306a36Sopenharmony_ci	return sizeof(u32) * ATL2_REGS_LEN;
180662306a36Sopenharmony_ci}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_cistatic void atl2_get_regs(struct net_device *netdev,
180962306a36Sopenharmony_ci	struct ethtool_regs *regs, void *p)
181062306a36Sopenharmony_ci{
181162306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
181262306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
181362306a36Sopenharmony_ci	u32 *regs_buff = p;
181462306a36Sopenharmony_ci	u16 phy_data;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	memset(p, 0, sizeof(u32) * ATL2_REGS_LEN);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	regs_buff[0]  = ATL2_READ_REG(hw, REG_VPD_CAP);
182162306a36Sopenharmony_ci	regs_buff[1]  = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
182262306a36Sopenharmony_ci	regs_buff[2]  = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG);
182362306a36Sopenharmony_ci	regs_buff[3]  = ATL2_READ_REG(hw, REG_TWSI_CTRL);
182462306a36Sopenharmony_ci	regs_buff[4]  = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL);
182562306a36Sopenharmony_ci	regs_buff[5]  = ATL2_READ_REG(hw, REG_MASTER_CTRL);
182662306a36Sopenharmony_ci	regs_buff[6]  = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT);
182762306a36Sopenharmony_ci	regs_buff[7]  = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT);
182862306a36Sopenharmony_ci	regs_buff[8]  = ATL2_READ_REG(hw, REG_PHY_ENABLE);
182962306a36Sopenharmony_ci	regs_buff[9]  = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER);
183062306a36Sopenharmony_ci	regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS);
183162306a36Sopenharmony_ci	regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL);
183262306a36Sopenharmony_ci	regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK);
183362306a36Sopenharmony_ci	regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL);
183462306a36Sopenharmony_ci	regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG);
183562306a36Sopenharmony_ci	regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
183662306a36Sopenharmony_ci	regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4);
183762306a36Sopenharmony_ci	regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE);
183862306a36Sopenharmony_ci	regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4);
183962306a36Sopenharmony_ci	regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL);
184062306a36Sopenharmony_ci	regs_buff[20] = ATL2_READ_REG(hw, REG_MTU);
184162306a36Sopenharmony_ci	regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL);
184262306a36Sopenharmony_ci	regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END);
184362306a36Sopenharmony_ci	regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI);
184462306a36Sopenharmony_ci	regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO);
184562306a36Sopenharmony_ci	regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE);
184662306a36Sopenharmony_ci	regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO);
184762306a36Sopenharmony_ci	regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE);
184862306a36Sopenharmony_ci	regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO);
184962306a36Sopenharmony_ci	regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM);
185062306a36Sopenharmony_ci	regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR);
185162306a36Sopenharmony_ci	regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH);
185262306a36Sopenharmony_ci	regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW);
185362306a36Sopenharmony_ci	regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH);
185462306a36Sopenharmony_ci	regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH);
185562306a36Sopenharmony_ci	regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX);
185662306a36Sopenharmony_ci	regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX);
185762306a36Sopenharmony_ci	regs_buff[38] = ATL2_READ_REG(hw, REG_ISR);
185862306a36Sopenharmony_ci	regs_buff[39] = ATL2_READ_REG(hw, REG_IMR);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_BMCR, &phy_data);
186162306a36Sopenharmony_ci	regs_buff[40] = (u32)phy_data;
186262306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
186362306a36Sopenharmony_ci	regs_buff[41] = (u32)phy_data;
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic int atl2_get_eeprom_len(struct net_device *netdev)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	if (!atl2_check_eeprom_exist(&adapter->hw))
187162306a36Sopenharmony_ci		return 512;
187262306a36Sopenharmony_ci	else
187362306a36Sopenharmony_ci		return 0;
187462306a36Sopenharmony_ci}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_cistatic int atl2_get_eeprom(struct net_device *netdev,
187762306a36Sopenharmony_ci	struct ethtool_eeprom *eeprom, u8 *bytes)
187862306a36Sopenharmony_ci{
187962306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
188062306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
188162306a36Sopenharmony_ci	u32 *eeprom_buff;
188262306a36Sopenharmony_ci	int first_dword, last_dword;
188362306a36Sopenharmony_ci	int ret_val = 0;
188462306a36Sopenharmony_ci	int i;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	if (eeprom->len == 0)
188762306a36Sopenharmony_ci		return -EINVAL;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (atl2_check_eeprom_exist(hw))
189062306a36Sopenharmony_ci		return -EINVAL;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	eeprom->magic = hw->vendor_id | (hw->device_id << 16);
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	first_dword = eeprom->offset >> 2;
189562306a36Sopenharmony_ci	last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32),
189862306a36Sopenharmony_ci				    GFP_KERNEL);
189962306a36Sopenharmony_ci	if (!eeprom_buff)
190062306a36Sopenharmony_ci		return -ENOMEM;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	for (i = first_dword; i < last_dword; i++) {
190362306a36Sopenharmony_ci		if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) {
190462306a36Sopenharmony_ci			ret_val = -EIO;
190562306a36Sopenharmony_ci			goto free;
190662306a36Sopenharmony_ci		}
190762306a36Sopenharmony_ci	}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
191062306a36Sopenharmony_ci		eeprom->len);
191162306a36Sopenharmony_cifree:
191262306a36Sopenharmony_ci	kfree(eeprom_buff);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	return ret_val;
191562306a36Sopenharmony_ci}
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_cistatic int atl2_set_eeprom(struct net_device *netdev,
191862306a36Sopenharmony_ci	struct ethtool_eeprom *eeprom, u8 *bytes)
191962306a36Sopenharmony_ci{
192062306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
192162306a36Sopenharmony_ci	struct atl2_hw *hw = &adapter->hw;
192262306a36Sopenharmony_ci	u32 *eeprom_buff;
192362306a36Sopenharmony_ci	u32 *ptr;
192462306a36Sopenharmony_ci	int max_len, first_dword, last_dword, ret_val = 0;
192562306a36Sopenharmony_ci	int i;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (eeprom->len == 0)
192862306a36Sopenharmony_ci		return -EOPNOTSUPP;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
193162306a36Sopenharmony_ci		return -EFAULT;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	max_len = 512;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	first_dword = eeprom->offset >> 2;
193662306a36Sopenharmony_ci	last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
193762306a36Sopenharmony_ci	eeprom_buff = kmalloc(max_len, GFP_KERNEL);
193862306a36Sopenharmony_ci	if (!eeprom_buff)
193962306a36Sopenharmony_ci		return -ENOMEM;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	ptr = eeprom_buff;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	if (eeprom->offset & 3) {
194462306a36Sopenharmony_ci		/* need read/modify/write of first changed EEPROM word */
194562306a36Sopenharmony_ci		/* only the second byte of the word is being modified */
194662306a36Sopenharmony_ci		if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) {
194762306a36Sopenharmony_ci			ret_val = -EIO;
194862306a36Sopenharmony_ci			goto out;
194962306a36Sopenharmony_ci		}
195062306a36Sopenharmony_ci		ptr++;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci	if (((eeprom->offset + eeprom->len) & 3)) {
195362306a36Sopenharmony_ci		/*
195462306a36Sopenharmony_ci		 * need read/modify/write of last changed EEPROM word
195562306a36Sopenharmony_ci		 * only the first byte of the word is being modified
195662306a36Sopenharmony_ci		 */
195762306a36Sopenharmony_ci		if (!atl2_read_eeprom(hw, last_dword * 4,
195862306a36Sopenharmony_ci					&(eeprom_buff[last_dword - first_dword]))) {
195962306a36Sopenharmony_ci			ret_val = -EIO;
196062306a36Sopenharmony_ci			goto out;
196162306a36Sopenharmony_ci		}
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	/* Device's eeprom is always little-endian, word addressable */
196562306a36Sopenharmony_ci	memcpy(ptr, bytes, eeprom->len);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	for (i = 0; i < last_dword - first_dword + 1; i++) {
196862306a36Sopenharmony_ci		if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) {
196962306a36Sopenharmony_ci			ret_val = -EIO;
197062306a36Sopenharmony_ci			goto out;
197162306a36Sopenharmony_ci		}
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci out:
197462306a36Sopenharmony_ci	kfree(eeprom_buff);
197562306a36Sopenharmony_ci	return ret_val;
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_cistatic void atl2_get_drvinfo(struct net_device *netdev,
197962306a36Sopenharmony_ci	struct ethtool_drvinfo *drvinfo)
198062306a36Sopenharmony_ci{
198162306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	strscpy(drvinfo->driver,  atl2_driver_name, sizeof(drvinfo->driver));
198462306a36Sopenharmony_ci	strscpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version));
198562306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
198662306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_cistatic void atl2_get_wol(struct net_device *netdev,
199062306a36Sopenharmony_ci	struct ethtool_wolinfo *wol)
199162306a36Sopenharmony_ci{
199262306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	wol->supported = WAKE_MAGIC;
199562306a36Sopenharmony_ci	wol->wolopts = 0;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	if (adapter->wol & ATLX_WUFC_EX)
199862306a36Sopenharmony_ci		wol->wolopts |= WAKE_UCAST;
199962306a36Sopenharmony_ci	if (adapter->wol & ATLX_WUFC_MC)
200062306a36Sopenharmony_ci		wol->wolopts |= WAKE_MCAST;
200162306a36Sopenharmony_ci	if (adapter->wol & ATLX_WUFC_BC)
200262306a36Sopenharmony_ci		wol->wolopts |= WAKE_BCAST;
200362306a36Sopenharmony_ci	if (adapter->wol & ATLX_WUFC_MAG)
200462306a36Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
200562306a36Sopenharmony_ci	if (adapter->wol & ATLX_WUFC_LNKC)
200662306a36Sopenharmony_ci		wol->wolopts |= WAKE_PHY;
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_cistatic int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
201062306a36Sopenharmony_ci{
201162306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
201462306a36Sopenharmony_ci		return -EOPNOTSUPP;
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
201762306a36Sopenharmony_ci		return -EOPNOTSUPP;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	/* these settings will always override what we currently have */
202062306a36Sopenharmony_ci	adapter->wol = 0;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC)
202362306a36Sopenharmony_ci		adapter->wol |= ATLX_WUFC_MAG;
202462306a36Sopenharmony_ci	if (wol->wolopts & WAKE_PHY)
202562306a36Sopenharmony_ci		adapter->wol |= ATLX_WUFC_LNKC;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	return 0;
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_cistatic int atl2_nway_reset(struct net_device *netdev)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct atl2_adapter *adapter = netdev_priv(netdev);
203362306a36Sopenharmony_ci	if (netif_running(netdev))
203462306a36Sopenharmony_ci		atl2_reinit_locked(adapter);
203562306a36Sopenharmony_ci	return 0;
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic const struct ethtool_ops atl2_ethtool_ops = {
203962306a36Sopenharmony_ci	.get_drvinfo		= atl2_get_drvinfo,
204062306a36Sopenharmony_ci	.get_regs_len		= atl2_get_regs_len,
204162306a36Sopenharmony_ci	.get_regs		= atl2_get_regs,
204262306a36Sopenharmony_ci	.get_wol		= atl2_get_wol,
204362306a36Sopenharmony_ci	.set_wol		= atl2_set_wol,
204462306a36Sopenharmony_ci	.get_msglevel		= atl2_get_msglevel,
204562306a36Sopenharmony_ci	.set_msglevel		= atl2_set_msglevel,
204662306a36Sopenharmony_ci	.nway_reset		= atl2_nway_reset,
204762306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
204862306a36Sopenharmony_ci	.get_eeprom_len		= atl2_get_eeprom_len,
204962306a36Sopenharmony_ci	.get_eeprom		= atl2_get_eeprom,
205062306a36Sopenharmony_ci	.set_eeprom		= atl2_set_eeprom,
205162306a36Sopenharmony_ci	.get_link_ksettings	= atl2_get_link_ksettings,
205262306a36Sopenharmony_ci	.set_link_ksettings	= atl2_set_link_ksettings,
205362306a36Sopenharmony_ci};
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci#define LBYTESWAP(a)  ((((a) & 0x00ff00ff) << 8) | \
205662306a36Sopenharmony_ci	(((a) & 0xff00ff00) >> 8))
205762306a36Sopenharmony_ci#define LONGSWAP(a)   ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16))
205862306a36Sopenharmony_ci#define SHORTSWAP(a)  (((a) << 8) | ((a) >> 8))
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci/*
206162306a36Sopenharmony_ci * Reset the transmit and receive units; mask and clear all interrupts.
206262306a36Sopenharmony_ci *
206362306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
206462306a36Sopenharmony_ci * return : 0  or  idle status (if error)
206562306a36Sopenharmony_ci */
206662306a36Sopenharmony_cistatic s32 atl2_reset_hw(struct atl2_hw *hw)
206762306a36Sopenharmony_ci{
206862306a36Sopenharmony_ci	u32 icr;
206962306a36Sopenharmony_ci	u16 pci_cfg_cmd_word;
207062306a36Sopenharmony_ci	int i;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
207362306a36Sopenharmony_ci	atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word);
207462306a36Sopenharmony_ci	if ((pci_cfg_cmd_word &
207562306a36Sopenharmony_ci		(CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) !=
207662306a36Sopenharmony_ci		(CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) {
207762306a36Sopenharmony_ci		pci_cfg_cmd_word |=
207862306a36Sopenharmony_ci			(CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER);
207962306a36Sopenharmony_ci		atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word);
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	/* Clear Interrupt mask to stop board from generating
208362306a36Sopenharmony_ci	 * interrupts & Clear any pending interrupt events
208462306a36Sopenharmony_ci	 */
208562306a36Sopenharmony_ci	/* FIXME */
208662306a36Sopenharmony_ci	/* ATL2_WRITE_REG(hw, REG_IMR, 0); */
208762306a36Sopenharmony_ci	/* ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); */
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	/* Issue Soft Reset to the MAC.  This will reset the chip's
209062306a36Sopenharmony_ci	 * transmit, receive, DMA.  It will not effect
209162306a36Sopenharmony_ci	 * the current PCI configuration.  The global reset bit is self-
209262306a36Sopenharmony_ci	 * clearing, and should clear within a microsecond.
209362306a36Sopenharmony_ci	 */
209462306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST);
209562306a36Sopenharmony_ci	wmb();
209662306a36Sopenharmony_ci	msleep(1); /* delay about 1ms */
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	/* Wait at least 10ms for All module to be Idle */
209962306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
210062306a36Sopenharmony_ci		icr = ATL2_READ_REG(hw, REG_IDLE_STATUS);
210162306a36Sopenharmony_ci		if (!icr)
210262306a36Sopenharmony_ci			break;
210362306a36Sopenharmony_ci		msleep(1); /* delay 1 ms */
210462306a36Sopenharmony_ci		cpu_relax();
210562306a36Sopenharmony_ci	}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	if (icr)
210862306a36Sopenharmony_ci		return icr;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	return 0;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci#define CUSTOM_SPI_CS_SETUP        2
211462306a36Sopenharmony_ci#define CUSTOM_SPI_CLK_HI          2
211562306a36Sopenharmony_ci#define CUSTOM_SPI_CLK_LO          2
211662306a36Sopenharmony_ci#define CUSTOM_SPI_CS_HOLD         2
211762306a36Sopenharmony_ci#define CUSTOM_SPI_CS_HI           3
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_cistatic struct atl2_spi_flash_dev flash_table[] =
212062306a36Sopenharmony_ci{
212162306a36Sopenharmony_ci/* MFR    WRSR  READ  PROGRAM WREN  WRDI  RDSR  RDID  SECTOR_ERASE CHIP_ERASE */
212262306a36Sopenharmony_ci{"Atmel", 0x0,  0x03, 0x02,   0x06, 0x04, 0x05, 0x15, 0x52,        0x62 },
212362306a36Sopenharmony_ci{"SST",   0x01, 0x03, 0x02,   0x06, 0x04, 0x05, 0x90, 0x20,        0x60 },
212462306a36Sopenharmony_ci{"ST",    0x01, 0x03, 0x02,   0x06, 0x04, 0x05, 0xAB, 0xD8,        0xC7 },
212562306a36Sopenharmony_ci};
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_cistatic bool atl2_spi_read(struct atl2_hw *hw, u32 addr, u32 *buf)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	int i;
213062306a36Sopenharmony_ci	u32 value;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_SPI_DATA, 0);
213362306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	value = SPI_FLASH_CTRL_WAIT_READY |
213662306a36Sopenharmony_ci		(CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) <<
213762306a36Sopenharmony_ci			SPI_FLASH_CTRL_CS_SETUP_SHIFT |
213862306a36Sopenharmony_ci		(CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) <<
213962306a36Sopenharmony_ci			SPI_FLASH_CTRL_CLK_HI_SHIFT |
214062306a36Sopenharmony_ci		(CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) <<
214162306a36Sopenharmony_ci			SPI_FLASH_CTRL_CLK_LO_SHIFT |
214262306a36Sopenharmony_ci		(CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) <<
214362306a36Sopenharmony_ci			SPI_FLASH_CTRL_CS_HOLD_SHIFT |
214462306a36Sopenharmony_ci		(CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) <<
214562306a36Sopenharmony_ci			SPI_FLASH_CTRL_CS_HI_SHIFT |
214662306a36Sopenharmony_ci		(0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	value |= SPI_FLASH_CTRL_START;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
215562306a36Sopenharmony_ci		msleep(1);
215662306a36Sopenharmony_ci		value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
215762306a36Sopenharmony_ci		if (!(value & SPI_FLASH_CTRL_START))
215862306a36Sopenharmony_ci			break;
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	if (value & SPI_FLASH_CTRL_START)
216262306a36Sopenharmony_ci		return false;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	*buf = ATL2_READ_REG(hw, REG_SPI_DATA);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	return true;
216762306a36Sopenharmony_ci}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci/*
217062306a36Sopenharmony_ci * get_permanent_address
217162306a36Sopenharmony_ci * return 0 if get valid mac address,
217262306a36Sopenharmony_ci */
217362306a36Sopenharmony_cistatic int get_permanent_address(struct atl2_hw *hw)
217462306a36Sopenharmony_ci{
217562306a36Sopenharmony_ci	u32 Addr[2];
217662306a36Sopenharmony_ci	u32 i, Control;
217762306a36Sopenharmony_ci	u16 Register;
217862306a36Sopenharmony_ci	u8  EthAddr[ETH_ALEN];
217962306a36Sopenharmony_ci	bool KeyValid;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	if (is_valid_ether_addr(hw->perm_mac_addr))
218262306a36Sopenharmony_ci		return 0;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	Addr[0] = 0;
218562306a36Sopenharmony_ci	Addr[1] = 0;
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */
218862306a36Sopenharmony_ci		Register = 0;
218962306a36Sopenharmony_ci		KeyValid = false;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci		/* Read out all EEPROM content */
219262306a36Sopenharmony_ci		i = 0;
219362306a36Sopenharmony_ci		while (1) {
219462306a36Sopenharmony_ci			if (atl2_read_eeprom(hw, i + 0x100, &Control)) {
219562306a36Sopenharmony_ci				if (KeyValid) {
219662306a36Sopenharmony_ci					if (Register == REG_MAC_STA_ADDR)
219762306a36Sopenharmony_ci						Addr[0] = Control;
219862306a36Sopenharmony_ci					else if (Register ==
219962306a36Sopenharmony_ci						(REG_MAC_STA_ADDR + 4))
220062306a36Sopenharmony_ci						Addr[1] = Control;
220162306a36Sopenharmony_ci					KeyValid = false;
220262306a36Sopenharmony_ci				} else if ((Control & 0xff) == 0x5A) {
220362306a36Sopenharmony_ci					KeyValid = true;
220462306a36Sopenharmony_ci					Register = (u16) (Control >> 16);
220562306a36Sopenharmony_ci				} else {
220662306a36Sopenharmony_ci			/* assume data end while encount an invalid KEYWORD */
220762306a36Sopenharmony_ci					break;
220862306a36Sopenharmony_ci				}
220962306a36Sopenharmony_ci			} else {
221062306a36Sopenharmony_ci				break; /* read error */
221162306a36Sopenharmony_ci			}
221262306a36Sopenharmony_ci			i += 4;
221362306a36Sopenharmony_ci		}
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci		*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
221662306a36Sopenharmony_ci		*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci		if (is_valid_ether_addr(EthAddr)) {
221962306a36Sopenharmony_ci			memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
222062306a36Sopenharmony_ci			return 0;
222162306a36Sopenharmony_ci		}
222262306a36Sopenharmony_ci		return 1;
222362306a36Sopenharmony_ci	}
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	/* see if SPI flash exists? */
222662306a36Sopenharmony_ci	Addr[0] = 0;
222762306a36Sopenharmony_ci	Addr[1] = 0;
222862306a36Sopenharmony_ci	Register = 0;
222962306a36Sopenharmony_ci	KeyValid = false;
223062306a36Sopenharmony_ci	i = 0;
223162306a36Sopenharmony_ci	while (1) {
223262306a36Sopenharmony_ci		if (atl2_spi_read(hw, i + 0x1f000, &Control)) {
223362306a36Sopenharmony_ci			if (KeyValid) {
223462306a36Sopenharmony_ci				if (Register == REG_MAC_STA_ADDR)
223562306a36Sopenharmony_ci					Addr[0] = Control;
223662306a36Sopenharmony_ci				else if (Register == (REG_MAC_STA_ADDR + 4))
223762306a36Sopenharmony_ci					Addr[1] = Control;
223862306a36Sopenharmony_ci				KeyValid = false;
223962306a36Sopenharmony_ci			} else if ((Control & 0xff) == 0x5A) {
224062306a36Sopenharmony_ci				KeyValid = true;
224162306a36Sopenharmony_ci				Register = (u16) (Control >> 16);
224262306a36Sopenharmony_ci			} else {
224362306a36Sopenharmony_ci				break; /* data end */
224462306a36Sopenharmony_ci			}
224562306a36Sopenharmony_ci		} else {
224662306a36Sopenharmony_ci			break; /* read error */
224762306a36Sopenharmony_ci		}
224862306a36Sopenharmony_ci		i += 4;
224962306a36Sopenharmony_ci	}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
225262306a36Sopenharmony_ci	*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]);
225362306a36Sopenharmony_ci	if (is_valid_ether_addr(EthAddr)) {
225462306a36Sopenharmony_ci		memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
225562306a36Sopenharmony_ci		return 0;
225662306a36Sopenharmony_ci	}
225762306a36Sopenharmony_ci	/* maybe MAC-address is from BIOS */
225862306a36Sopenharmony_ci	Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
225962306a36Sopenharmony_ci	Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4);
226062306a36Sopenharmony_ci	*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
226162306a36Sopenharmony_ci	*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	if (is_valid_ether_addr(EthAddr)) {
226462306a36Sopenharmony_ci		memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
226562306a36Sopenharmony_ci		return 0;
226662306a36Sopenharmony_ci	}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	return 1;
226962306a36Sopenharmony_ci}
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci/*
227262306a36Sopenharmony_ci * Reads the adapter's MAC address from the EEPROM
227362306a36Sopenharmony_ci *
227462306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
227562306a36Sopenharmony_ci */
227662306a36Sopenharmony_cistatic s32 atl2_read_mac_addr(struct atl2_hw *hw)
227762306a36Sopenharmony_ci{
227862306a36Sopenharmony_ci	if (get_permanent_address(hw)) {
227962306a36Sopenharmony_ci		/* for test */
228062306a36Sopenharmony_ci		/* FIXME: shouldn't we use eth_random_addr() here? */
228162306a36Sopenharmony_ci		hw->perm_mac_addr[0] = 0x00;
228262306a36Sopenharmony_ci		hw->perm_mac_addr[1] = 0x13;
228362306a36Sopenharmony_ci		hw->perm_mac_addr[2] = 0x74;
228462306a36Sopenharmony_ci		hw->perm_mac_addr[3] = 0x00;
228562306a36Sopenharmony_ci		hw->perm_mac_addr[4] = 0x5c;
228662306a36Sopenharmony_ci		hw->perm_mac_addr[5] = 0x38;
228762306a36Sopenharmony_ci	}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	memcpy(hw->mac_addr, hw->perm_mac_addr, ETH_ALEN);
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	return 0;
229262306a36Sopenharmony_ci}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci/*
229562306a36Sopenharmony_ci * Hashes an address to determine its location in the multicast table
229662306a36Sopenharmony_ci *
229762306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
229862306a36Sopenharmony_ci * mc_addr - the multicast address to hash
229962306a36Sopenharmony_ci *
230062306a36Sopenharmony_ci * atl2_hash_mc_addr
230162306a36Sopenharmony_ci *  purpose
230262306a36Sopenharmony_ci *      set hash value for a multicast address
230362306a36Sopenharmony_ci *      hash calcu processing :
230462306a36Sopenharmony_ci *          1. calcu 32bit CRC for multicast address
230562306a36Sopenharmony_ci *          2. reverse crc with MSB to LSB
230662306a36Sopenharmony_ci */
230762306a36Sopenharmony_cistatic u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr)
230862306a36Sopenharmony_ci{
230962306a36Sopenharmony_ci	u32 crc32, value;
231062306a36Sopenharmony_ci	int i;
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	value = 0;
231362306a36Sopenharmony_ci	crc32 = ether_crc_le(6, mc_addr);
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	for (i = 0; i < 32; i++)
231662306a36Sopenharmony_ci		value |= (((crc32 >> i) & 1) << (31 - i));
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	return value;
231962306a36Sopenharmony_ci}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci/*
232262306a36Sopenharmony_ci * Sets the bit in the multicast table corresponding to the hash value.
232362306a36Sopenharmony_ci *
232462306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
232562306a36Sopenharmony_ci * hash_value - Multicast address hash value
232662306a36Sopenharmony_ci */
232762306a36Sopenharmony_cistatic void atl2_hash_set(struct atl2_hw *hw, u32 hash_value)
232862306a36Sopenharmony_ci{
232962306a36Sopenharmony_ci	u32 hash_bit, hash_reg;
233062306a36Sopenharmony_ci	u32 mta;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	/* The HASH Table  is a register array of 2 32-bit registers.
233362306a36Sopenharmony_ci	 * It is treated like an array of 64 bits.  We want to set
233462306a36Sopenharmony_ci	 * bit BitArray[hash_value]. So we figure out what register
233562306a36Sopenharmony_ci	 * the bit is in, read it, OR in the new bit, then write
233662306a36Sopenharmony_ci	 * back the new value.  The register is determined by the
233762306a36Sopenharmony_ci	 * upper 7 bits of the hash value and the bit within that
233862306a36Sopenharmony_ci	 * register are determined by the lower 5 bits of the value.
233962306a36Sopenharmony_ci	 */
234062306a36Sopenharmony_ci	hash_reg = (hash_value >> 31) & 0x1;
234162306a36Sopenharmony_ci	hash_bit = (hash_value >> 26) & 0x1F;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	mta |= (1 << hash_bit);
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
234862306a36Sopenharmony_ci}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci/*
235162306a36Sopenharmony_ci * atl2_init_pcie - init PCIE module
235262306a36Sopenharmony_ci */
235362306a36Sopenharmony_cistatic void atl2_init_pcie(struct atl2_hw *hw)
235462306a36Sopenharmony_ci{
235562306a36Sopenharmony_ci    u32 value;
235662306a36Sopenharmony_ci    value = LTSSM_TEST_MODE_DEF;
235762306a36Sopenharmony_ci    ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci    value = PCIE_DLL_TX_CTRL1_DEF;
236062306a36Sopenharmony_ci    ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value);
236162306a36Sopenharmony_ci}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_cistatic void atl2_init_flash_opcode(struct atl2_hw *hw)
236462306a36Sopenharmony_ci{
236562306a36Sopenharmony_ci	if (hw->flash_vendor >= ARRAY_SIZE(flash_table))
236662306a36Sopenharmony_ci		hw->flash_vendor = 0; /* ATMEL */
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	/* Init OP table */
236962306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM,
237062306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdPROGRAM);
237162306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE,
237262306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdSECTOR_ERASE);
237362306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE,
237462306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdCHIP_ERASE);
237562306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID,
237662306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdRDID);
237762306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN,
237862306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdWREN);
237962306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR,
238062306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdRDSR);
238162306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR,
238262306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdWRSR);
238362306a36Sopenharmony_ci	ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ,
238462306a36Sopenharmony_ci		flash_table[hw->flash_vendor].cmdREAD);
238562306a36Sopenharmony_ci}
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci/********************************************************************
238862306a36Sopenharmony_ci* Performs basic configuration of the adapter.
238962306a36Sopenharmony_ci*
239062306a36Sopenharmony_ci* hw - Struct containing variables accessed by shared code
239162306a36Sopenharmony_ci* Assumes that the controller has previously been reset and is in a
239262306a36Sopenharmony_ci* post-reset uninitialized state. Initializes multicast table,
239362306a36Sopenharmony_ci* and  Calls routines to setup link
239462306a36Sopenharmony_ci* Leaves the transmit and receive units disabled and uninitialized.
239562306a36Sopenharmony_ci********************************************************************/
239662306a36Sopenharmony_cistatic s32 atl2_init_hw(struct atl2_hw *hw)
239762306a36Sopenharmony_ci{
239862306a36Sopenharmony_ci	u32 ret_val = 0;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	atl2_init_pcie(hw);
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	/* Zero out the Multicast HASH table */
240362306a36Sopenharmony_ci	/* clear the old settings from the multicast hash table */
240462306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
240562306a36Sopenharmony_ci	ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	atl2_init_flash_opcode(hw);
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	ret_val = atl2_phy_init(hw);
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	return ret_val;
241262306a36Sopenharmony_ci}
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci/*
241562306a36Sopenharmony_ci * Detects the current speed and duplex settings of the hardware.
241662306a36Sopenharmony_ci *
241762306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
241862306a36Sopenharmony_ci * speed - Speed of the connection
241962306a36Sopenharmony_ci * duplex - Duplex setting of the connection
242062306a36Sopenharmony_ci */
242162306a36Sopenharmony_cistatic s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed,
242262306a36Sopenharmony_ci	u16 *duplex)
242362306a36Sopenharmony_ci{
242462306a36Sopenharmony_ci	s32 ret_val;
242562306a36Sopenharmony_ci	u16 phy_data;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	/* Read PHY Specific Status Register (17) */
242862306a36Sopenharmony_ci	ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data);
242962306a36Sopenharmony_ci	if (ret_val)
243062306a36Sopenharmony_ci		return ret_val;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED))
243362306a36Sopenharmony_ci		return ATLX_ERR_PHY_RES;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	switch (phy_data & MII_ATLX_PSSR_SPEED) {
243662306a36Sopenharmony_ci	case MII_ATLX_PSSR_100MBS:
243762306a36Sopenharmony_ci		*speed = SPEED_100;
243862306a36Sopenharmony_ci		break;
243962306a36Sopenharmony_ci	case MII_ATLX_PSSR_10MBS:
244062306a36Sopenharmony_ci		*speed = SPEED_10;
244162306a36Sopenharmony_ci		break;
244262306a36Sopenharmony_ci	default:
244362306a36Sopenharmony_ci		return ATLX_ERR_PHY_SPEED;
244462306a36Sopenharmony_ci	}
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	if (phy_data & MII_ATLX_PSSR_DPLX)
244762306a36Sopenharmony_ci		*duplex = FULL_DUPLEX;
244862306a36Sopenharmony_ci	else
244962306a36Sopenharmony_ci		*duplex = HALF_DUPLEX;
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	return 0;
245262306a36Sopenharmony_ci}
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci/*
245562306a36Sopenharmony_ci * Reads the value from a PHY register
245662306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
245762306a36Sopenharmony_ci * reg_addr - address of the PHY register to read
245862306a36Sopenharmony_ci */
245962306a36Sopenharmony_cistatic s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data)
246062306a36Sopenharmony_ci{
246162306a36Sopenharmony_ci	u32 val;
246262306a36Sopenharmony_ci	int i;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
246562306a36Sopenharmony_ci		MDIO_START |
246662306a36Sopenharmony_ci		MDIO_SUP_PREAMBLE |
246762306a36Sopenharmony_ci		MDIO_RW |
246862306a36Sopenharmony_ci		MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
246962306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val);
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	wmb();
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
247462306a36Sopenharmony_ci		udelay(2);
247562306a36Sopenharmony_ci		val = ATL2_READ_REG(hw, REG_MDIO_CTRL);
247662306a36Sopenharmony_ci		if (!(val & (MDIO_START | MDIO_BUSY)))
247762306a36Sopenharmony_ci			break;
247862306a36Sopenharmony_ci		wmb();
247962306a36Sopenharmony_ci	}
248062306a36Sopenharmony_ci	if (!(val & (MDIO_START | MDIO_BUSY))) {
248162306a36Sopenharmony_ci		*phy_data = (u16)val;
248262306a36Sopenharmony_ci		return 0;
248362306a36Sopenharmony_ci	}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	return ATLX_ERR_PHY;
248662306a36Sopenharmony_ci}
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci/*
248962306a36Sopenharmony_ci * Writes a value to a PHY register
249062306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
249162306a36Sopenharmony_ci * reg_addr - address of the PHY register to write
249262306a36Sopenharmony_ci * data - data to write to the PHY
249362306a36Sopenharmony_ci */
249462306a36Sopenharmony_cistatic s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data)
249562306a36Sopenharmony_ci{
249662306a36Sopenharmony_ci	int i;
249762306a36Sopenharmony_ci	u32 val;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
250062306a36Sopenharmony_ci		(reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
250162306a36Sopenharmony_ci		MDIO_SUP_PREAMBLE |
250262306a36Sopenharmony_ci		MDIO_START |
250362306a36Sopenharmony_ci		MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
250462306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val);
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	wmb();
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
250962306a36Sopenharmony_ci		udelay(2);
251062306a36Sopenharmony_ci		val = ATL2_READ_REG(hw, REG_MDIO_CTRL);
251162306a36Sopenharmony_ci		if (!(val & (MDIO_START | MDIO_BUSY)))
251262306a36Sopenharmony_ci			break;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci		wmb();
251562306a36Sopenharmony_ci	}
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	if (!(val & (MDIO_START | MDIO_BUSY)))
251862306a36Sopenharmony_ci		return 0;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	return ATLX_ERR_PHY;
252162306a36Sopenharmony_ci}
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci/*
252462306a36Sopenharmony_ci * Configures PHY autoneg and flow control advertisement settings
252562306a36Sopenharmony_ci *
252662306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
252762306a36Sopenharmony_ci */
252862306a36Sopenharmony_cistatic s32 atl2_phy_setup_autoneg_adv(struct atl2_hw *hw)
252962306a36Sopenharmony_ci{
253062306a36Sopenharmony_ci	s16 mii_autoneg_adv_reg;
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	/* Read the MII Auto-Neg Advertisement Register (Address 4). */
253362306a36Sopenharmony_ci	mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	/* Need to parse autoneg_advertised  and set up
253662306a36Sopenharmony_ci	 * the appropriate PHY registers.  First we will parse for
253762306a36Sopenharmony_ci	 * autoneg_advertised software override.  Since we can advertise
253862306a36Sopenharmony_ci	 * a plethora of combinations, we need to check each bit
253962306a36Sopenharmony_ci	 * individually.
254062306a36Sopenharmony_ci	 */
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	/* First we clear all the 10/100 mb speed bits in the Auto-Neg
254362306a36Sopenharmony_ci	 * Advertisement Register (Address 4) and the 1000 mb speed bits in
254462306a36Sopenharmony_ci	 * the  1000Base-T Control Register (Address 9). */
254562306a36Sopenharmony_ci	mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	/* Need to parse MediaType and setup the
254862306a36Sopenharmony_ci	 * appropriate PHY registers. */
254962306a36Sopenharmony_ci	switch (hw->MediaType) {
255062306a36Sopenharmony_ci	case MEDIA_TYPE_AUTO_SENSOR:
255162306a36Sopenharmony_ci		mii_autoneg_adv_reg |=
255262306a36Sopenharmony_ci			(MII_AR_10T_HD_CAPS |
255362306a36Sopenharmony_ci			MII_AR_10T_FD_CAPS  |
255462306a36Sopenharmony_ci			MII_AR_100TX_HD_CAPS|
255562306a36Sopenharmony_ci			MII_AR_100TX_FD_CAPS);
255662306a36Sopenharmony_ci		hw->autoneg_advertised =
255762306a36Sopenharmony_ci			ADVERTISE_10_HALF |
255862306a36Sopenharmony_ci			ADVERTISE_10_FULL |
255962306a36Sopenharmony_ci			ADVERTISE_100_HALF|
256062306a36Sopenharmony_ci			ADVERTISE_100_FULL;
256162306a36Sopenharmony_ci		break;
256262306a36Sopenharmony_ci	case MEDIA_TYPE_100M_FULL:
256362306a36Sopenharmony_ci		mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS;
256462306a36Sopenharmony_ci		hw->autoneg_advertised = ADVERTISE_100_FULL;
256562306a36Sopenharmony_ci		break;
256662306a36Sopenharmony_ci	case MEDIA_TYPE_100M_HALF:
256762306a36Sopenharmony_ci		mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS;
256862306a36Sopenharmony_ci		hw->autoneg_advertised = ADVERTISE_100_HALF;
256962306a36Sopenharmony_ci		break;
257062306a36Sopenharmony_ci	case MEDIA_TYPE_10M_FULL:
257162306a36Sopenharmony_ci		mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS;
257262306a36Sopenharmony_ci		hw->autoneg_advertised = ADVERTISE_10_FULL;
257362306a36Sopenharmony_ci		break;
257462306a36Sopenharmony_ci	default:
257562306a36Sopenharmony_ci		mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS;
257662306a36Sopenharmony_ci		hw->autoneg_advertised = ADVERTISE_10_HALF;
257762306a36Sopenharmony_ci		break;
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	/* flow control fixed to enable all */
258162306a36Sopenharmony_ci	mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	return atl2_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
258662306a36Sopenharmony_ci}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci/*
258962306a36Sopenharmony_ci * Resets the PHY and make all config validate
259062306a36Sopenharmony_ci *
259162306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code
259262306a36Sopenharmony_ci *
259362306a36Sopenharmony_ci * Sets bit 15 and 12 of the MII Control regiser (for F001 bug)
259462306a36Sopenharmony_ci */
259562306a36Sopenharmony_cistatic s32 atl2_phy_commit(struct atl2_hw *hw)
259662306a36Sopenharmony_ci{
259762306a36Sopenharmony_ci	s32 ret_val;
259862306a36Sopenharmony_ci	u16 phy_data;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG;
260162306a36Sopenharmony_ci	ret_val = atl2_write_phy_reg(hw, MII_BMCR, phy_data);
260262306a36Sopenharmony_ci	if (ret_val) {
260362306a36Sopenharmony_ci		u32 val;
260462306a36Sopenharmony_ci		int i;
260562306a36Sopenharmony_ci		/* pcie serdes link may be down ! */
260662306a36Sopenharmony_ci		for (i = 0; i < 25; i++) {
260762306a36Sopenharmony_ci			msleep(1);
260862306a36Sopenharmony_ci			val = ATL2_READ_REG(hw, REG_MDIO_CTRL);
260962306a36Sopenharmony_ci			if (!(val & (MDIO_START | MDIO_BUSY)))
261062306a36Sopenharmony_ci				break;
261162306a36Sopenharmony_ci		}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci		if (0 != (val & (MDIO_START | MDIO_BUSY))) {
261462306a36Sopenharmony_ci			printk(KERN_ERR "atl2: PCIe link down for at least 25ms !\n");
261562306a36Sopenharmony_ci			return ret_val;
261662306a36Sopenharmony_ci		}
261762306a36Sopenharmony_ci	}
261862306a36Sopenharmony_ci	return 0;
261962306a36Sopenharmony_ci}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_cistatic s32 atl2_phy_init(struct atl2_hw *hw)
262262306a36Sopenharmony_ci{
262362306a36Sopenharmony_ci	s32 ret_val;
262462306a36Sopenharmony_ci	u16 phy_val;
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	if (hw->phy_configured)
262762306a36Sopenharmony_ci		return 0;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	/* Enable PHY */
263062306a36Sopenharmony_ci	ATL2_WRITE_REGW(hw, REG_PHY_ENABLE, 1);
263162306a36Sopenharmony_ci	ATL2_WRITE_FLUSH(hw);
263262306a36Sopenharmony_ci	msleep(1);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	/* check if the PHY is in powersaving mode */
263562306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_ADDR, 0);
263662306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val);
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	/* 024E / 124E 0r 0274 / 1274 ? */
263962306a36Sopenharmony_ci	if (phy_val & 0x1000) {
264062306a36Sopenharmony_ci		phy_val &= ~0x1000;
264162306a36Sopenharmony_ci		atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val);
264262306a36Sopenharmony_ci	}
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	msleep(1);
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	/*Enable PHY LinkChange Interrupt */
264762306a36Sopenharmony_ci	ret_val = atl2_write_phy_reg(hw, 18, 0xC00);
264862306a36Sopenharmony_ci	if (ret_val)
264962306a36Sopenharmony_ci		return ret_val;
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	/* setup AutoNeg parameters */
265262306a36Sopenharmony_ci	ret_val = atl2_phy_setup_autoneg_adv(hw);
265362306a36Sopenharmony_ci	if (ret_val)
265462306a36Sopenharmony_ci		return ret_val;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	/* SW.Reset & En-Auto-Neg to restart Auto-Neg */
265762306a36Sopenharmony_ci	ret_val = atl2_phy_commit(hw);
265862306a36Sopenharmony_ci	if (ret_val)
265962306a36Sopenharmony_ci		return ret_val;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	hw->phy_configured = true;
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	return ret_val;
266462306a36Sopenharmony_ci}
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_cistatic void atl2_set_mac_addr(struct atl2_hw *hw)
266762306a36Sopenharmony_ci{
266862306a36Sopenharmony_ci	u32 value;
266962306a36Sopenharmony_ci	/* 00-0B-6A-F6-00-DC
267062306a36Sopenharmony_ci	 * 0:  6AF600DC   1: 000B
267162306a36Sopenharmony_ci	 * low dword */
267262306a36Sopenharmony_ci	value = (((u32)hw->mac_addr[2]) << 24) |
267362306a36Sopenharmony_ci		(((u32)hw->mac_addr[3]) << 16) |
267462306a36Sopenharmony_ci		(((u32)hw->mac_addr[4]) << 8)  |
267562306a36Sopenharmony_ci		(((u32)hw->mac_addr[5]));
267662306a36Sopenharmony_ci	ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
267762306a36Sopenharmony_ci	/* hight dword */
267862306a36Sopenharmony_ci	value = (((u32)hw->mac_addr[0]) << 8) |
267962306a36Sopenharmony_ci		(((u32)hw->mac_addr[1]));
268062306a36Sopenharmony_ci	ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
268162306a36Sopenharmony_ci}
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci/*
268462306a36Sopenharmony_ci * check_eeprom_exist
268562306a36Sopenharmony_ci * return 0 if eeprom exist
268662306a36Sopenharmony_ci */
268762306a36Sopenharmony_cistatic int atl2_check_eeprom_exist(struct atl2_hw *hw)
268862306a36Sopenharmony_ci{
268962306a36Sopenharmony_ci	u32 value;
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL);
269262306a36Sopenharmony_ci	if (value & SPI_FLASH_CTRL_EN_VPD) {
269362306a36Sopenharmony_ci		value &= ~SPI_FLASH_CTRL_EN_VPD;
269462306a36Sopenharmony_ci		ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
269562306a36Sopenharmony_ci	}
269662306a36Sopenharmony_ci	value = ATL2_READ_REGW(hw, REG_PCIE_CAP_LIST);
269762306a36Sopenharmony_ci	return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
269862306a36Sopenharmony_ci}
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci/* FIXME: This doesn't look right. -- CHS */
270162306a36Sopenharmony_cistatic bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value)
270262306a36Sopenharmony_ci{
270362306a36Sopenharmony_ci	return true;
270462306a36Sopenharmony_ci}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_cistatic bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue)
270762306a36Sopenharmony_ci{
270862306a36Sopenharmony_ci	int i;
270962306a36Sopenharmony_ci	u32    Control;
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	if (Offset & 0x3)
271262306a36Sopenharmony_ci		return false; /* address do not align */
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_VPD_DATA, 0);
271562306a36Sopenharmony_ci	Control = (Offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
271662306a36Sopenharmony_ci	ATL2_WRITE_REG(hw, REG_VPD_CAP, Control);
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
271962306a36Sopenharmony_ci		msleep(2);
272062306a36Sopenharmony_ci		Control = ATL2_READ_REG(hw, REG_VPD_CAP);
272162306a36Sopenharmony_ci		if (Control & VPD_CAP_VPD_FLAG)
272262306a36Sopenharmony_ci			break;
272362306a36Sopenharmony_ci	}
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	if (Control & VPD_CAP_VPD_FLAG) {
272662306a36Sopenharmony_ci		*pValue = ATL2_READ_REG(hw, REG_VPD_DATA);
272762306a36Sopenharmony_ci		return true;
272862306a36Sopenharmony_ci	}
272962306a36Sopenharmony_ci	return false; /* timeout */
273062306a36Sopenharmony_ci}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_cistatic void atl2_force_ps(struct atl2_hw *hw)
273362306a36Sopenharmony_ci{
273462306a36Sopenharmony_ci	u16 phy_val;
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_ADDR, 0);
273762306a36Sopenharmony_ci	atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val);
273862306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val | 0x1000);
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_ADDR, 2);
274162306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_DATA, 0x3000);
274262306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_ADDR, 3);
274362306a36Sopenharmony_ci	atl2_write_phy_reg(hw, MII_DBG_DATA, 0);
274462306a36Sopenharmony_ci}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci/* This is the only thing that needs to be changed to adjust the
274762306a36Sopenharmony_ci * maximum number of ports that the driver can manage.
274862306a36Sopenharmony_ci */
274962306a36Sopenharmony_ci#define ATL2_MAX_NIC 4
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci#define OPTION_UNSET    -1
275262306a36Sopenharmony_ci#define OPTION_DISABLED 0
275362306a36Sopenharmony_ci#define OPTION_ENABLED  1
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci/* All parameters are treated the same, as an integer array of values.
275662306a36Sopenharmony_ci * This macro just reduces the need to repeat the same declaration code
275762306a36Sopenharmony_ci * over and over (plus this helps to avoid typo bugs).
275862306a36Sopenharmony_ci */
275962306a36Sopenharmony_ci#define ATL2_PARAM_INIT {[0 ... ATL2_MAX_NIC] = OPTION_UNSET}
276062306a36Sopenharmony_ci#ifndef module_param_array
276162306a36Sopenharmony_ci/* Module Parameters are always initialized to -1, so that the driver
276262306a36Sopenharmony_ci * can tell the difference between no user specified value or the
276362306a36Sopenharmony_ci * user asking for the default value.
276462306a36Sopenharmony_ci * The true default values are loaded in when atl2_check_options is called.
276562306a36Sopenharmony_ci *
276662306a36Sopenharmony_ci * This is a GCC extension to ANSI C.
276762306a36Sopenharmony_ci * See the item "Labeled Elements in Initializers" in the section
276862306a36Sopenharmony_ci * "Extensions to the C Language Family" of the GCC documentation.
276962306a36Sopenharmony_ci */
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci#define ATL2_PARAM(X, desc) \
277262306a36Sopenharmony_ci    static const int X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \
277362306a36Sopenharmony_ci    MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \
277462306a36Sopenharmony_ci    MODULE_PARM_DESC(X, desc);
277562306a36Sopenharmony_ci#else
277662306a36Sopenharmony_ci#define ATL2_PARAM(X, desc) \
277762306a36Sopenharmony_ci    static int X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \
277862306a36Sopenharmony_ci    static unsigned int num_##X; \
277962306a36Sopenharmony_ci    module_param_array_named(X, X, int, &num_##X, 0); \
278062306a36Sopenharmony_ci    MODULE_PARM_DESC(X, desc);
278162306a36Sopenharmony_ci#endif
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci/*
278462306a36Sopenharmony_ci * Transmit Memory Size
278562306a36Sopenharmony_ci * Valid Range: 64-2048
278662306a36Sopenharmony_ci * Default Value: 128
278762306a36Sopenharmony_ci */
278862306a36Sopenharmony_ci#define ATL2_MIN_TX_MEMSIZE		4	/* 4KB */
278962306a36Sopenharmony_ci#define ATL2_MAX_TX_MEMSIZE		64	/* 64KB */
279062306a36Sopenharmony_ci#define ATL2_DEFAULT_TX_MEMSIZE		8	/* 8KB */
279162306a36Sopenharmony_ciATL2_PARAM(TxMemSize, "Bytes of Transmit Memory");
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci/*
279462306a36Sopenharmony_ci * Receive Memory Block Count
279562306a36Sopenharmony_ci * Valid Range: 16-512
279662306a36Sopenharmony_ci * Default Value: 128
279762306a36Sopenharmony_ci */
279862306a36Sopenharmony_ci#define ATL2_MIN_RXD_COUNT		16
279962306a36Sopenharmony_ci#define ATL2_MAX_RXD_COUNT		512
280062306a36Sopenharmony_ci#define ATL2_DEFAULT_RXD_COUNT		64
280162306a36Sopenharmony_ciATL2_PARAM(RxMemBlock, "Number of receive memory block");
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci/*
280462306a36Sopenharmony_ci * User Specified MediaType Override
280562306a36Sopenharmony_ci *
280662306a36Sopenharmony_ci * Valid Range: 0-5
280762306a36Sopenharmony_ci *  - 0    - auto-negotiate at all supported speeds
280862306a36Sopenharmony_ci *  - 1    - only link at 1000Mbps Full Duplex
280962306a36Sopenharmony_ci *  - 2    - only link at 100Mbps Full Duplex
281062306a36Sopenharmony_ci *  - 3    - only link at 100Mbps Half Duplex
281162306a36Sopenharmony_ci *  - 4    - only link at 10Mbps Full Duplex
281262306a36Sopenharmony_ci *  - 5    - only link at 10Mbps Half Duplex
281362306a36Sopenharmony_ci * Default Value: 0
281462306a36Sopenharmony_ci */
281562306a36Sopenharmony_ciATL2_PARAM(MediaType, "MediaType Select");
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci/*
281862306a36Sopenharmony_ci * Interrupt Moderate Timer in units of 2048 ns (~2 us)
281962306a36Sopenharmony_ci * Valid Range: 10-65535
282062306a36Sopenharmony_ci * Default Value: 45000(90ms)
282162306a36Sopenharmony_ci */
282262306a36Sopenharmony_ci#define INT_MOD_DEFAULT_CNT	100 /* 200us */
282362306a36Sopenharmony_ci#define INT_MOD_MAX_CNT		65000
282462306a36Sopenharmony_ci#define INT_MOD_MIN_CNT		50
282562306a36Sopenharmony_ciATL2_PARAM(IntModTimer, "Interrupt Moderator Timer");
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci/*
282862306a36Sopenharmony_ci * FlashVendor
282962306a36Sopenharmony_ci * Valid Range: 0-2
283062306a36Sopenharmony_ci * 0 - Atmel
283162306a36Sopenharmony_ci * 1 - SST
283262306a36Sopenharmony_ci * 2 - ST
283362306a36Sopenharmony_ci */
283462306a36Sopenharmony_ciATL2_PARAM(FlashVendor, "SPI Flash Vendor");
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci#define AUTONEG_ADV_DEFAULT	0x2F
283762306a36Sopenharmony_ci#define AUTONEG_ADV_MASK	0x2F
283862306a36Sopenharmony_ci#define FLOW_CONTROL_DEFAULT	FLOW_CONTROL_FULL
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci#define FLASH_VENDOR_DEFAULT	0
284162306a36Sopenharmony_ci#define FLASH_VENDOR_MIN	0
284262306a36Sopenharmony_ci#define FLASH_VENDOR_MAX	2
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_cistruct atl2_option {
284562306a36Sopenharmony_ci	enum { enable_option, range_option, list_option } type;
284662306a36Sopenharmony_ci	char *name;
284762306a36Sopenharmony_ci	char *err;
284862306a36Sopenharmony_ci	int  def;
284962306a36Sopenharmony_ci	union {
285062306a36Sopenharmony_ci		struct { /* range_option info */
285162306a36Sopenharmony_ci			int min;
285262306a36Sopenharmony_ci			int max;
285362306a36Sopenharmony_ci		} r;
285462306a36Sopenharmony_ci		struct { /* list_option info */
285562306a36Sopenharmony_ci			int nr;
285662306a36Sopenharmony_ci			struct atl2_opt_list { int i; char *str; } *p;
285762306a36Sopenharmony_ci		} l;
285862306a36Sopenharmony_ci	} arg;
285962306a36Sopenharmony_ci};
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_cistatic int atl2_validate_option(int *value, struct atl2_option *opt)
286262306a36Sopenharmony_ci{
286362306a36Sopenharmony_ci	int i;
286462306a36Sopenharmony_ci	struct atl2_opt_list *ent;
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	if (*value == OPTION_UNSET) {
286762306a36Sopenharmony_ci		*value = opt->def;
286862306a36Sopenharmony_ci		return 0;
286962306a36Sopenharmony_ci	}
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci	switch (opt->type) {
287262306a36Sopenharmony_ci	case enable_option:
287362306a36Sopenharmony_ci		switch (*value) {
287462306a36Sopenharmony_ci		case OPTION_ENABLED:
287562306a36Sopenharmony_ci			printk(KERN_INFO "%s Enabled\n", opt->name);
287662306a36Sopenharmony_ci			return 0;
287762306a36Sopenharmony_ci		case OPTION_DISABLED:
287862306a36Sopenharmony_ci			printk(KERN_INFO "%s Disabled\n", opt->name);
287962306a36Sopenharmony_ci			return 0;
288062306a36Sopenharmony_ci		}
288162306a36Sopenharmony_ci		break;
288262306a36Sopenharmony_ci	case range_option:
288362306a36Sopenharmony_ci		if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
288462306a36Sopenharmony_ci			printk(KERN_INFO "%s set to %i\n", opt->name, *value);
288562306a36Sopenharmony_ci			return 0;
288662306a36Sopenharmony_ci		}
288762306a36Sopenharmony_ci		break;
288862306a36Sopenharmony_ci	case list_option:
288962306a36Sopenharmony_ci		for (i = 0; i < opt->arg.l.nr; i++) {
289062306a36Sopenharmony_ci			ent = &opt->arg.l.p[i];
289162306a36Sopenharmony_ci			if (*value == ent->i) {
289262306a36Sopenharmony_ci				if (ent->str[0] != '\0')
289362306a36Sopenharmony_ci					printk(KERN_INFO "%s\n", ent->str);
289462306a36Sopenharmony_ci				return 0;
289562306a36Sopenharmony_ci			}
289662306a36Sopenharmony_ci		}
289762306a36Sopenharmony_ci		break;
289862306a36Sopenharmony_ci	default:
289962306a36Sopenharmony_ci		BUG();
290062306a36Sopenharmony_ci	}
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	printk(KERN_INFO "Invalid %s specified (%i) %s\n",
290362306a36Sopenharmony_ci		opt->name, *value, opt->err);
290462306a36Sopenharmony_ci	*value = opt->def;
290562306a36Sopenharmony_ci	return -1;
290662306a36Sopenharmony_ci}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci/**
290962306a36Sopenharmony_ci * atl2_check_options - Range Checking for Command Line Parameters
291062306a36Sopenharmony_ci * @adapter: board private structure
291162306a36Sopenharmony_ci *
291262306a36Sopenharmony_ci * This routine checks all command line parameters for valid user
291362306a36Sopenharmony_ci * input.  If an invalid value is given, or if no user specified
291462306a36Sopenharmony_ci * value exists, a default value is used.  The final value is stored
291562306a36Sopenharmony_ci * in a variable in the adapter structure.
291662306a36Sopenharmony_ci */
291762306a36Sopenharmony_cistatic void atl2_check_options(struct atl2_adapter *adapter)
291862306a36Sopenharmony_ci{
291962306a36Sopenharmony_ci	int val;
292062306a36Sopenharmony_ci	struct atl2_option opt;
292162306a36Sopenharmony_ci	int bd = adapter->bd_number;
292262306a36Sopenharmony_ci	if (bd >= ATL2_MAX_NIC) {
292362306a36Sopenharmony_ci		printk(KERN_NOTICE "Warning: no configuration for board #%i\n",
292462306a36Sopenharmony_ci			bd);
292562306a36Sopenharmony_ci		printk(KERN_NOTICE "Using defaults for all values\n");
292662306a36Sopenharmony_ci#ifndef module_param_array
292762306a36Sopenharmony_ci		bd = ATL2_MAX_NIC;
292862306a36Sopenharmony_ci#endif
292962306a36Sopenharmony_ci	}
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	/* Bytes of Transmit Memory */
293262306a36Sopenharmony_ci	opt.type = range_option;
293362306a36Sopenharmony_ci	opt.name = "Bytes of Transmit Memory";
293462306a36Sopenharmony_ci	opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_TX_MEMSIZE);
293562306a36Sopenharmony_ci	opt.def = ATL2_DEFAULT_TX_MEMSIZE;
293662306a36Sopenharmony_ci	opt.arg.r.min = ATL2_MIN_TX_MEMSIZE;
293762306a36Sopenharmony_ci	opt.arg.r.max = ATL2_MAX_TX_MEMSIZE;
293862306a36Sopenharmony_ci#ifdef module_param_array
293962306a36Sopenharmony_ci	if (num_TxMemSize > bd) {
294062306a36Sopenharmony_ci#endif
294162306a36Sopenharmony_ci		val = TxMemSize[bd];
294262306a36Sopenharmony_ci		atl2_validate_option(&val, &opt);
294362306a36Sopenharmony_ci		adapter->txd_ring_size = ((u32) val) * 1024;
294462306a36Sopenharmony_ci#ifdef module_param_array
294562306a36Sopenharmony_ci	} else
294662306a36Sopenharmony_ci		adapter->txd_ring_size = ((u32)opt.def) * 1024;
294762306a36Sopenharmony_ci#endif
294862306a36Sopenharmony_ci	/* txs ring size: */
294962306a36Sopenharmony_ci	adapter->txs_ring_size = adapter->txd_ring_size / 128;
295062306a36Sopenharmony_ci	if (adapter->txs_ring_size > 160)
295162306a36Sopenharmony_ci		adapter->txs_ring_size = 160;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* Receive Memory Block Count */
295462306a36Sopenharmony_ci	opt.type = range_option;
295562306a36Sopenharmony_ci	opt.name = "Number of receive memory block";
295662306a36Sopenharmony_ci	opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_RXD_COUNT);
295762306a36Sopenharmony_ci	opt.def = ATL2_DEFAULT_RXD_COUNT;
295862306a36Sopenharmony_ci	opt.arg.r.min = ATL2_MIN_RXD_COUNT;
295962306a36Sopenharmony_ci	opt.arg.r.max = ATL2_MAX_RXD_COUNT;
296062306a36Sopenharmony_ci#ifdef module_param_array
296162306a36Sopenharmony_ci	if (num_RxMemBlock > bd) {
296262306a36Sopenharmony_ci#endif
296362306a36Sopenharmony_ci		val = RxMemBlock[bd];
296462306a36Sopenharmony_ci		atl2_validate_option(&val, &opt);
296562306a36Sopenharmony_ci		adapter->rxd_ring_size = (u32)val;
296662306a36Sopenharmony_ci		/* FIXME */
296762306a36Sopenharmony_ci		/* ((u16)val)&~1; */	/* even number */
296862306a36Sopenharmony_ci#ifdef module_param_array
296962306a36Sopenharmony_ci	} else
297062306a36Sopenharmony_ci		adapter->rxd_ring_size = (u32)opt.def;
297162306a36Sopenharmony_ci#endif
297262306a36Sopenharmony_ci	/* init RXD Flow control value */
297362306a36Sopenharmony_ci	adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size / 8) * 7;
297462306a36Sopenharmony_ci	adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT / 8) >
297562306a36Sopenharmony_ci		(adapter->rxd_ring_size / 12) ? (ATL2_MIN_RXD_COUNT / 8) :
297662306a36Sopenharmony_ci		(adapter->rxd_ring_size / 12);
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci	/* Interrupt Moderate Timer */
297962306a36Sopenharmony_ci	opt.type = range_option;
298062306a36Sopenharmony_ci	opt.name = "Interrupt Moderate Timer";
298162306a36Sopenharmony_ci	opt.err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT);
298262306a36Sopenharmony_ci	opt.def = INT_MOD_DEFAULT_CNT;
298362306a36Sopenharmony_ci	opt.arg.r.min = INT_MOD_MIN_CNT;
298462306a36Sopenharmony_ci	opt.arg.r.max = INT_MOD_MAX_CNT;
298562306a36Sopenharmony_ci#ifdef module_param_array
298662306a36Sopenharmony_ci	if (num_IntModTimer > bd) {
298762306a36Sopenharmony_ci#endif
298862306a36Sopenharmony_ci		val = IntModTimer[bd];
298962306a36Sopenharmony_ci		atl2_validate_option(&val, &opt);
299062306a36Sopenharmony_ci		adapter->imt = (u16) val;
299162306a36Sopenharmony_ci#ifdef module_param_array
299262306a36Sopenharmony_ci	} else
299362306a36Sopenharmony_ci		adapter->imt = (u16)(opt.def);
299462306a36Sopenharmony_ci#endif
299562306a36Sopenharmony_ci	/* Flash Vendor */
299662306a36Sopenharmony_ci	opt.type = range_option;
299762306a36Sopenharmony_ci	opt.name = "SPI Flash Vendor";
299862306a36Sopenharmony_ci	opt.err = "using default of " __MODULE_STRING(FLASH_VENDOR_DEFAULT);
299962306a36Sopenharmony_ci	opt.def = FLASH_VENDOR_DEFAULT;
300062306a36Sopenharmony_ci	opt.arg.r.min = FLASH_VENDOR_MIN;
300162306a36Sopenharmony_ci	opt.arg.r.max = FLASH_VENDOR_MAX;
300262306a36Sopenharmony_ci#ifdef module_param_array
300362306a36Sopenharmony_ci	if (num_FlashVendor > bd) {
300462306a36Sopenharmony_ci#endif
300562306a36Sopenharmony_ci		val = FlashVendor[bd];
300662306a36Sopenharmony_ci		atl2_validate_option(&val, &opt);
300762306a36Sopenharmony_ci		adapter->hw.flash_vendor = (u8) val;
300862306a36Sopenharmony_ci#ifdef module_param_array
300962306a36Sopenharmony_ci	} else
301062306a36Sopenharmony_ci		adapter->hw.flash_vendor = (u8)(opt.def);
301162306a36Sopenharmony_ci#endif
301262306a36Sopenharmony_ci	/* MediaType */
301362306a36Sopenharmony_ci	opt.type = range_option;
301462306a36Sopenharmony_ci	opt.name = "Speed/Duplex Selection";
301562306a36Sopenharmony_ci	opt.err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR);
301662306a36Sopenharmony_ci	opt.def = MEDIA_TYPE_AUTO_SENSOR;
301762306a36Sopenharmony_ci	opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR;
301862306a36Sopenharmony_ci	opt.arg.r.max = MEDIA_TYPE_10M_HALF;
301962306a36Sopenharmony_ci#ifdef module_param_array
302062306a36Sopenharmony_ci	if (num_MediaType > bd) {
302162306a36Sopenharmony_ci#endif
302262306a36Sopenharmony_ci		val = MediaType[bd];
302362306a36Sopenharmony_ci		atl2_validate_option(&val, &opt);
302462306a36Sopenharmony_ci		adapter->hw.MediaType = (u16) val;
302562306a36Sopenharmony_ci#ifdef module_param_array
302662306a36Sopenharmony_ci	} else
302762306a36Sopenharmony_ci		adapter->hw.MediaType = (u16)(opt.def);
302862306a36Sopenharmony_ci#endif
302962306a36Sopenharmony_ci}
3030