Index: linux-2.6.26.noarch/drivers/net/Kconfig =================================================================== --- linux-2.6.26.noarch.orig/drivers/net/Kconfig +++ linux-2.6.26.noarch/drivers/net/Kconfig @@ -2375,6 +2375,17 @@ config ATL1E To compile this driver as a module, choose M here. The module will be called atl1e. + config ATL2 + tristate "Atheros L2 Fast Ethernet support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + select MII + help + This driver supports the Atheros L2 fast ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl2. + endif # NETDEV_1000 # Index: linux-2.6.26.noarch/drivers/net/Makefile =================================================================== --- linux-2.6.26.noarch.orig/drivers/net/Makefile +++ linux-2.6.26.noarch/drivers/net/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_CAN) += can/ obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_ATL1) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ +obj-$(CONFIG_ATL2) += atl2/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_TEHUTI) += tehuti.o Index: linux-2.6.26.noarch/drivers/net/atl2/Makefile =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ATL2) += atl2.o +atl2-y += atl2_main.o atl2_hw.o atl2_ethtool.o atl2_param.o Index: linux-2.6.26.noarch/drivers/net/atl2/atl2.h =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2.h @@ -0,0 +1,120 @@ +/* atl2.h -- atl2 driver definitions + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL2_H_ +#define _ATL2_H_ + +#include +#include + +#include "atl2_hw.h" + +struct atl2_ring_header { + /* pointer to the descriptor ring memory */ + void *desc; + /* physical adress of the descriptor ring */ + dma_addr_t dma; + /* length of descriptor ring in bytes */ + unsigned int size; +}; + +/* board specific private data structure */ +struct atl2_adapter { + /* OS defined structs */ + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; +#ifdef NETIF_F_HW_VLAN_TX + struct vlan_group *vlgrp; +#endif + u32 wol; + u16 link_speed; + u16 link_duplex; + + spinlock_t stats_lock; + spinlock_t tx_lock; + + struct work_struct reset_task; + struct work_struct link_chg_task; + struct timer_list watchdog_timer; + struct timer_list phy_config_timer; + + unsigned long cfg_phy; + bool mac_disabled; + + /* All Descriptor memory */ + dma_addr_t ring_dma; + void *ring_vir_addr; + int ring_size; + + tx_pkt_header_t *txd_ring; + dma_addr_t txd_dma; + + tx_pkt_status_t *txs_ring; + dma_addr_t txs_dma; + + rx_desc_t *rxd_ring; + dma_addr_t rxd_dma; + + u32 txd_ring_size; // bytes per unit + u32 txs_ring_size; // dwords per unit + u32 rxd_ring_size; // 1536bytes per unit + + // read /write ptr: + // host + u32 txd_write_ptr; + u32 txs_next_clear; + u32 rxd_read_ptr; + + // nic + atomic_t txd_read_ptr; + atomic_t txs_write_ptr; + u32 rxd_write_ptr; + + /* Interrupt Moderator timer ( 2us resolution) */ + u16 imt; + /* Interrupt Clear timer (2us resolution) */ + u16 ict; + + unsigned long flags; + /* structs defined in atl2_hw.h */ + u32 bd_number; // board number; + bool pci_using_64; + bool have_msi; + struct atl2_hw hw; + + u32 usr_cmd; +// u32 regs_buff[ATL2_REGS_LEN]; + u32 pci_state[16]; + + u32 *config_space; +}; + +enum atl2_state_t { + __ATL2_TESTING, + __ATL2_RESETTING, + __ATL2_DOWN +}; + +#endif /* _ATL2_H_ */ Index: linux-2.6.26.noarch/drivers/net/atl2/atl2_ethtool.c =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2_ethtool.c @@ -0,0 +1,416 @@ +/* atl2_ethtool.c -- atl2 ethtool support + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "atl2.h" +#include "atl2_hw.h" + +extern char atl2_driver_name[]; +extern char atl2_driver_version[]; + +extern int atl2_up(struct atl2_adapter *adapter); +extern void atl2_down(struct atl2_adapter *adapter); +extern void atl2_reinit_locked(struct atl2_adapter *adapter); +extern s32 atl2_reset_hw(struct atl2_hw *hw); + +static int +atl2_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); + ecmd->advertising = ADVERTISED_TP; + + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->advertising |= hw->autoneg_advertised; + + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->transceiver = XCVR_INTERNAL; + + if (adapter->link_speed != SPEED_0) { + ecmd->speed = adapter->link_speed; + if (adapter->link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = AUTONEG_ENABLE; + return 0; +} + +static int +atl2_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + + while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) + msleep(1); + + if (ecmd->autoneg == AUTONEG_ENABLE) { +#define MY_ADV_MASK (ADVERTISE_10_HALF| \ + ADVERTISE_10_FULL| \ + ADVERTISE_100_HALF| \ + ADVERTISE_100_FULL) + + if ((ecmd->advertising&MY_ADV_MASK) == MY_ADV_MASK) { + hw->MediaType = MEDIA_TYPE_AUTO_SENSOR; + hw->autoneg_advertised = MY_ADV_MASK; + } else if ((ecmd->advertising&MY_ADV_MASK) == ADVERTISE_100_FULL) { + hw->MediaType = MEDIA_TYPE_100M_FULL; + hw->autoneg_advertised = ADVERTISE_100_FULL; + } else if ((ecmd->advertising&MY_ADV_MASK) == ADVERTISE_100_HALF) { + hw->MediaType = MEDIA_TYPE_100M_HALF; + hw->autoneg_advertised = ADVERTISE_100_HALF; + } else if ((ecmd->advertising&MY_ADV_MASK) == ADVERTISE_10_FULL) { + hw->MediaType = MEDIA_TYPE_10M_FULL; + hw->autoneg_advertised = ADVERTISE_10_FULL; + } else if ((ecmd->advertising&MY_ADV_MASK) == ADVERTISE_10_HALF) { + hw->MediaType = MEDIA_TYPE_10M_HALF; + hw->autoneg_advertised = ADVERTISE_10_HALF; + } else { + clear_bit(__ATL2_RESETTING, &adapter->flags); + return -EINVAL; + } + ecmd->advertising = hw->autoneg_advertised | + ADVERTISED_TP | ADVERTISED_Autoneg; + } else { + clear_bit(__ATL2_RESETTING, &adapter->flags); + return -EINVAL; + } + + /* reset the link */ + if (netif_running(adapter->netdev)) { + atl2_down(adapter); + atl2_up(adapter); + } else + atl2_reset_hw(&adapter->hw); + + clear_bit(__ATL2_RESETTING, &adapter->flags); + return 0; +} + +static u32 +atl2_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} + +static u32 +atl2_get_msglevel(struct net_device *netdev) +{ + return 0; +} + +/* + * It's sane for this to be empty, but we might want to take advantage of this. + */ +static void +atl2_set_msglevel(struct net_device *netdev, u32 data) +{ +} + +static int +atl2_get_regs_len(struct net_device *netdev) +{ +#define ATL2_REGS_LEN 42 + return ATL2_REGS_LEN * sizeof(u32); +} + +static void +atl2_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u32 *regs_buff = p; + u16 phy_data; + + memset(p, 0, ATL2_REGS_LEN * sizeof(u32)); + + regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; + + regs_buff[0] = ATL2_READ_REG(hw, REG_VPD_CAP); + regs_buff[1] = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); + regs_buff[2] = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG); + regs_buff[3] = ATL2_READ_REG(hw, REG_TWSI_CTRL); + regs_buff[4] = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); + regs_buff[5] = ATL2_READ_REG(hw, REG_MASTER_CTRL); + regs_buff[6] = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT); + regs_buff[7] = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); + regs_buff[8] = ATL2_READ_REG(hw, REG_PHY_ENABLE); + regs_buff[9] = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER); + regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS); + regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL); + regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK); + regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL); + regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG); + regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); + regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4); + regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE); + regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4); + regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); + regs_buff[20] = ATL2_READ_REG(hw, REG_MTU); + regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL); + regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END); + regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI); + regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO); + regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE); + regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO); + regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE); + regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO); + regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM); + regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR); + regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH); + regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW); + regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH); + regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH); + regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX); + regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX); + regs_buff[38] = ATL2_READ_REG(hw, REG_ISR); + regs_buff[39] = ATL2_READ_REG(hw, REG_IMR); + + atl2_read_phy_reg(hw, MII_BMCR, &phy_data); + regs_buff[40] = (u32)phy_data; + atl2_read_phy_reg(hw, MII_BMSR, &phy_data); + regs_buff[41] = (u32)phy_data; +} + +static int +atl2_get_eeprom_len(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + if (!atl2_check_eeprom_exist(&adapter->hw)) { + return 512; + } else + return 0; +} + +static int +atl2_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u32 *eeprom_buff; + int first_dword, last_dword; + int ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (atl2_check_eeprom_exist(hw)) { + return -EINVAL; + } + + eeprom->magic = hw->vendor_id | (hw->device_id << 16); + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32) * (last_dword - first_dword + 1), GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + for (i=first_dword; i < last_dword; i++) { + if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) + return -EIO; + } + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; +} + +static int +atl2_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + u32 *eeprom_buff; + u32 *ptr; + int max_len, first_dword, last_dword, ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EOPNOTSUPP; + + if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) + return -EFAULT; + + max_len = 512; + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + eeprom_buff = kmalloc(max_len, GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + ptr = (u32 *)eeprom_buff; + + if (eeprom->offset & 3) { + /* need read/modify/write of first changed EEPROM word */ + /* only the second byte of the word is being modified */ + if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) + return -EIO; + ptr++; + } + if (((eeprom->offset + eeprom->len) & 3) ) { + /* need read/modify/write of last changed EEPROM word */ + /* only the first byte of the word is being modified */ + + if (!atl2_read_eeprom(hw, last_dword*4, &(eeprom_buff[last_dword - first_dword]))) + return -EIO; + } + + /* Device's eeprom is always little-endian, word addressable */ + memcpy(ptr, bytes, eeprom->len); + + for (i = 0; i < last_dword - first_dword + 1; i++) { + if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) + return -EIO; + } + + kfree(eeprom_buff); + return ret_val; +} + +static void +atl2_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + strncpy(drvinfo->driver, atl2_driver_name, 32); + strncpy(drvinfo->version, atl2_driver_version, 32); + strncpy(drvinfo->fw_version, "L2", 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = atl2_get_regs_len(netdev); + drvinfo->eedump_len = atl2_get_eeprom_len(netdev); +} + +static void +atl2_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + if (adapter->wol & ATL2_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if (adapter->wol & ATL2_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if (adapter->wol & ATL2_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if (adapter->wol & ATL2_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + if (adapter->wol & ATL2_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; +} + +static int +atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)) + return -EOPNOTSUPP; + + if (wol->wolopts & (WAKE_MCAST|WAKE_BCAST|WAKE_MCAST)) + return -EOPNOTSUPP; + + /* these settings will always override what we currently have */ + adapter->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + adapter->wol |= ATL2_WUFC_MAG; + if (wol->wolopts & WAKE_PHY) + adapter->wol |= ATL2_WUFC_LNKC; + + return 0; +} + +static int +atl2_nway_reset(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + if (netif_running(netdev)) + atl2_reinit_locked(adapter); + return 0; +} + +static struct ethtool_ops atl2_ethtool_ops = { + .get_settings = atl2_get_settings, + .set_settings = atl2_set_settings, + .get_drvinfo = atl2_get_drvinfo, + .get_regs_len = atl2_get_regs_len, + .get_regs = atl2_get_regs, + .get_wol = atl2_get_wol, + .set_wol = atl2_set_wol, + .get_msglevel = atl2_get_msglevel, + .set_msglevel = atl2_set_msglevel, + .nway_reset = atl2_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = atl2_get_eeprom_len, + .get_eeprom = atl2_get_eeprom, + .set_eeprom = atl2_set_eeprom, + .get_tx_csum = atl2_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#ifdef NETIF_F_TSO + .get_tso = ethtool_op_get_tso, +#endif +#if 0 //FIXME: not implemented? +//#ifdef ETHTOOL_GPERMADDR + .get_perm_addr = ethtool_op_get_perm_addr, +#endif +}; + +void +atl2_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops); +} Index: linux-2.6.26.noarch/drivers/net/atl2/atl2_hw.c =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2_hw.c @@ -0,0 +1,760 @@ +/* atl2_hw.c -- atl2 hardware control functions + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include "atl2.h" +#include "atl2_hw.h" + +#define LBYTESWAP( a ) ( ( ( (a) & 0x00ff00ff ) << 8 ) | ( ( (a) & 0xff00ff00 ) >> 8 ) ) +#define LONGSWAP( a ) ( ( LBYTESWAP( a ) << 16 ) | ( LBYTESWAP( a ) >> 16 ) ) +#define SHORTSWAP( a ) ( ( (a) << 8 ) | ( (a) >> 8 ) ) + +/* + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + * return : ATL2_SUCCESS or idle status (if error) + */ +s32 +atl2_reset_hw(struct atl2_hw *hw) +{ + u32 icr; + u16 pci_cfg_cmd_word; + int i; + + /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ + atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); + if ((pci_cfg_cmd_word & + (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) != + (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) { + pci_cfg_cmd_word |= + (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER); + atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); + } + + /* Clear Interrupt mask to stop board from generating + * interrupts & Clear any pending interrupt events + */ +// ATL2_WRITE_REG(hw, REG_IMR, 0); +// ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); + + /* Issue Soft Reset to the MAC. This will reset the chip's + * transmit, receive, DMA. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); + wmb(); + msec_delay(1); // delay about 1ms + + /* Wait at least 10ms for All module to be Idle */ + for (i=0; i < 10; i++) { + icr = ATL2_READ_REG(hw, REG_IDLE_STATUS); + if (!icr) + break; + msec_delay(1); // delay 1 ms + cpu_relax(); + } + + if (icr) + return icr; + + return ATL2_SUCCESS; +} + +#define CUSTOM_SPI_CS_SETUP 2 +#define CUSTOM_SPI_CLK_HI 2 +#define CUSTOM_SPI_CLK_LO 2 +#define CUSTOM_SPI_CS_HOLD 2 +#define CUSTOM_SPI_CS_HI 3 + +static struct atl2_spi_flash_dev flash_table[] = +{ +/* manu_name WRSR READ PROGRAM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */ + {"Atmel", 0x0, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62 }, + {"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60 }, + {"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7 }, +}; + +static bool +atl2_spi_read(struct atl2_hw* hw, u32 addr, u32* buf) +{ + int i; + u32 value; + + ATL2_WRITE_REG(hw, REG_SPI_DATA, 0); + ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr); + + value = SPI_FLASH_CTRL_WAIT_READY | + (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << SPI_FLASH_CTRL_CS_SETUP_SHIFT | + (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) << SPI_FLASH_CTRL_CLK_HI_SHIFT | + (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) << SPI_FLASH_CTRL_CLK_LO_SHIFT | + (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) << SPI_FLASH_CTRL_CS_HOLD_SHIFT | + (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) << SPI_FLASH_CTRL_CS_HI_SHIFT | + (0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT; + + ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + + value |= SPI_FLASH_CTRL_START; + + ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + + for (i = 0; i < 10; i++) + { + msec_delay(1); // 1ms + value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); + if (!(value & SPI_FLASH_CTRL_START)) + break; + } + + if (value & SPI_FLASH_CTRL_START) + return false; + + *buf = ATL2_READ_REG(hw, REG_SPI_DATA); + + return true; +} + +/* + * get_permanent_address + * return 0 if get valid mac address, + */ +static int +get_permanent_address(struct atl2_hw *hw) +{ + u32 Addr[2]; + u32 i, Control; + u16 Register; + u8 EthAddr[NODE_ADDRESS_SIZE]; + bool KeyValid; + + if (is_valid_ether_addr(hw->perm_mac_addr)) + return 0; + + Addr[0] = 0; + Addr[1] = 0; + + if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */ + Register = 0; + KeyValid = false; + + /* Read out all EEPROM content */ + i = 0; + while (1) { + if (atl2_read_eeprom(hw, i + 0x100, &Control)) { + if (KeyValid) { + if (Register == REG_MAC_STA_ADDR) + Addr[0] = Control; + else if (Register == (REG_MAC_STA_ADDR + 4)) + Addr[1] = Control; + KeyValid = false; + } else if ((Control & 0xff) == 0x5A) { + KeyValid = true; + Register = (u16) (Control >> 16); + } else { + break; /* assume data end while encount an invalid KEYWORD */ + } + } else { + break; /* read error */ + } + i += 4; + } + + *(u32*) &EthAddr[2] = LONGSWAP(Addr[0]); + *(u16*) &EthAddr[0] = SHORTSWAP(*(u16*)&Addr[1]); + + if (is_valid_ether_addr(EthAddr)) { + memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE); + return 0; + } + return 1; + } + + // see if SPI FLAHS exist ? + Addr[0] = 0; + Addr[1] = 0; + Register = 0; + KeyValid = false; + i = 0; + while (1) { + if (atl2_spi_read(hw, i + 0x1f000, &Control)) { + if (KeyValid) { + if (Register == REG_MAC_STA_ADDR) + Addr[0] = Control; + else if (Register == (REG_MAC_STA_ADDR + 4)) + Addr[1] = Control; + KeyValid = false; + } else if ((Control & 0xff) == 0x5A) { + KeyValid = true; + Register = (u16) (Control >> 16); + } else { + break; /* data end */ + } + } else { + break; /* read error */ + } + i += 4; + } + + *(u32*) &EthAddr[2] = LONGSWAP(Addr[0]); + *(u16*) &EthAddr[0] = SHORTSWAP(*(u16*)&Addr[1]); + if (is_valid_ether_addr(EthAddr)) { + memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE); + return 0; + } + /* maybe MAC-address is from BIOS */ + Addr[0] = ATL2_READ_REG(hw,REG_MAC_STA_ADDR); + Addr[1] = ATL2_READ_REG(hw,REG_MAC_STA_ADDR+4); + *(u32*) &EthAddr[2] = LONGSWAP(Addr[0]); + *(u16*) &EthAddr[0] = SHORTSWAP(*(u16*)&Addr[1]); + + if (is_valid_ether_addr(EthAddr)) { + memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE); + return 0; + } + + return 1; +} + +/* + * Reads the adapter's MAC address from the EEPROM + * + * hw - Struct containing variables accessed by shared code + */ +s32 +atl2_read_mac_addr(struct atl2_hw *hw) +{ + u16 i; + + if (get_permanent_address(hw)) { + // for test + hw->perm_mac_addr[0] = 0x00; + hw->perm_mac_addr[1] = 0x13; + hw->perm_mac_addr[2] = 0x74; + hw->perm_mac_addr[3] = 0x00; + hw->perm_mac_addr[4] = 0x5c; + hw->perm_mac_addr[5] = 0x38; + } + + for(i = 0; i < NODE_ADDRESS_SIZE; i++) + hw->mac_addr[i] = hw->perm_mac_addr[i]; + + return ATL2_SUCCESS; +} + +/* + * Hashes an address to determine its location in the multicast table + * + * hw - Struct containing variables accessed by shared code + * mc_addr - the multicast address to hash + * + * atl2_hash_mc_addr + * purpose + * set hash value for a multicast address + * hash calcu processing : + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ +u32 +atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr) +{ + u32 crc32, value=0; + int i; + + crc32 = ether_crc_le(6, mc_addr); + + for (i=0; i<32; i++) + value |= (((crc32>>i)&1)<<(31-i)); + + return value; +} + +/* + * Sets the bit in the multicast table corresponding to the hash value. + * + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + */ +void +atl2_hash_set(struct atl2_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg; + u32 mta; + + /* The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 31) & 0x1; + hash_bit = (hash_value >> 26) & 0x1F; + + mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); + + mta |= (1 << hash_bit); + + ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); +} + +/* + * atl2_init_pcie - init PCIE module + */ +static void +atl2_init_pcie(struct atl2_hw *hw) +{ + u32 value; + value = LTSSM_TEST_MODE_DEF; + ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value); + + value = PCIE_DLL_TX_CTRL1_DEF; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value); +} + +void +atl2_init_flash_opcode(struct atl2_hw *hw) +{ + if (hw->flash_vendor >= ARRAY_SIZE(flash_table)) { + hw->flash_vendor = 0; // ATMEL + } + // Init OP table + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM, flash_table[hw->flash_vendor].cmdPROGRAM); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE, flash_table[hw->flash_vendor].cmdSECTOR_ERASE); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE, flash_table[hw->flash_vendor].cmdCHIP_ERASE); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID, flash_table[hw->flash_vendor].cmdRDID); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN, flash_table[hw->flash_vendor].cmdWREN); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR, flash_table[hw->flash_vendor].cmdRDSR); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR, flash_table[hw->flash_vendor].cmdWRSR); + ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ, flash_table[hw->flash_vendor].cmdREAD); +} + +/******************************************************************** +* Performs basic configuration of the adapter. +* +* hw - Struct containing variables accessed by shared code +* Assumes that the controller has previously been reset and is in a +* post-reset uninitialized state. Initializes multicast table, +* and Calls routines to setup link +* Leaves the transmit and receive units disabled and uninitialized. +********************************************************************/ +s32 +atl2_init_hw(struct atl2_hw *hw) +{ + u32 ret_val = 0; + + atl2_init_pcie(hw); + + /* Zero out the Multicast HASH table */ + /* clear the old settings from the multicast hash table */ + ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + atl2_init_flash_opcode(hw); + + ret_val = atl2_phy_init(hw); + + return ret_val; +} + +/* + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + */ +s32 +atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, u16 *duplex) +{ + s32 ret_val; + u16 phy_data; + + // ; --- Read PHY Specific Status Register (17) + ret_val = atl2_read_phy_reg(hw, MII_AT001_PSSR, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED)) + return ATL2_ERR_PHY_RES; + + switch(phy_data & MII_AT001_PSSR_SPEED) { + case MII_AT001_PSSR_100MBS: + *speed = SPEED_100; + break; + case MII_AT001_PSSR_10MBS: + *speed = SPEED_10; + break; + default: + return ATL2_ERR_PHY_SPEED; + break; + } + + if (phy_data & MII_AT001_PSSR_DPLX) { + *duplex = FULL_DUPLEX; + } else { + *duplex = HALF_DUPLEX; + } + + return ATL2_SUCCESS; +} + +/* + * Reads the value from a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to read + */ +s32 +atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data) +{ + u32 val; + int i; + + val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | + MDIO_START | + MDIO_SUP_PREAMBLE | + MDIO_RW | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); + + wmb(); + + for (i=0; iMediaType) { + case MEDIA_TYPE_AUTO_SENSOR: + mii_autoneg_adv_reg |= + (MII_AR_10T_HD_CAPS | + MII_AR_10T_FD_CAPS | + MII_AR_100TX_HD_CAPS | + MII_AR_100TX_FD_CAPS); + hw->autoneg_advertised = + ADVERTISE_10_HALF | + ADVERTISE_10_FULL | + ADVERTISE_100_HALF | + ADVERTISE_100_FULL; + break; + case MEDIA_TYPE_100M_FULL: + mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; + hw->autoneg_advertised = ADVERTISE_100_FULL; + break; + case MEDIA_TYPE_100M_HALF: + mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; + hw->autoneg_advertised = ADVERTISE_100_HALF; + break; + case MEDIA_TYPE_10M_FULL: + mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; + hw->autoneg_advertised = ADVERTISE_10_FULL; + break; + default: + mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; + hw->autoneg_advertised = ADVERTISE_10_HALF; + break; + } + + /* flow control fixed to enable all */ + mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); + + hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; + + ret_val = atl2_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); + + if(ret_val) + return ret_val; + + return ATL2_SUCCESS; +} + +/* + * Resets the PHY and make all config validate + * + * hw - Struct containing variables accessed by shared code + * + * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) + */ +static s32 +atl2_phy_commit(struct atl2_hw *hw) +{ + s32 ret_val; + u16 phy_data; + +/* FIXME: use or remove -- CHS + if (hw->MediaType == MEDIA_TYPE_AUTO_SENSOR) { + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; + } else { + switch (hw->MediaType) + { + case MEDIA_TYPE_100M_FULL: + phy_data = MII_CR_FULL_DUPLEX|MII_CR_SPEED_100|MII_CR_RESET; + break; + case MEDIA_TYPE_100M_HALF: + phy_data = MII_CR_SPEED_100|MII_CR_RESET; + break; + case MEDIA_TYPE_10M_FULL: + phy_data = MII_CR_FULL_DUPLEX|MII_CR_SPEED_10|MII_CR_RESET; + break; + default: // MEDIA_TYPE_10M_HALF: + phy_data = MII_CR_SPEED_10|MII_CR_RESET; + break; + } + } +*/ + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG; + ret_val = atl2_write_phy_reg(hw, MII_BMCR, phy_data); + if (ret_val) { // bug fixed + u32 val; + int i; + /* pcie serdes link may be down ! */ + for (i=0; i < 25; i++) { + msec_delay(1); + val = ATL2_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if (0 != (val & (MDIO_START | MDIO_BUSY))) { + printk(KERN_ERR "atl2: PCIe link down for at least 25ms !\n"); + return ret_val; + } + } + return ATL2_SUCCESS; +} + +s32 +atl2_phy_init(struct atl2_hw *hw) +{ + s32 ret_val; + u16 phy_val; + + if (hw->phy_configured) + return 0; + + /* Enable PHY */ + ATL2_WRITE_REGW(hw, REG_PHY_ENABLE, 1); + ATL2_WRITE_FLUSH(hw); + msec_delay(1); + + /* check if the PHY is in powersaving mode */ + atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); + atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); + + /* 024E / 124E 0r 0274 / 1274 ? */ + if (phy_val & 0x1000) { + phy_val &= ~0x1000; + atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val); + } + + msec_delay(1); + + + /*Enable PHY LinkChange Interrupt */ + ret_val = atl2_write_phy_reg(hw, 18, 0xC00); + if (ret_val) + return ret_val; + + /* setup AutoNeg parameters */ + ret_val = atl2_phy_setup_autoneg_adv(hw); + if(ret_val) + return ret_val; + + /* SW.Reset & En-Auto-Neg to restart Auto-Neg */ + ret_val = atl2_phy_commit(hw); + if (ret_val) + return ret_val; + + hw->phy_configured = true; + + return ret_val; +} + +void +atl2_set_mac_addr(struct atl2_hw *hw) +{ + u32 value; + // 00-0B-6A-F6-00-DC + // 0: 6AF600DC 1: 000B + // low dword + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8 ) | + (((u32)hw->mac_addr[5]) ) ; + ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); + // hight dword + value = (((u32)hw->mac_addr[0]) << 8 ) | + (((u32)hw->mac_addr[1]) ) ; + ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); +} + +/* + * check_eeprom_exist + * return 0 if eeprom exist + */ +int +atl2_check_eeprom_exist(struct atl2_hw *hw) +{ + u32 value; + + value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); + if (value & SPI_FLASH_CTRL_EN_VPD) { + value &= ~SPI_FLASH_CTRL_EN_VPD; + ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + } + value = ATL2_READ_REGW(hw, REG_PCIE_CAP_LIST); + return ((value & 0xFF00) == 0x6C00) ? 0 : 1; +} + +// FIXME: This doesn't look right. -- CHS +bool +atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value) +{ + return true; +} + +bool +atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue) +{ + int i; + u32 Control; + + if (Offset & 0x3) + return false; /* address do not align */ + + ATL2_WRITE_REG(hw, REG_VPD_DATA, 0); + Control = (Offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; + ATL2_WRITE_REG(hw, REG_VPD_CAP, Control); + + for (i = 0; i < 10; i++) { + msec_delay(2); + Control = ATL2_READ_REG(hw, REG_VPD_CAP); + if (Control & VPD_CAP_VPD_FLAG) + break; + } + + if (Control & VPD_CAP_VPD_FLAG) { + *pValue = ATL2_READ_REG(hw, REG_VPD_DATA); + return true; + } + return false; /* timeout */ +} + +void +atl2_force_ps(struct atl2_hw *hw) +{ + u16 phy_val; + + atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); + atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); + atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val | 0x1000); + + atl2_write_phy_reg(hw, MII_DBG_ADDR, 2); + atl2_write_phy_reg(hw, MII_DBG_DATA, 0x3000); + atl2_write_phy_reg(hw, MII_DBG_ADDR, 3); + atl2_write_phy_reg(hw, MII_DBG_DATA, 0); +} Index: linux-2.6.26.noarch/drivers/net/atl2/atl2_hw.h =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2_hw.h @@ -0,0 +1,758 @@ +/* atl2_hw.h -- atl2 hardware definitions + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Some of these defines are unused for various reasons. Some describe + * hardware features we don't yet use. Some are specific to the cousin atl1 + * hardware, which we may merge this driver with in the future. Please + * remember this is a surrogate for hardware specs, and don't unnecessarily + * abuse the content or formatting. -- CHS + */ + +#ifndef _ATL2_HW_H_ +#define _ATL2_HW_H_ + +#include "atl2_osdep.h" + +struct atl2_adapter; +struct atl2_hw; + +/* function prototype */ +s32 atl2_reset_hw(struct atl2_hw *hw); +s32 atl2_read_mac_addr(struct atl2_hw *hw); +s32 atl2_init_hw(struct atl2_hw *hw); +s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, u16 *duplex); +u32 atl2_auto_get_fc(struct atl2_adapter *adapter, u16 duplex); +u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr); +void atl2_hash_set(struct atl2_hw *hw, u32 hash_value); +s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data); +s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data); +void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value); +void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value); +s32 atl2_validate_mdi_setting(struct atl2_hw *hw); +void atl2_set_mac_addr(struct atl2_hw *hw); +bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue); +bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value); +s32 atl2_phy_init(struct atl2_hw *hw); +int atl2_check_eeprom_exist(struct atl2_hw *hw); +void atl2_force_ps(struct atl2_hw *hw); + +/* register definition */ +#define REG_PM_CTRLSTAT 0x44 + +#define REG_PCIE_CAP_LIST 0x58 + +#define REG_VPD_CAP 0x6C +#define VPD_CAP_ID_MASK 0xff +#define VPD_CAP_ID_SHIFT 0 +#define VPD_CAP_NEXT_PTR_MASK 0xFF +#define VPD_CAP_NEXT_PTR_SHIFT 8 +#define VPD_CAP_VPD_ADDR_MASK 0x7FFF +#define VPD_CAP_VPD_ADDR_SHIFT 16 +#define VPD_CAP_VPD_FLAG 0x80000000 + +#define REG_VPD_DATA 0x70 + +#define REG_SPI_FLASH_CTRL 0x200 +#define SPI_FLASH_CTRL_STS_NON_RDY 0x1 +#define SPI_FLASH_CTRL_STS_WEN 0x2 +#define SPI_FLASH_CTRL_STS_WPEN 0x80 +#define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF +#define SPI_FLASH_CTRL_DEV_STS_SHIFT 0 +#define SPI_FLASH_CTRL_INS_MASK 0x7 +#define SPI_FLASH_CTRL_INS_SHIFT 8 +#define SPI_FLASH_CTRL_START 0x800 +#define SPI_FLASH_CTRL_EN_VPD 0x2000 +#define SPI_FLASH_CTRL_LDSTART 0x8000 +#define SPI_FLASH_CTRL_CS_HI_MASK 0x3 +#define SPI_FLASH_CTRL_CS_HI_SHIFT 16 +#define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3 +#define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18 +#define SPI_FLASH_CTRL_CLK_LO_MASK 0x3 +#define SPI_FLASH_CTRL_CLK_LO_SHIFT 20 +#define SPI_FLASH_CTRL_CLK_HI_MASK 0x3 +#define SPI_FLASH_CTRL_CLK_HI_SHIFT 22 +#define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3 +#define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24 +#define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3 +#define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26 +#define SPI_FLASH_CTRL_WAIT_READY 0x10000000 + +#define REG_SPI_ADDR 0x204 + +#define REG_SPI_DATA 0x208 + +#define REG_SPI_FLASH_CONFIG 0x20C +#define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF +#define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0 +#define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3 +#define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24 +#define SPI_FLASH_CONFIG_LD_EXIST 0x4000000 + +#define REG_SPI_FLASH_OP_PROGRAM 0x210 +#define REG_SPI_FLASH_OP_SC_ERASE 0x211 +#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212 +#define REG_SPI_FLASH_OP_RDID 0x213 +#define REG_SPI_FLASH_OP_WREN 0x214 +#define REG_SPI_FLASH_OP_RDSR 0x215 +#define REG_SPI_FLASH_OP_WRSR 0x216 +#define REG_SPI_FLASH_OP_READ 0x217 + +#define REG_TWSI_CTRL 0x218 +#define TWSI_CTRL_LD_OFFSET_MASK 0xFF +#define TWSI_CTRL_LD_OFFSET_SHIFT 0 +#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 +#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 +#define TWSI_CTRL_SW_LDSTART 0x800 +#define TWSI_CTRL_HW_LDSTART 0x1000 +#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x0x7F +#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 +#define TWSI_CTRL_LD_EXIST 0x400000 +#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 +#define TWSI_CTRL_FREQ_SEL_100K 0 +#define TWSI_CTRL_FREQ_SEL_200K 1 +#define TWSI_CTRL_FREQ_SEL_300K 2 +#define TWSI_CTRL_FREQ_SEL_400K 3 +#define TWSI_CTRL_SMB_SLV_ADDR +#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 + +#define REG_PCIE_DEV_MISC_CTRL 0x21C +#define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2 +#define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1 +#define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4 +#define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8 +#define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10 + +#define REG_PCIE_PHYMISC 0x1000 +#define PCIE_PHYMISC_FORCE_RCV_DET 0x4 + +#define REG_PCIE_DLL_TX_CTRL1 0x1104 +#define PCIE_DLL_TX_CTRL1_SEL_NOR_CLK 0x0400 +#define PCIE_DLL_TX_CTRL1_DEF 0x0568 + +#define REG_LTSSM_TEST_MODE 0x12FC +#define LTSSM_TEST_MODE_DEF 0x6500 + +/* Master Control Register */ +#define REG_MASTER_CTRL 0x1400 +#define MASTER_CTRL_SOFT_RST 0x1 +#define MASTER_CTRL_MTIMER_EN 0x2 +#define MASTER_CTRL_ITIMER_EN 0x4 +#define MASTER_CTRL_MANUAL_INT 0x8 +#define MASTER_CTRL_REV_NUM_SHIFT 16 +#define MASTER_CTRL_REV_NUM_MASK 0xff +#define MASTER_CTRL_DEV_ID_SHIFT 24 +#define MASTER_CTRL_DEV_ID_MASK 0xff + +/* Timer Initial Value Register */ +#define REG_MANUAL_TIMER_INIT 0x1404 + +/* IRQ ModeratorTimer Initial Value Register */ +#define REG_IRQ_MODU_TIMER_INIT 0x1408 + +#define REG_PHY_ENABLE 0x140C +// IRQ Anti-Lost Timer Initial Value Register +//#define REG_IRQ_CLR_TIMER 0x140c // Maximum allowance for software to clear the interrupt. +// IRQ Anti-Lost Timer Initial Value Register +#define REG_CMBDISDMA_TIMER 0x140E + +/* Block IDLE Status Register */ +#define REG_IDLE_STATUS 0x1410 +#define IDLE_STATUS_RXMAC 1 /* 1: RXMAC state machine is in non-IDLE state. 0: RXMAC is idling */ +#define IDLE_STATUS_TXMAC 2 /* 1: TXMAC state machine is in non-IDLE state. 0: TXMAC is idling */ +#define IDLE_STATUS_DMAR 8 /* 1: DMAR state machine is in non-IDLE state. 0: DMAR is idling */ +#define IDLE_STATUS_DMAW 4 /* 1: DMAW state machine is in non-IDLE state. 0: DMAW is idling */ + +/* MDIO Control Register */ +#define REG_MDIO_CTRL 0x1414 +#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit control data to write to PHY MII management register */ +#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit status data that was read from the PHY MII management register. */ +#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */ +#define MDIO_REG_ADDR_SHIFT 16 +#define MDIO_RW 0x200000 /* 1: read, 0: write */ +#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */ +#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO master. And this bit is self cleared after one cycle. */ +#define MDIO_CLK_SEL_SHIFT 24 +#define MDIO_CLK_25_4 0 +#define MDIO_CLK_25_6 2 +#define MDIO_CLK_25_8 3 +#define MDIO_CLK_25_10 4 +#define MDIO_CLK_25_14 5 +#define MDIO_CLK_25_20 6 +#define MDIO_CLK_25_28 7 +#define MDIO_BUSY 0x8000000 +#define MDIO_WAIT_TIMES 10 + +/* SerDes Lock Detect Control and Status Register */ +#define REG_SERDES_LOCK 0x1424 +#define SERDES_LOCK_DETECT 1 /* 1: SerDes lock detected. This signal comes from Analog SerDes. */ +#define SERDES_LOCK_DETECT_EN 2 /* 1: Enable SerDes Lock detect function. */ + +/* MAC Control Register */ +#define REG_MAC_CTRL 0x1480 +#define MAC_CTRL_TX_EN 1 /* 1: Transmit Enable */ +#define MAC_CTRL_RX_EN 2 /* 1: Receive Enable */ +#define MAC_CTRL_TX_FLOW 4 /* 1: Transmit Flow Control Enable */ +#define MAC_CTRL_RX_FLOW 8 /* 1: Receive Flow Control Enable */ +#define MAC_CTRL_LOOPBACK 0x10 /* 1: Loop back at G/MII Interface */ +#define MAC_CTRL_DUPLX 0x20 /* 1: Full-duplex mode 0: Half-duplex mode */ +#define MAC_CTRL_ADD_CRC 0x40 /* 1: Instruct MAC to attach CRC on all egress Ethernet frames */ +#define MAC_CTRL_PAD 0x80 /* 1: Instruct MAC to pad short frames to 60-bytes, and then attach CRC. This bit has higher priority over CRC_EN */ +#define MAC_CTRL_PRMLEN_SHIFT 10 /* Preamble length, it's 0x07 by standard */ +#define MAC_CTRL_PRMLEN_MASK 0xf +#define MAC_CTRL_RMV_VLAN 0x4000 /* 1: to remove VLAN Tag automatically from all receive packets */ +#define MAC_CTRL_PROMIS_EN 0x8000 /* 1: Promiscuous Mode Enable */ +#define MAC_CTRL_DBG_TX_BKPRESURE 0x100000 /* 1: transmit maximum backoff (half-duplex test bit) */ +#define MAC_CTRL_MC_ALL_EN 0x2000000 /* 1: upload all multicast frame without error to system */ +#define MAC_CTRL_BC_EN 0x4000000 /* 1: upload all broadcast frame without error to system */ +#define MAC_CTRL_MACLP_CLK_PHY 0x8000000 /* 1: MAC-LoopBack clock from phy, 0:from sys_25M */ +#define MAC_CTRL_HALF_LEFT_BUF_SHIFT 28 +#define MAC_CTRL_HALF_LEFT_BUF_MASK 0xF /* When half-duplex mode, should hold some bytes for mac retry . (8*4bytes unit) */ + +/* MAC IPG/IFG Control Register */ +#define REG_MAC_IPG_IFG 0x1484 +#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back inter-packet gap. The default is 96-bit time. */ +#define MAC_IPG_IFG_IPGT_MASK 0x7f +#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to enforce in between RX frames. */ +#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped. */ +#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */ +#define MAC_IPG_IFG_IPGR1_MASK 0x7f +#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */ +#define MAC_IPG_IFG_IPGR2_MASK 0x7f + +/* MAC STATION ADDRESS */ +#define REG_MAC_STA_ADDR 0x1488 + +/* Hash table for multicast address */ +#define REG_RX_HASH_TABLE 0x1490 + +/* MAC Half-Duplex Control Register */ +#define REG_MAC_HALF_DUPLX_CTRL 0x1498 +#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window. */ +#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff +#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 /* Retransmission maximum, afterwards the packet will be discarded. */ +#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 /* 1: Allow the transmission of a packet which has been excessively deferred */ +#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 /* 1: No back-off on collision, immediately start the retransmission. */ +#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* 1: No back-off on backpressure, immediately start the transmission after back pressure */ +#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number. */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */ +#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time. */ + +/* Maximum Frame Length Control Register */ +#define REG_MTU 0x149c + +/* Wake-On-Lan control register */ +#define REG_WOL_CTRL 0x14a0 +#define WOL_PATTERN_EN 0x00000001 +#define WOL_PATTERN_PME_EN 0x00000002 +#define WOL_MAGIC_EN 0x00000004 +#define WOL_MAGIC_PME_EN 0x00000008 +#define WOL_LINK_CHG_EN 0x00000010 +#define WOL_LINK_CHG_PME_EN 0x00000020 +#define WOL_PATTERN_ST 0x00000100 +#define WOL_MAGIC_ST 0x00000200 +#define WOL_LINKCHG_ST 0x00000400 +#define WOL_PT0_EN 0x00010000 +#define WOL_PT1_EN 0x00020000 +#define WOL_PT2_EN 0x00040000 +#define WOL_PT3_EN 0x00080000 +#define WOL_PT4_EN 0x00100000 +#define WOL_PT0_MATCH 0x01000000 +#define WOL_PT1_MATCH 0x02000000 +#define WOL_PT2_MATCH 0x04000000 +#define WOL_PT3_MATCH 0x08000000 +#define WOL_PT4_MATCH 0x10000000 + +/* Internal SRAM Partition Register */ +#define REG_SRAM_TXRAM_END 0x1500 /* Internal tail address of TXRAM default: 2byte*1024 */ +#define REG_SRAM_RXRAM_END 0x1502 /* Internal tail address of RXRAM default: 2byte*1024 */ + +/* +#define REG_SRAM_TCPH_PATH_ADDR (REG_SRAM_RFD_ADDR+48) +#define SRAM_TCPH_ADDR_MASK 0x0fff +#define SRAM_TCPH_ADDR_SHIFT 0 +#define SRAM_PATH_ADDR_MASK 0x0fff +#define SRAM_PATH_ADDR_SHIFT 16 +*/ + +/* Descriptor Control register */ +#define REG_DESC_BASE_ADDR_HI 0x1540 +#define REG_TXD_BASE_ADDR_LO 0x1544 /* The base address of the Transmit Data Memory low 32-bit(dword align) */ +#define REG_TXD_MEM_SIZE 0x1548 /* Transmit Data Memory size(by double word , max 256KB) */ +#define REG_TXS_BASE_ADDR_LO 0x154C /* The base address of the Transmit Status Memory low 32-bit(dword word align) */ +#define REG_TXS_MEM_SIZE 0x1550 /* double word unit, max 4*2047 bytes. */ +#define REG_RXD_BASE_ADDR_LO 0x1554 /* The base address of the Transmit Status Memory low 32-bit(unit 8 bytes) */ +#define REG_RXD_BUF_NUM 0x1558 /* Receive Data & Status Memory buffer number (unit 1536bytes, max 1536*2047) */ + +/* DMAR Control Register */ +#define REG_DMAR 0x1580 +#define DMAR_EN 0x1 /* 1: Enable DMAR */ + +/* TX Cur-Through (early tx threshold) Control Register */ +#define REG_TX_CUT_THRESH 0x1590 /* TxMac begin transmit packet threshold(unit word) */ + +/* DMAW Control Register */ +#define REG_DMAW 0x15A0 +#define DMAW_EN 0x1 + +/* Flow control register */ +#define REG_PAUSE_ON_TH 0x15A8 /* RXD high watermark of overflow threshold configuration register */ +#define REG_PAUSE_OFF_TH 0x15AA /* RXD lower watermark of overflow threshold configuration register */ + +/* Mailbox Register */ +#define REG_MB_TXD_WR_IDX 0x15f0 /* double word align */ +#define REG_MB_RXD_RD_IDX 0x15F4 /* RXD Read index (unit: 1536byets) */ + +/* Interrupt Status Register */ +#define REG_ISR 0x1600 +#define ISR_TIMER 1 /* Interrupt when Timer is counted down to zero */ +#define ISR_MANUAL 2 /* Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set in Table 51 Selene Master Control Register (Offset 0x1400). */ +#define ISR_RXF_OV 4 /* RXF overflow interrupt */ +#define ISR_TXF_UR 8 /* TXF underrun interrupt */ +#define ISR_TXS_OV 0x10 /* Internal transmit status buffer full interrupt */ +#define ISR_RXS_OV 0x20 /* Internal receive status buffer ful interrupt */ +#define ISR_LINK_CHG 0x40 /* Link Status Change Interrupt */ +#define ISR_HOST_TXD_UR 0x80 +#define ISR_HOST_RXD_OV 0x100 /* Host rx data memory full , one pulse */ +//#define ISR_HOST_TXS_OV 0x200 /* Host tx status memory full , one pulse */ +#define ISR_DMAR_TO_RST 0x200 /* DMAR op timeout interrupt. SW should do Reset */ +#define ISR_DMAW_TO_RST 0x400 +#define ISR_PHY 0x800 /* phy interrupt */ +#define ISR_TS_UPDATE 0x10000 /* interrupt after new tx pkt status written to host */ +#define ISR_RS_UPDATE 0x20000 /* interrupt ater new rx pkt status written to host. */ +#define ISR_TX_EARLY 0x40000 /* interrupt when txmac begin transmit one packet */ +#define ISR_UR_DETECTED 0x1000000 +#define ISR_FERR_DETECTED 0x2000000 +#define ISR_NFERR_DETECTED 0x4000000 +#define ISR_CERR_DETECTED 0x8000000 +#define ISR_PHY_LINKDOWN 0x10000000 +#define ISR_DIS_INT 0x80000000 + +#define ISR_TX_EVENT (ISR_TXF_UR|ISR_TXS_OV|ISR_HOST_TXD_UR|ISR_TS_UPDATE|ISR_TX_EARLY) +#define ISR_RX_EVENT (ISR_RXF_OV|ISR_RXS_OV|ISR_HOST_RXD_OV|ISR_RS_UPDATE) + +/* Interrupt Mask Register */ +#define REG_IMR 0x1604 + +#define IMR_NORMAL_MASK (\ + /*ISR_LINK_CHG |*/\ + ISR_MANUAL |\ + ISR_DMAR_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_PHY |\ + ISR_PHY_LINKDOWN |\ + ISR_TS_UPDATE |\ + ISR_RS_UPDATE ) + +/* Receive MAC Statistics Registers */ +#define REG_STS_RX_PAUSE 0x1700 /* The number of Pause packet received */ +#define REG_STS_RXD_OV 0x1704 /* The number of frame dropped due to occurrence of RX FIFO overflow */ +#define REG_STS_RXS_OV 0x1708 /* The number of frame dropped due to occerrence of RX Status Buffer Overflow */ +#define REG_STS_RX_FILTER 0x170C /* The number of packet dropped due to address filtering */ + +/* MII definitions */ + +/* PHY Common Register */ +#define MII_AT001_CR 0x09 +#define MII_AT001_SR 0x0A +#define MII_AT001_ESR 0x0F +#define MII_AT001_PSCR 0x10 +#define MII_AT001_PSSR 0x11 +#define MII_SMARTSPEED 0x14 +#define MII_DBG_ADDR 0x1D +#define MII_DBG_DATA 0x1E + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_MASK 0x2040 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Link partner ability register. */ +#define MII_LPA_SLCT 0x001f /* Same as advertise selector */ +#define MII_LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define MII_LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define MII_LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define MII_LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define MII_LPA_100BASE4 0x0200 /* 100BASE-T4 */ +#define MII_LPA_PAUSE 0x0400 /* PAUSE */ +#define MII_LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ +#define MII_LPA_RFAULT 0x2000 /* Link partner faulted */ +#define MII_LPA_LPACK 0x4000 /* Link partner acked us */ +#define MII_LPA_NPAGE 0x8000 /* Next page bit */ + +/* Autoneg Advertisement Register */ +#define MII_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define MII_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define MII_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define MII_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define MII_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define MII_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define MII_AR_PAUSE 0x0400 /* Pause operation desired */ +#define MII_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define MII_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define MII_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ +#define MII_AR_SPEED_MASK 0x01E0 +#define MII_AR_DEFAULT_CAP_MASK 0x0DE0 + +/* 1000BASE-T Control Register */ +#define MII_AT001_CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define MII_AT001_CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define MII_AT001_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port, 0=DTE device */ +#define MII_AT001_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master, 0=Configure PHY as Slave */ +#define MII_AT001_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value, 0=Automatic Master/Slave config */ +#define MII_AT001_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define MII_AT001_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define MII_AT001_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define MII_AT001_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define MII_AT001_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ +#define MII_AT001_CR_1000T_SPEED_MASK 0x0300 +#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK 0x0300 + +/* 1000BASE-T Status Register */ +#define MII_AT001_SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define MII_AT001_SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define MII_AT001_SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define MII_AT001_SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define MII_AT001_SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define MII_AT001_SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define MII_AT001_SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define MII_AT001_SR_1000T_LOCAL_RX_STATUS_SHIFT 13 + +/* Extended Status Register */ +#define MII_AT001_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define MII_AT001_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define MII_AT001_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define MII_AT001_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +/* AT001 PHY Specific Control Register */ +#define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define MII_AT001_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define MII_AT001_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled *// +#define MII_AT001_PSCR_MAC_POWERDOWN 0x0008 +#define MII_AT001_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, 0=CLK125 toggling */ +#define MII_AT001_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5, Manual MDI configuration */ +#define MII_AT001_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define MII_AT001_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, 100BASE-TX/10BASE-T: MDI Mode */ +#define MII_AT001_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled all speeds. */ +#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE 0x0080 /* 1=Enable Extended 10BASE-T distance (Lower 10BASE-T RX Threshold), 0=Normal 10BASE-T RX Threshold */ +#define MII_AT001_PSCR_MII_5BIT_ENABLE 0x0100 /* 1=5-Bit interface in 100BASE-TX, 0=MII interface in 100BASE-TX */ +#define MII_AT001_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define MII_AT001_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define MII_AT001_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ +#define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define MII_AT001_PSCR_AUTO_X_MODE_SHIFT 5 +#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* AT001 PHY Specific Status Register */ +#define MII_AT001_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define MII_AT001_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define MII_AT001_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define MII_AT001_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define MII_AT001_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define MII_AT001_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +/* PCI Command Register Bit Definitions */ +#define PCI_REG_COMMAND 0x04 +#define CMD_IO_SPACE 0x0001 +#define CMD_MEMORY_SPACE 0x0002 +#define CMD_BUS_MASTER 0x0004 + +/* Wake Up Filter Control */ +#define ATL2_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define ATL2_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define ATL2_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define ATL2_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ +#define ATL2_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ + +/* Error Codes */ +#define ATL2_SUCCESS 0 +#define ATL2_ERR_EEPROM 1 +#define ATL2_ERR_PHY 2 +#define ATL2_ERR_CONFIG 3 +#define ATL2_ERR_PARAM 4 +#define ATL2_ERR_MAC_TYPE 5 +#define ATL2_ERR_PHY_TYPE 6 +#define ATL2_ERR_PHY_SPEED 7 +#define ATL2_ERR_PHY_RES 8 + +#define SPEED_0 0xffff +#define SPEED_10 10 +#define SPEED_100 100 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define MEDIA_TYPE_AUTO_SENSOR 0 +#define MEDIA_TYPE_100M_FULL 1 +#define MEDIA_TYPE_100M_HALF 2 +#define MEDIA_TYPE_10M_FULL 3 +#define MEDIA_TYPE_10M_HALF 4 + +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */ +#define ADVERTISE_1000_FULL 0x0020 + +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x000F /* Everything */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/ + +/* The size (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* with FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* with FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAX_JUMBO_FRAME_SIZE 0x2000 +#define VLAN_SIZE 4 + +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ + +/* For checksumming , the sum of all words in the EEPROM should equal 0xBABA */ +#define EEPROM_SUM 0xBABA +#define NODE_ADDRESS_SIZE 6 + +typedef struct _tx_pkt_header { + unsigned pkt_size : 11; + unsigned : 4; // reserved + unsigned ins_vlan : 1; // txmac should insert vlan + unsigned short vlan ; // vlan tag +} tx_pkt_header_t; +/* FIXME: replace above bitfields with MASK/SHIFT defines below */ +#define TX_PKT_HEADER_SIZE_MASK 0x7FF +#define TX_PKT_HEADER_SIZE_SHIFT 0 +#define TX_PKT_HEADER_INS_VLAN_MASK 0x1 +#define TX_PKT_HEADER_INS_VLAN_SHIFT 15 +#define TX_PKT_HEADER_VLAN_TAG_MASK 0xFFFF +#define TX_PKT_HEADER_VLAN_TAG_SHIFT 16 + +typedef struct _tx_pkt_status { + unsigned pkt_size : 11; + unsigned : 5; // reserved + unsigned ok : 1; // current packet is transmitted ok without error + unsigned bcast : 1; // current packet is broadcast + unsigned mcast : 1; // current packet is multicast + unsigned pause : 1; // transmiited a pause frame + unsigned ctrl : 1; + unsigned defer : 1; // current packet is xmitted with defer. + unsigned exc_defer : 1; + unsigned single_col : 1; + unsigned multi_col : 1; + unsigned late_col : 1; + unsigned abort_col : 1; + unsigned underun : 1; // current packet is abort due to txram underrun. + unsigned : 3; // reserved + unsigned update : 1; // always 1'b1 in tx_status_buf. +} tx_pkt_status_t; +/* FIXME: replace above bitfields with MASK/SHIFT defines below */ +#define TX_PKT_STATUS_SIZE_MASK 0x7FF +#define TX_PKT_STATUS_SIZE_SHIFT 0 +#define TX_PKT_STATUS_OK_MASK 0x1 +#define TX_PKT_STATUS_OK_SHIFT 16 +#define TX_PKT_STATUS_BCAST_MASK 0x1 +#define TX_PKT_STATUS_BCAST_SHIFT 17 +#define TX_PKT_STATUS_MCAST_MASK 0x1 +#define TX_PKT_STATUS_MCAST_SHIFT 18 +#define TX_PKT_STATUS_PAUSE_MASK 0x1 +#define TX_PKT_STATUS_PAUSE_SHIFT 19 +#define TX_PKT_STATUS_CTRL_MASK 0x1 +#define TX_PKT_STATUS_CTRL_SHIFT 20 +#define TX_PKT_STATUS_DEFER_MASK 0x1 +#define TX_PKT_STATUS_DEFER_SHIFT 21 +#define TX_PKT_STATUS_EXC_DEFER_MASK 0x1 +#define TX_PKT_STATUS_EXC_DEFER_SHIFT 22 +#define TX_PKT_STATUS_SINGLE_COL_MASK 0x1 +#define TX_PKT_STATUS_SINGLE_COL_SHIFT 23 +#define TX_PKT_STATUS_MULTI_COL_MASK 0x1 +#define TX_PKT_STATUS_MULTI_COL_SHIFT 24 +#define TX_PKT_STATUS_LATE_COL_MASK 0x1 +#define TX_PKT_STATUS_LATE_COL_SHIFT 25 +#define TX_PKT_STATUS_ABORT_COL_MASK 0x1 +#define TX_PKT_STATUS_ABORT_COL_SHIFT 26 +#define TX_PKT_STATUS_UNDERRUN_MASK 0x1 +#define TX_PKT_STATUS_UNDERRUN_SHIFT 27 +#define TX_PKT_STATUS_UPDATE_MASK 0x1 +#define TX_PKT_STATUS_UPDATE_SHIFT 31 + +typedef struct _rx_pkt_status { + unsigned pkt_size : 11; // packet size, max 2047bytes + unsigned : 5; // reserved + unsigned ok : 1; // current packet is received ok without error. + unsigned bcast : 1; // current packet is broadcast. + unsigned mcast : 1; // current packet is multicast. + unsigned pause : 1; + unsigned ctrl : 1; + unsigned crc : 1; // received a packet with crc error. + unsigned code : 1; // received a packet with code error. + unsigned runt : 1; // received a packet less than 64bytes with good crc + unsigned frag : 1; // ....................................with bad crc + unsigned trunc : 1; // current frame is cutted due to rxram full. + unsigned align : 1; // this packet is alignment error. + unsigned vlan : 1; // this packet has vlan + unsigned : 3; // reserved + unsigned update : 1; + unsigned short vtag ; // vlan tag + unsigned : 16; +} rx_pkt_status_t; +/* FIXME: replace above bitfields with MASK/SHIFT defines below */ +#define RX_PKT_STATUS_SIZE_MASK 0x7FF +#define RX_PKT_STATUS_SIZE_SHIFT 0 +#define RX_PKT_STATUS_OK_MASK 0x1 +#define RX_PKT_STATUS_OK_SHIFT 16 +#define RX_PKT_STATUS_BCAST_MASK 0x1 +#define RX_PKT_STATUS_BCAST_SHIFT 17 +#define RX_PKT_STATUS_MCAST_MASK 0x1 +#define RX_PKT_STATUS_MCAST_SHIFT 18 +#define RX_PKT_STATUS_PAUSE_MASK 0x1 +#define RX_PKT_STATUS_PAUSE_SHIFT 19 +#define RX_PKT_STATUS_CTRL_MASK 0x1 +#define RX_PKT_STATUS_CTRL_SHIFT 20 +#define RX_PKT_STATUS_CRC_MASK 0x1 +#define RX_PKT_STATUS_CRC_SHIFT 21 +#define RX_PKT_STATUS_CODE_MASK 0x1 +#define RX_PKT_STATUS_CODE_SHIFT 22 +#define RX_PKT_STATUS_RUNT_MASK 0x1 +#define RX_PKT_STATUS_RUNT_SHIFT 23 +#define RX_PKT_STATUS_FRAG_MASK 0x1 +#define RX_PKT_STATUS_FRAG_SHIFT 24 +#define RX_PKT_STATUS_TRUNK_MASK 0x1 +#define RX_PKT_STATUS_TRUNK_SHIFT 25 +#define RX_PKT_STATUS_ALIGN_MASK 0x1 +#define RX_PKT_STATUS_ALIGN_SHIFT 26 +#define RX_PKT_STATUS_VLAN_MASK 0x1 +#define RX_PKT_STATUS_VLAN_SHIFT 27 +#define RX_PKT_STATUS_UPDATE_MASK 0x1 +#define RX_PKT_STATUS_UPDATE_SHIFT 31 +#define RX_PKT_STATUS_VLAN_TAG_MASK 0xFFFF +#define RX_PKT_STATUS_VLAN_TAG_SHIFT 32 + +typedef struct _rx_desc { + rx_pkt_status_t status; + unsigned char packet[1536-sizeof(rx_pkt_status_t)]; +} rx_desc_t; + +typedef enum { + atl2_10_half = 0, + atl2_10_full = 1, + atl2_100_half = 2, + atl2_100_full = 3 +} atl2_speed_duplex_type; + +struct atl2_spi_flash_dev { + const char *manu_name; /* manufacturer id */ + /* op-code */ + u8 cmdWRSR; + u8 cmdREAD; + u8 cmdPROGRAM; + u8 cmdWREN; + u8 cmdWRDI; + u8 cmdRDSR; + u8 cmdRDID; + u8 cmdSECTOR_ERASE; + u8 cmdCHIP_ERASE; +}; + +/* Structure containing variables used by the shared code (atl2_hw.c) */ +struct atl2_hw { + u8 *hw_addr; + void *back; + + u8 preamble_len; + u8 max_retry; // Retransmission maximum , afterwards the packet will be discarded. + u8 jam_ipg; // IPG to start JAM for collision based flow control in half-duplex mode. In unit of 8-bit time. + u8 ipgt; // Desired back to back inter-packet gap. The default is 96-bit time. + u8 min_ifg; // Minimum number of IFG to enforce in between RX frames. Frame gap below such IFP is dropped. + u8 ipgr1; // 64bit Carrier-Sense window + u8 ipgr2; // 96-bit IPG window + u8 retry_buf; // When half-duplex mode, should hold some bytes for mac retry . (8*4bytes unit) + + u16 fc_rxd_hi; + u16 fc_rxd_lo; + u16 lcol; // Collision Window + u16 max_frame_size; + + u16 MediaType; + u16 autoneg_advertised; + u16 pci_cmd_word; + + u16 mii_autoneg_adv_reg; + + u32 mem_rang; + u32 txcw; + u32 mc_filter_type; + u32 num_mc_addrs; + u32 collision_delta; + u32 tx_packet_delta; + u16 phy_spd_default; + + u16 device_id; + u16 vendor_id; + u16 subsystem_id; + u16 subsystem_vendor_id; + u8 revision_id; + + // spi flash + u8 flash_vendor; + + u8 dma_fairness; + u8 mac_addr[NODE_ADDRESS_SIZE]; + u8 perm_mac_addr[NODE_ADDRESS_SIZE]; + + // bool phy_preamble_sup; + bool phy_configured; +}; + +#endif /* _ATL2_HW_H_ */ Index: linux-2.6.26.noarch/drivers/net/atl2/atl2_main.c =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2_main.c @@ -0,0 +1,1851 @@ +/* atl2_main.c -- atl2 driver main functions + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atl2.h" + +#define ATL2_DRV_VERSION "2.0.4" + +char atl2_driver_name[] = "atl2"; +static const char atl2_driver_string[] = "Atheros(R) L2 Ethernet Driver"; +static char atl2_copyright[] = "Copyright (c) 2007 Atheros Corporation."; +char atl2_driver_version[] = ATL2_DRV_VERSION; + +MODULE_AUTHOR("Atheros Corporation , Chris Snook "); +MODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ATL2_DRV_VERSION); + +/* FIXME: remove this after merging, goes in pci_ids.h */ +#ifndef PCI_DEVICE_ID_ATTANSIC_L2 +#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 +#endif + +/* + * atl2_pci_tbl - PCI Device ID Table + */ +static struct pci_device_id atl2_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)}, + /* required last entry */ + {0,} +}; +MODULE_DEVICE_TABLE(pci, atl2_pci_tbl); + +extern void atl2_set_ethtool_ops(struct net_device *netdev); +#ifdef ETHTOOL_OPS_COMPAT +extern int ethtool_ioctl(struct ifreq *ifr); +#endif + +#define COPYBREAK_DEFAULT 256 +static unsigned int copybreak __read_mostly = COPYBREAK_DEFAULT; +module_param(copybreak, uint, 0644); +MODULE_PARM_DESC(copybreak, "Maximum size of packet that is copied to a new buffer on receive"); + +extern void atl2_check_options(struct atl2_adapter *adapter); +#ifdef SIOCDEVPRIVATE +extern int atl2_priv_ioctl(struct net_device* netdev, struct ifreq* ifr); +#endif + +/** + * atl2_sw_init - Initialize general software structures (struct atl2_adapter) + * @adapter: board private structure to initialize + * + * atl2_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ +static int __devinit +atl2_sw_init(struct atl2_adapter *adapter) +{ + struct atl2_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + + /* PCI config space info */ + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + hw->subsystem_vendor_id = pdev->subsystem_vendor; + hw->subsystem_id = pdev->subsystem_device; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id); + pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); + + adapter->wol = 0; + adapter->ict = 50000; // 100ms + adapter->link_speed = SPEED_0; // hardware init + adapter->link_duplex = FULL_DUPLEX; + + hw->phy_configured = false; + hw->preamble_len = 7; + hw->ipgt = 0x60; + hw->min_ifg = 0x50; + hw->ipgr1 = 0x40; + hw->ipgr2 = 0x60; + hw->retry_buf = 2; + hw->max_retry = 0xf; + hw->lcol = 0x37; + hw->jam_ipg = 7; + hw->fc_rxd_hi = 0; + hw->fc_rxd_lo = 0; + hw->max_frame_size = adapter->netdev->mtu; + + spin_lock_init(&adapter->stats_lock); + spin_lock_init(&adapter->tx_lock); + + set_bit(__ATL2_DOWN, &adapter->flags); + + return 0; +} + +/** + * atl2_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + **/ +static void +atl2_set_multi(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + struct dev_mc_list *mc_ptr; + u32 rctl; + u32 hash_value; + + /* Check for Promiscuous and All Multicast modes */ + rctl = ATL2_READ_REG(hw, REG_MAC_CTRL); + + if(netdev->flags & IFF_PROMISC) { + rctl |= MAC_CTRL_PROMIS_EN; + } else if(netdev->flags & IFF_ALLMULTI) { + rctl |= MAC_CTRL_MC_ALL_EN; + rctl &= ~MAC_CTRL_PROMIS_EN; + } else { + rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); + } + + ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl); + + /* clear the old settings from the multicast hash table */ + ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + /* comoute mc addresses' hash value ,and put it into hash table */ + for(mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = atl2_hash_mc_addr(hw, mc_ptr->dmi_addr); + atl2_hash_set(hw, hash_value); + } +} + +static void +init_ring_ptrs(struct atl2_adapter *adapter) +{ + // Read / Write Ptr Initialize: + adapter->txd_write_ptr = 0; + atomic_set(&adapter->txd_read_ptr, 0); + + adapter->rxd_read_ptr = 0; + adapter->rxd_write_ptr = 0; + + atomic_set(&adapter->txs_write_ptr, 0); + adapter->txs_next_clear = 0; +} + +/** + * atl2_configure - Configure Transmit&Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Tx /Rx unit of the MAC after a reset. + **/ +static int +atl2_configure(struct atl2_adapter *adapter) +{ + struct atl2_hw * hw = &adapter->hw; + u32 value; + + // clear interrupt status + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff); + + // set MAC Address + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8 ) | + (((u32)hw->mac_addr[5]) ) ; + ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value); + value = (((u32)hw->mac_addr[0]) << 8 ) | + (((u32)hw->mac_addr[1]) ) ; + ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value); + + // HI base address + ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, + (u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32)); + + // LO base address + ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO, + (u32)(adapter->txd_dma & 0x00000000ffffffffULL)); + ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO, + (u32)(adapter->txs_dma & 0x00000000ffffffffULL)); + ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO, + (u32)(adapter->rxd_dma & 0x00000000ffffffffULL)); + + // element count + ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4)); + ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size); + ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM, (u16)adapter->rxd_ring_size); + + /* config Internal SRAM */ +/* + ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end); + ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end); +*/ + + /* config IPG/IFG */ + value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) << MAC_IPG_IFG_IPGT_SHIFT) | + (((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) << MAC_IPG_IFG_MIFG_SHIFT) | + (((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) << MAC_IPG_IFG_IPGR1_SHIFT)| + (((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) << MAC_IPG_IFG_IPGR2_SHIFT); + ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value); + + /* config Half-Duplex Control */ + value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | + (((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) << + MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | + MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | + (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | + (((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) << + MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); + ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value); + + /* set Interrupt Moderator Timer */ + ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt); + ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN); + + /* set Interrupt Clear Timer */ + ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict); + + /* set MTU */ + ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu + + ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE); + + /* 1590 */ + ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177); + + /* flow control */ + ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi); + ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo); + + /* Init mailbox */ + ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr); + ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr); + + /* enable DMA read/write */ + ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN); + ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN); + + value = ATL2_READ_REG(&adapter->hw, REG_ISR); + if ((value & ISR_PHY_LINKDOWN) != 0) { + value = 1; // config failed + } else { + value = 0; + } + + // clear all interrupt status + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff); + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); + return value; +} + +/** + * atl2_setup_ring_resources - allocate Tx / RX descriptor resources + * @adapter: board private structure + * + * Return 0 on success, negative on failure + **/ +static s32 +atl2_setup_ring_resources(struct atl2_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int size; + u8 offset = 0; + + /* real ring DMA buffer */ + adapter->ring_size = size = + adapter->txd_ring_size * 1 + 7 + // dword align + adapter->txs_ring_size * 4 + 7 + // dword align + adapter->rxd_ring_size * 1536 + 127; // 128bytes align + + adapter->ring_vir_addr = pci_alloc_consistent(pdev, size, &adapter->ring_dma); + if (!adapter->ring_vir_addr) { + return -ENOMEM; + } +#if 0 + if (adapter->pci_using_64) { + // test whether HIDWORD dma buffer is not cross boundary + if ( ((adapter->ring_dma &0xffffffff00000000ULL)>>32) + != (((adapter->ring_dma+size)&0xffffffff00000000ULL)>>32) ) { + pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr, adapter->ring_dma); + DEBUGOUT("memory allocated cross 32bit boundary !"); + return -ENOMEM; + } + } +#endif + memset(adapter->ring_vir_addr, 0, adapter->ring_size); + + // Init TXD Ring + adapter->txd_dma = adapter->ring_dma ; + offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0; + adapter->txd_dma += offset; + adapter->txd_ring = (tx_pkt_header_t*) (adapter->ring_vir_addr + offset); + + // Init TXS Ring + adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size; + offset = (adapter->txs_dma & 0x7) ? (8- (adapter->txs_dma & 0x7)) : 0; + adapter->txs_dma += offset; + adapter->txs_ring = (tx_pkt_status_t*) + (((u8*)adapter->txd_ring) + (adapter->txd_ring_size+offset)); + + // Init RXD Ring + adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size*4; + offset = (adapter->rxd_dma & 127) ? (128 - (adapter->rxd_dma & 127)) : 0; + if (offset > 7) { + offset -= 8; + } else { + offset += (128 - 8); + } + adapter->rxd_dma += offset; + adapter->rxd_ring = (rx_desc_t*) (((u8*)adapter->txs_ring) + + (adapter->txs_ring_size*4 + offset)); + +// Read / Write Ptr Initialize: +// init_ring_ptrs(adapter); + + return ATL2_SUCCESS; +} + +/** + * atl2_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + **/ +static inline void +atl2_irq_enable(struct atl2_adapter *adapter) +{ + ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); + ATL2_WRITE_FLUSH(&adapter->hw); +} + +/** + * atl2_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + **/ +static inline void +atl2_irq_disable(struct atl2_adapter *adapter) +{ + ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0); + ATL2_WRITE_FLUSH(&adapter->hw); + synchronize_irq(adapter->pdev->irq); +} + +#ifdef NETIF_F_HW_VLAN_TX +static void +atl2_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + u32 ctrl; + + atl2_irq_disable(adapter); + adapter->vlgrp = grp; + + if(grp) { + /* enable VLAN tag insert/strip */ + ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL); + ctrl |= MAC_CTRL_RMV_VLAN; + ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl); + } else { + /* disable VLAN tag insert/strip */ + ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL); + ctrl &= ~MAC_CTRL_RMV_VLAN; + ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl); + } + + atl2_irq_enable(adapter); +} + +static void +atl2_restore_vlan(struct atl2_adapter *adapter) +{ + atl2_vlan_rx_register(adapter->netdev, adapter->vlgrp); +} +#endif + +static void +atl2_intr_rx(struct atl2_adapter* adapter) +{ + struct net_device *netdev = adapter->netdev; + rx_desc_t* rxd; + struct sk_buff* skb; + + do { + rxd = adapter->rxd_ring+adapter->rxd_write_ptr; + if (!rxd->status.update) + break; // end of tx + + // clear this flag at once + rxd->status.update = 0; + + if (rxd->status.ok && rxd->status.pkt_size >= 60) { + int rx_size = (int)(rxd->status.pkt_size - 4); + // alloc new buffer + skb = netdev_alloc_skb(netdev, rx_size + NET_IP_ALIGN); + if (NULL == skb) { + printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n", + netdev->name); + /* We should check that some rx space is free. + * If not, free one and mark stats->rx_dropped++. */ + adapter->net_stats.rx_dropped++; + break; + } +/* FIXME: ??? + if (rx_size > 1400) { + int s,c; + c = 0; + printk("rx_size= %d\n", rx_size); + for (s=0; s < 800; s++) { + if (0 == c) { + printk("%04x ", s); + } + printk("%02x ", rxd->packet[s]); + if (++c == 16) { + c = 0; + printk("\n"); + } + } + printk(KERN_WARNING"\n"); + } +*/ + skb_reserve(skb, NET_IP_ALIGN); + skb->dev = netdev; +/* gone in 2.6.23, just use memcpy? -- CHS + eth_copy_and_sum(skb, rxd->packet, rx_size, 0); */ + memcpy(skb->data, rxd->packet, rx_size); /* totally untested -- CHS */ + skb_put(skb, rx_size); + /* FIXME ??? + memcpy(skb_put(skb, rx_size), + rxd->packet, + rx_size); + */ + skb->protocol = eth_type_trans(skb, netdev); +#ifdef NETIF_F_HW_VLAN_TX + if(adapter->vlgrp && (rxd->status.vlan)) { + u16 vlan_tag = (rxd->status.vtag>>4) | + ((rxd->status.vtag&7) << 13) | + ((rxd->status.vtag&8) << 9); + vlan_hwaccel_rx(skb, adapter->vlgrp, vlan_tag); + } else +#endif + netif_rx(skb); + adapter->net_stats.rx_bytes += rx_size; + adapter->net_stats.rx_packets++; + netdev->last_rx = jiffies; + } else { + adapter->net_stats.rx_errors++; + + if (rxd->status.ok && rxd->status.pkt_size <= 60) { + adapter->net_stats.rx_length_errors++; + } + if (rxd->status.mcast) adapter->net_stats.multicast++; + if (rxd->status.crc) adapter->net_stats.rx_crc_errors++; + if (rxd->status.align) adapter->net_stats.rx_frame_errors++; + } + + // advance write ptr + if (++adapter->rxd_write_ptr == adapter->rxd_ring_size) + adapter->rxd_write_ptr = 0; + } while (1); + + // update mailbox ? + adapter->rxd_read_ptr = adapter->rxd_write_ptr; + ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr); +} + +static void +atl2_intr_tx(struct atl2_adapter* adapter) +{ + u32 txd_read_ptr; + u32 txs_write_ptr; + tx_pkt_status_t* txs; + tx_pkt_header_t* txph; + int free_hole = 0; + + do { + txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); + txs = adapter->txs_ring + txs_write_ptr; + if (!txs->update) + break; // tx stop here + + free_hole = 1; + txs->update = 0; + + if (++txs_write_ptr == adapter->txs_ring_size) + txs_write_ptr = 0; + atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr); + + txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr); + txph = (tx_pkt_header_t*) (((u8*)adapter->txd_ring) + txd_read_ptr); + + if ( txph->pkt_size != txs->pkt_size) { + tx_pkt_status_t* old_txs = txs; + printk(KERN_WARNING + "%s: txs packet size do not coinsist with txd" + " txd_:0x%08x, txs_:0x%08x!\n", + adapter->netdev->name, + *(u32*)txph, *(u32*)txs); + printk(KERN_WARNING + "txd read ptr: 0x%x\n", + txd_read_ptr); + txs = adapter->txs_ring + txs_write_ptr; + printk(KERN_WARNING + "txs-behind:0x%08x\n", + *(u32*)txs); + if (txs_write_ptr < 2) { + txs = adapter->txs_ring + + (adapter->txs_ring_size + + txs_write_ptr - 2); + } else { + txs = adapter->txs_ring + (txs_write_ptr - 2); + } + printk(KERN_WARNING + "txs-before:0x%08x\n", + *(u32*)txs); + txs = old_txs; + } + + txd_read_ptr += (((u32)(txph->pkt_size)+7)& ~3);//4for TPH + if (txd_read_ptr >= adapter->txd_ring_size) + txd_read_ptr -= adapter->txd_ring_size; + + atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr); + + // tx statistics: + if (txs->ok) + adapter->net_stats.tx_packets++; + else + adapter->net_stats.tx_errors++; + + if (txs->defer) adapter->net_stats.collisions++; + if (txs->abort_col) adapter->net_stats.tx_aborted_errors++; + if (txs->late_col) adapter->net_stats.tx_window_errors++; + if (txs->underun) adapter->net_stats.tx_fifo_errors++; + } while (1); + + if (free_hole) { + if(netif_queue_stopped(adapter->netdev) && + netif_carrier_ok(adapter->netdev)) + netif_wake_queue(adapter->netdev); + } +} + +static void +atl2_check_for_link(struct atl2_adapter* adapter) +{ + struct net_device *netdev = adapter->netdev; + u16 phy_data = 0; + + spin_lock(&adapter->stats_lock); + atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + spin_unlock(&adapter->stats_lock); + + // notify upper layer link down ASAP + if (!(phy_data & BMSR_LSTATUS)) { // Link Down + if (netif_carrier_ok(netdev)) { // old link state: Up + printk(KERN_INFO "%s: %s NIC Link is Down\n", + atl2_driver_name, netdev->name); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } + schedule_work(&adapter->link_chg_task); +} + +static inline void +atl2_clear_phy_int(struct atl2_adapter *adapter) +{ + u16 phy_data; + spin_lock(&adapter->stats_lock); + atl2_read_phy_reg(&adapter->hw, 19, &phy_data); + spin_unlock(&adapter->stats_lock); +} + +/** + * atl2_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + **/ +static irqreturn_t +atl2_intr(int irq, void *data) +{ + struct atl2_adapter *adapter = netdev_priv(data); + struct atl2_hw *hw = &adapter->hw; + u32 status; + + status = ATL2_READ_REG(hw, REG_ISR); + if (0 == status) + return IRQ_NONE; + + // link event + if (status & ISR_PHY) { + atl2_clear_phy_int(adapter); + } + + // clear ISR status, and Enable CMB DMA/Disable Interrupt + ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); + + // FIXME: if PCIe link is down, how did we read the register? -- CHS + // check if PCIE PHY Link down + if (status & ISR_PHY_LINKDOWN) { + if(netif_running(adapter->netdev)) { // reset MAC + ATL2_WRITE_REG(hw, REG_ISR, 0); + ATL2_WRITE_REG(hw, REG_IMR, 0); + ATL2_WRITE_FLUSH(hw); + schedule_work(&adapter->reset_task); + return IRQ_HANDLED; + } + } + + // check if DMA read/write error ? + if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) + { + //ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); + ATL2_WRITE_REG(hw, REG_ISR, 0); + ATL2_WRITE_REG(hw, REG_IMR, 0); + ATL2_WRITE_FLUSH(hw); + schedule_work(&adapter->reset_task); + return IRQ_HANDLED; + } + + // link event + if (status & (ISR_PHY | ISR_MANUAL)) + { + adapter->net_stats.tx_carrier_errors++; + atl2_check_for_link(adapter); + } + + // transmit event + if (status & ISR_TX_EVENT) { + atl2_intr_tx(adapter); + } + + // rx exception + if (status & ISR_RX_EVENT) { + atl2_intr_rx(adapter); + } + + // re-enable Interrupt + ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); + return IRQ_HANDLED; +} + +static int +atl2_request_irq(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int flags, err = 0; + + flags = IRQF_SHARED; +#ifdef CONFIG_PCI_MSI + adapter->have_msi = true; + if ((err = pci_enable_msi(adapter->pdev))) + adapter->have_msi = false; + + if (adapter->have_msi) + flags &= ~IRQF_SHARED; +#endif + + return request_irq(adapter->pdev->irq, &atl2_intr, flags, netdev->name, netdev); +} + +/** + * atl2_free_ring_resources - Free Tx / RX descriptor Resources + * @adapter: board private structure + * + * Free all transmit software resources + **/ +static void +atl2_free_ring_resources(struct atl2_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr, adapter->ring_dma); +} + +/** + * atl2_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + **/ +static int +atl2_open(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + int err; + u32 val; + + /* disallow open during test */ + if (test_bit(__ATL2_TESTING, &adapter->flags)) + return -EBUSY; + + /* allocate transmit descriptors */ + if((err = atl2_setup_ring_resources(adapter))) + return err; + + if((err = atl2_init_hw(&adapter->hw))) { + err = -EIO; + goto err_init_hw; + } + + /* hardware has been reset, we need to reload some things */ + atl2_set_multi(netdev); + init_ring_ptrs(adapter); + +#ifdef NETIF_F_HW_VLAN_TX + atl2_restore_vlan(adapter); +#endif + + if (atl2_configure(adapter)) { + err = -EIO; + goto err_config; + } + + if ((err = atl2_request_irq(adapter))) + goto err_req_irq; + + clear_bit(__ATL2_DOWN, &adapter->flags); + + mod_timer(&adapter->watchdog_timer, jiffies + 4*HZ); + + val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); + ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | MASTER_CTRL_MANUAL_INT); + + atl2_irq_enable(adapter); + + return 0; + +err_init_hw: +err_req_irq: +err_config: + atl2_free_ring_resources(adapter); + atl2_reset_hw(&adapter->hw); + + return err; +} + +void +atl2_down(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + /* signal that we're down so the interrupt handler does not + * reschedule our watchdog timer */ + set_bit(__ATL2_DOWN, &adapter->flags); + +#ifdef NETIF_F_LLTX + netif_stop_queue(netdev); +#else + netif_tx_disable(netdev); +#endif + + /* reset MAC to disable all RX/TX */ + atl2_reset_hw(&adapter->hw); + msleep(1); + + atl2_irq_disable(adapter); + + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_config_timer); + clear_bit(0, &adapter->cfg_phy); + + netif_carrier_off(netdev); + adapter->link_speed = SPEED_0; + adapter->link_duplex = -1; + +// atl2_reset(adapter); +} + +static void +atl2_free_irq(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + free_irq(adapter->pdev->irq, netdev); + +#ifdef CONFIG_PCI_MSI + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); +#endif +} + +/** + * atl2_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + **/ +static int +atl2_close(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); + + atl2_down(adapter); + atl2_free_irq(adapter); + atl2_free_ring_resources(adapter); + + return 0; +} + +static inline int +TxsFreeUnit(struct atl2_adapter *adapter) +{ + u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); + + return (adapter->txs_next_clear >= txs_write_ptr) ? + (int) (adapter->txs_ring_size - adapter->txs_next_clear + + txs_write_ptr - 1) : + (int) (txs_write_ptr - adapter->txs_next_clear - 1); +} + +static inline int +TxdFreeBytes(struct atl2_adapter *adapter) +{ + u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr); + + return (adapter->txd_write_ptr >= txd_read_ptr) ? + (int) (adapter->txd_ring_size - adapter->txd_write_ptr + + txd_read_ptr - 1): + (int) (txd_read_ptr - adapter->txd_write_ptr - 1); +} + +static int +atl2_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + tx_pkt_header_t* txph; + u32 offset, copy_len; + int txs_unused; + int txbuf_unused; + + if (test_bit(__ATL2_DOWN, &adapter->flags)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (unlikely(skb->len <= 0)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + +#ifdef NETIF_F_LLTX + local_irq_save(flags); + if (!spin_trylock(&adapter->tx_lock)) { + /* Collision - tell upper layer to requeue */ + local_irq_restore(flags); + return NETDEV_TX_LOCKED; + } +#else + spin_lock_irqsave(&adapter->tx_lock, flags); +#endif + txs_unused = TxsFreeUnit(adapter); + txbuf_unused = TxdFreeBytes(adapter); + + if (txs_unused < 1 || skb->len > txbuf_unused) { + // no enough resource + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + offset = adapter->txd_write_ptr; + + txph = (tx_pkt_header_t*) (((u8*)adapter->txd_ring)+offset); + + *(u32*)txph = 0; + txph->pkt_size = skb->len; + + offset += 4; + if (offset >= adapter->txd_ring_size) + offset -= adapter->txd_ring_size; + copy_len = adapter->txd_ring_size - offset; + if (copy_len >= skb->len) { + memcpy(((u8*)adapter->txd_ring)+offset, skb->data, skb->len); + offset += ((u32)(skb->len+3)&~3); + } else { + memcpy(((u8*)adapter->txd_ring)+offset, skb->data, copy_len); + memcpy((u8*)adapter->txd_ring, skb->data+copy_len, skb->len-copy_len); + offset = ((u32)(skb->len-copy_len+3)&~3); + } +#ifdef NETIF_F_HW_VLAN_TX + if (adapter->vlgrp && vlan_tx_tag_present(skb)) { + u16 vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = (vlan_tag << 4) | + (vlan_tag >> 13) | + ((vlan_tag >>9) & 0x8); + txph->ins_vlan = 1; + txph->vlan = vlan_tag; + } +#endif + if (offset >= adapter->txd_ring_size) + offset -= adapter->txd_ring_size; + adapter->txd_write_ptr = offset; + + // clear txs before send + adapter->txs_ring[adapter->txs_next_clear].update = 0; + if (++adapter->txs_next_clear == adapter->txs_ring_size) + adapter->txs_next_clear = 0; + + ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX, (adapter->txd_write_ptr >> 2)); + + spin_unlock_irqrestore(&adapter->tx_lock, flags); + + netdev->trans_start = jiffies; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/** + * atl2_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + **/ +static struct net_device_stats * +atl2_get_stats(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + return &adapter->net_stats; +} + +/** + * atl2_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + **/ +static int +atl2_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw *hw = &adapter->hw; + + if ((new_mtu < 40) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE))) + return -EINVAL; + + /* set MTU */ + if (hw->max_frame_size != new_mtu) { +// while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) +// msleep(1); + netdev->mtu = new_mtu; + + ATL2_WRITE_REG(hw, REG_MTU, + new_mtu + ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE); +// clear_bit(__ATL2_RESETTING, &adapter->flags); + } + + return 0; +} + +/** + * atl2_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int +atl2_set_mac(struct net_device *netdev, void *p) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (netif_running(netdev)) + return -EBUSY; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + + atl2_set_mac_addr(&adapter->hw); + + return 0; +} + +/** + * atl2_mii_ioctl - + * @netdev: + * @ifreq: + * @cmd: + **/ +static int +atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + struct mii_ioctl_data *data = if_mii(ifr); + unsigned long flags; + + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_irqsave(&adapter->stats_lock, flags); + if (atl2_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, &data->val_out)) { + spin_unlock_irqrestore(&adapter->stats_lock, flags); + return -EIO; + } + spin_unlock_irqrestore(&adapter->stats_lock, flags); + break; + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (data->reg_num & ~(0x1F)) + return -EFAULT; + spin_lock_irqsave(&adapter->stats_lock, flags); + if (atl2_write_phy_reg(&adapter->hw, data->reg_num, data->val_in)) { + spin_unlock_irqrestore(&adapter->stats_lock, flags); + return -EIO; + } + spin_unlock_irqrestore(&adapter->stats_lock, flags); + break; + default: + return -EOPNOTSUPP; + } + return ATL2_SUCCESS; +} + +/** + * atl2_ioctl - + * @netdev: + * @ifreq: + * @cmd: + **/ +static int +atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return atl2_mii_ioctl(netdev, ifr, cmd); +#ifdef ETHTOOL_OPS_COMPAT + case SIOCETHTOOL: + return ethtool_ioctl(ifr); +#endif + default: + return -EOPNOTSUPP; + } +} + +/** + * atl2_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + **/ +static void +atl2_tx_timeout(struct net_device *netdev) +{ + struct atl2_adapter *adapter = netdev_priv(netdev); + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); +} + +/** + * atl2_watchdog - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + **/ +static void +atl2_watchdog(unsigned long data) +{ + struct atl2_adapter *adapter = (struct atl2_adapter *) data; + u32 drop_rxd, drop_rxs; + unsigned long flags; + + if (!test_bit(__ATL2_DOWN, &adapter->flags)) { + spin_lock_irqsave(&adapter->stats_lock, flags); + drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV); + drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV); + adapter->net_stats.rx_over_errors += (drop_rxd+drop_rxs); + spin_unlock_irqrestore(&adapter->stats_lock, flags); + + /* Reset the timer */ + mod_timer(&adapter->watchdog_timer, jiffies + 4 * HZ); + } +} + +/** + * atl2_phy_config - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + **/ +static void +atl2_phy_config(unsigned long data) +{ + struct atl2_adapter *adapter = (struct atl2_adapter *) data; + struct atl2_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->stats_lock, flags); + atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); + atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET|MII_CR_AUTO_NEG_EN|MII_CR_RESTART_AUTO_NEG); + spin_unlock_irqrestore(&adapter->stats_lock, flags); + clear_bit(0, &adapter->cfg_phy); +} + +int +atl2_up(struct atl2_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int err = 0; + u32 val; + + /* hardware has been reset, we need to reload some things */ + + err = atl2_init_hw(&adapter->hw); + if (err) { + err = -EIO; + return err; + } + + atl2_set_multi(netdev); + init_ring_ptrs(adapter); + +#ifdef NETIF_F_HW_VLAN_TX + atl2_restore_vlan(adapter); +#endif + + if (atl2_configure(adapter)) { + err = -EIO; + goto err_up; + } + + clear_bit(__ATL2_DOWN, &adapter->flags); + + val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); + ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | MASTER_CTRL_MANUAL_INT); + + atl2_irq_enable(adapter); + +err_up: + return err; +} + +void +atl2_reinit_locked(struct atl2_adapter *adapter) +{ + WARN_ON(in_interrupt()); + while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) + msleep(1); + atl2_down(adapter); + atl2_up(adapter); + clear_bit(__ATL2_RESETTING, &adapter->flags); +} + +static void +atl2_reset_task(struct work_struct *work) +{ + struct atl2_adapter *adapter; + adapter = container_of(work, struct atl2_adapter, reset_task); + + atl2_reinit_locked(adapter); +} + +static inline void +atl2_setup_mac_ctrl(struct atl2_adapter *adapter) +{ + u32 value; + struct atl2_hw* hw = &adapter->hw; + struct net_device* netdev = adapter->netdev; + + /* Config MAC CTRL Register */ + value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; + + // duplex + if (FULL_DUPLEX == adapter->link_duplex) + value |= MAC_CTRL_DUPLX; + + // flow control + value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); + + // PAD & CRC + value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + + // preamble length + value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + // vlan + if (adapter->vlgrp) + value |= MAC_CTRL_RMV_VLAN; + + // filter mode + value |= MAC_CTRL_BC_EN; + if (netdev->flags & IFF_PROMISC) + value |= MAC_CTRL_PROMIS_EN; + else if (netdev->flags & IFF_ALLMULTI) + value |= MAC_CTRL_MC_ALL_EN; + + // half retry buffer + value |= (((u32)(adapter->hw.retry_buf & MAC_CTRL_HALF_LEFT_BUF_MASK)) << + MAC_CTRL_HALF_LEFT_BUF_SHIFT); + + ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); +} + +static int +atl2_check_link(struct atl2_adapter *adapter) +{ + struct atl2_hw *hw = &adapter->hw; + struct net_device * netdev = adapter->netdev; + int ret_val; + u16 speed, duplex, phy_data; + int reconfig = 0; + + // MII_BMSR must read twise + atl2_read_phy_reg(hw, MII_BMSR, &phy_data); + atl2_read_phy_reg(hw, MII_BMSR, &phy_data); + if (!(phy_data&BMSR_LSTATUS)) { // link down + if (netif_carrier_ok(netdev)) { // old link state: Up + u32 value; + //disable rx + value = ATL2_READ_REG(hw, REG_MAC_CTRL); + value &= ~MAC_CTRL_RX_EN; + ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + return ATL2_SUCCESS; + } + + // Link Up + ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) + return ret_val; + switch( hw->MediaType ) { + case MEDIA_TYPE_100M_FULL: + if (speed != SPEED_100 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_100M_HALF: + if (speed != SPEED_100 || duplex != HALF_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_10M_FULL: + if (speed != SPEED_10 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_10M_HALF: + if (speed != SPEED_10 || duplex != HALF_DUPLEX) + reconfig = 1; + break; + } + // link result is our setting + if (0 == reconfig) { + if (adapter->link_speed != speed || adapter->link_duplex != duplex ) { + adapter->link_speed = speed; + adapter->link_duplex = duplex; + atl2_setup_mac_ctrl(adapter); + printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n", + atl2_driver_name, netdev->name, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex"); + } + + if (!netif_carrier_ok(netdev)) { // Link down -> Up + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } + return ATL2_SUCCESS; + } + + // change orignal link status + if (netif_carrier_ok(netdev)) { + u32 value; + // disable rx + value = ATL2_READ_REG(hw, REG_MAC_CTRL); + value &= ~MAC_CTRL_RX_EN; + ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); + + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + + // auto-neg, insert timer to re-config phy (if interval smaller than 5 seconds, something strange) + if (!test_bit(__ATL2_DOWN, &adapter->flags)) { + if (!test_and_set_bit(0, &adapter->cfg_phy)) { + mod_timer(&adapter->phy_config_timer, jiffies + 5 * HZ); + } + } + + return ATL2_SUCCESS; +} + +/** + * atl2_link_chg_task - deal with link change event Out of interrupt context + * @netdev: network interface device structure + **/ +static void +atl2_link_chg_task(struct work_struct *work) +{ + struct atl2_adapter *adapter; + unsigned long flags; + + adapter = container_of(work, struct atl2_adapter, link_chg_task); + + spin_lock_irqsave(&adapter->stats_lock, flags); + atl2_check_link(adapter); + spin_unlock_irqrestore(&adapter->stats_lock, flags); +} + +static void +atl2_setup_pcicmd(struct pci_dev *pdev) +{ + u16 cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + + if (cmd & PCI_COMMAND_INTX_DISABLE) + cmd &= ~PCI_COMMAND_INTX_DISABLE; + if (cmd & PCI_COMMAND_IO) + cmd &= ~PCI_COMMAND_IO; + if (0 == (cmd & PCI_COMMAND_MEMORY)) + cmd |= PCI_COMMAND_MEMORY; + if (0 == (cmd & PCI_COMMAND_MASTER)) + cmd |= PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + + /* + * some motherboards BIOS(PXE/EFI) driver may set PME + * while they transfer control to OS (Windows/Linux) + * so we should clear this bit before NIC work normally + */ + pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); +} + +/** + * atl2_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in atl2_pci_tbl + * + * Returns 0 on success, negative on failure + * + * atl2_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int __devinit +atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct atl2_adapter *adapter; + static int cards_found = 0; + unsigned long mmio_start; + int mmio_len; + int err; + + if((err = pci_enable_device(pdev))) + return err; + + /* + * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA + * until the kernel has the proper infrastructure to support 64-bit DMA + * on these devices. + */ + if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) && + (err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))) { + printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n"); + goto err_dma; + } + + // Mark all PCI regions associated with PCI device + // pdev as being reserved by owner atl2_driver_name + if((err = pci_request_regions(pdev, atl2_driver_name))) + goto err_pci_reg; + + // Enables bus-mastering on the device and calls + // pcibios_set_master to do the needed arch specific settings + pci_set_master(pdev); + + err = -ENOMEM; + netdev = alloc_etherdev(sizeof(struct atl2_adapter)); + if(!netdev) + goto err_alloc_etherdev; + + SET_NETDEV_DEV(netdev, &pdev->dev); + + pci_set_drvdata(pdev, netdev); + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.back = adapter; + + mmio_start = pci_resource_start(pdev, 0x0); + mmio_len = pci_resource_len(pdev, 0x0); + + adapter->hw.mem_rang = (u32)mmio_len; + adapter->hw.hw_addr = ioremap(mmio_start, mmio_len); + if(!adapter->hw.hw_addr) { + err = -EIO; + goto err_ioremap; + } + + atl2_setup_pcicmd(pdev); + + netdev->open = &atl2_open; + netdev->stop = &atl2_close; + netdev->hard_start_xmit = &atl2_xmit_frame; + netdev->get_stats = &atl2_get_stats; + netdev->set_multicast_list = &atl2_set_multi; + netdev->set_mac_address = &atl2_set_mac; + netdev->change_mtu = &atl2_change_mtu; + netdev->do_ioctl = &atl2_ioctl; + atl2_set_ethtool_ops(netdev); + +#ifdef HAVE_TX_TIMEOUT + netdev->tx_timeout = &atl2_tx_timeout; + netdev->watchdog_timeo = 5 * HZ; //FIXME -- CHS +#endif +#ifdef NETIF_F_HW_VLAN_TX + netdev->vlan_rx_register = atl2_vlan_rx_register; +#endif + strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); + + netdev->mem_start = mmio_start; + netdev->mem_end = mmio_start + mmio_len; + //netdev->base_addr = adapter->io_base; + adapter->bd_number = cards_found; + adapter->pci_using_64 = false; + + /* setup the private structure */ + + if((err = atl2_sw_init(adapter))) + goto err_sw_init; + + err = -EIO; + +#ifdef NETIF_F_HW_VLAN_TX + netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX ); +#endif + +#ifdef NETIF_F_LLTX + netdev->features |= NETIF_F_LLTX; +#endif + + /* Init PHY as early as possible due to power saving issue */ + atl2_phy_init(&adapter->hw); + + /* reset the controller to + * put the device in a known good starting state */ + + if (atl2_reset_hw(&adapter->hw)) { + err = -EIO; + goto err_reset; + } + + /* copy the MAC address out of the EEPROM */ + atl2_read_mac_addr(&adapter->hw); + memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); +//FIXME: do we still need this? +#ifdef ETHTOOL_GPERMADDR + memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); + + if (!is_valid_ether_addr(netdev->perm_addr)) { +#else + if (!is_valid_ether_addr(netdev->dev_addr)) { +#endif + err = -EIO; + goto err_eeprom; + } + + atl2_check_options(adapter); + + init_timer(&adapter->watchdog_timer); + adapter->watchdog_timer.function = &atl2_watchdog; + adapter->watchdog_timer.data = (unsigned long) adapter; + + init_timer(&adapter->phy_config_timer); + adapter->phy_config_timer.function = &atl2_phy_config; + adapter->phy_config_timer.data = (unsigned long) adapter; + + INIT_WORK(&adapter->reset_task, atl2_reset_task); + INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task); + + strcpy(netdev->name, "eth%d"); // ?? + if((err = register_netdev(netdev))) + goto err_register; + + /* assume we have no link for now */ + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + cards_found++; + + return 0; + +//err_init_hw: +err_reset: +err_register: +err_sw_init: +err_eeprom: + iounmap(adapter->hw.hw_addr); +err_ioremap: + free_netdev(netdev); +err_alloc_etherdev: + pci_release_regions(pdev); +err_pci_reg: +err_dma: + pci_disable_device(pdev); + return err; +} + +/** + * atl2_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * atl2_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +/* FIXME: write the original MAC address back in case it was changed from a + * BIOS-set value, as in atl1 -- CHS */ +static void __devexit +atl2_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl2_adapter *adapter = netdev_priv(netdev); + + /* flush_scheduled work may reschedule our watchdog task, so + * explicitly disable watchdog tasks from being rescheduled */ + set_bit(__ATL2_DOWN, &adapter->flags); + + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_config_timer); + + flush_scheduled_work(); + + unregister_netdev(netdev); + + atl2_force_ps(&adapter->hw); + + iounmap(adapter->hw.hw_addr); + pci_release_regions(pdev); + + free_netdev(netdev); + + pci_disable_device(pdev); +} + +static int +atl2_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl2_adapter *adapter = netdev_priv(netdev); + struct atl2_hw * hw = &adapter->hw; + u16 speed, duplex; + u32 ctrl = 0; + u32 wufc = adapter->wol; + +#ifdef CONFIG_PM + int retval = 0; +#endif + + netif_device_detach(netdev); + + if (netif_running(netdev)) { + WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); + atl2_down(adapter); + } + +#ifdef CONFIG_PM + retval = pci_save_state(pdev); + if (retval) + return retval; +#endif + + atl2_read_phy_reg(hw, MII_BMSR, (u16*)&ctrl); + atl2_read_phy_reg(hw, MII_BMSR, (u16*)&ctrl); + if(ctrl & BMSR_LSTATUS) + wufc &= ~ATL2_WUFC_LNKC; + + if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) { + u32 ret_val; + /* get current link speed & duplex */ + ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + printk(KERN_DEBUG "%s: get speed&duplex error while suspend\n", atl2_driver_name); + goto wol_dis; + } + + ctrl = 0; + + /* turn on magic packet wol */ + if (wufc & ATL2_WUFC_MAG) + ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN); + + /* ignore Link Chg event when Link is up */ + ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); + + /* Config MAC CTRL Register */ + ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; + if (FULL_DUPLEX == adapter->link_duplex) + ctrl |= MAC_CTRL_DUPLX; + ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + ctrl |= (((u32)adapter->hw.preamble_len & + MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); + ctrl |= (((u32)(adapter->hw.retry_buf & + MAC_CTRL_HALF_LEFT_BUF_MASK)) << + MAC_CTRL_HALF_LEFT_BUF_SHIFT); + if (wufc & ATL2_WUFC_MAG) { + /* magic packet maybe Broadcast&multicast&Unicast frame */ + ctrl |= MAC_CTRL_BC_EN; + } + + ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl); + + /* pcie patch */ + ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); + ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + goto suspend_exit; + } + + if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATL2_WUFC_LNKC)) { + /* link is down, so only LINK CHG WOL event enable */ + ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); + ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); + ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0); + + /* pcie patch */ + ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); + ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); + + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + + goto suspend_exit; + } + +wol_dis: + /* WOL disabled */ + ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0); + + /* pcie patch */ + ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); + ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; + ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); + + atl2_force_ps(hw); + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + +suspend_exit: + if (netif_running(netdev)) + atl2_free_irq(adapter); + + pci_disable_device(pdev); + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +#ifdef CONFIG_PM +static int +atl2_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl2_adapter *adapter = netdev_priv(netdev); + u32 err; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if ((err = pci_enable_device(pdev))) { + printk(KERN_ERR "atl2: Cannot enable PCI device from suspend\n"); + return err; + } + + pci_set_master(pdev); + + ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); + + if (netif_running(netdev) && (err = atl2_request_irq(adapter))) + return err; + + atl2_reset_hw(&adapter->hw); + + if(netif_running(netdev)) + atl2_up(adapter); + + netif_device_attach(netdev); + + return 0; +} +#endif + +static void +atl2_shutdown(struct pci_dev *pdev) +{ + atl2_suspend(pdev, PMSG_SUSPEND); +} + +static struct pci_driver atl2_driver = { + .name = atl2_driver_name, + .id_table = atl2_pci_tbl, + .probe = atl2_probe, + .remove = __devexit_p(atl2_remove), + /* Power Managment Hooks */ + .suspend = atl2_suspend, +#ifdef CONFIG_PM + .resume = atl2_resume, +#endif + .shutdown = atl2_shutdown, +}; + +/** + * atl2_init_module - Driver Registration Routine + * + * atl2_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + **/ +static int __init +atl2_init_module(void) +{ + int ret; + printk(KERN_INFO "%s - version %s\n", atl2_driver_string, atl2_driver_version); + printk(KERN_INFO "%s\n", atl2_copyright); + + ret = pci_register_driver(&atl2_driver); + if (copybreak != COPYBREAK_DEFAULT) { + if (copybreak == 0) + printk(KERN_INFO "atl2: copybreak disabled\n"); + else + printk(KERN_INFO "atl2: copybreak enabled for packets <= %u bytes\n", copybreak); + } + return ret; +} +module_init(atl2_init_module); + +/** + * atl2_exit_module - Driver Exit Cleanup Routine + * + * atl2_exit_module is called just before the driver is removed + * from memory. + **/ +static void __exit +atl2_exit_module(void) +{ + pci_unregister_driver(&atl2_driver); +} +module_exit(atl2_exit_module); + +void +atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) +{ + struct atl2_adapter *adapter = hw->back; + pci_read_config_word(adapter->pdev, reg, value); +} + +void +atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) +{ + struct atl2_adapter *adapter = hw->back; + pci_write_config_word(adapter->pdev, reg, *value); +} Index: linux-2.6.26.noarch/drivers/net/atl2/atl2_osdep.h =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2_osdep.h @@ -0,0 +1,72 @@ +/* atl2_osdep.h -- atl2 compat cruft + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ATL2_OSDEP_H_ +#define _ATL2_OSDEP_H_ + +#include +#include +#include +#include + +#define usec_delay(x) udelay(x) +#ifndef msec_delay +#define msec_delay(x) do { \ + if(in_interrupt()) BUG(); \ + else msleep(x); \ + } while (0) + +/* Some workarounds require millisecond delays and are run during interrupt + * context. Most notably, when establishing link, the phy may need tweaking + * but cannot process phy register reads/writes faster than millisecond + * intervals...and we establish link due to a "link status change" interrupt. + */ +#define msec_delay_irq(x) mdelay(x) +#endif + +#define PCI_COMMAND_REGISTER PCI_COMMAND +#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE +#define ETH_ADDR_LEN ETH_ALEN + +#define ATL2_WRITE_REG(a, reg, value) (writel((value), ((a)->hw_addr + reg))) + +#define ATL2_WRITE_FLUSH(a) (readl((a)->hw_addr)) + +#define ATL2_READ_REG(a, reg) (readl((a)->hw_addr + reg)) + +#define ATL2_WRITE_REGB(a, reg, value) (writeb((value), ((a)->hw_addr + reg))) + +#define ATL2_READ_REGB(a, reg) (readb((a)->hw_addr + reg)) + +#define ATL2_WRITE_REGW(a, reg, value) (writew((value), ((a)->hw_addr + reg))) + +#define ATL2_READ_REGW(a, reg) (readw((a)->hw_addr + reg)) + +#define ATL2_WRITE_REG_ARRAY(a, reg, offset, value) \ + (writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) + +#define ATL2_READ_REG_ARRAY(a, reg, offset) \ + (readl(((a)->hw_addr + reg) + ((offset) << 2))) + +#endif /* _ATL2_OSDEP_H_ */ Index: linux-2.6.26.noarch/drivers/net/atl2/atl2_param.c =================================================================== --- /dev/null +++ linux-2.6.26.noarch/drivers/net/atl2/atl2_param.c @@ -0,0 +1,317 @@ +/* atl2_param.c -- atl2 parameter processing + * + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2006 xiong huang + * Copyright(c) 2007 Chris Snook + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "atl2.h" + +/* This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. + */ +#define ATL2_MAX_NIC 4 + +#define OPTION_UNSET -1 +#define OPTION_DISABLED 0 +#define OPTION_ENABLED 1 + +/* All parameters are treated the same, as an integer array of values. + * This macro just reduces the need to repeat the same declaration code + * over and over (plus this helps to avoid typo bugs). + */ +#define ATL2_PARAM_INIT { [0 ... ATL2_MAX_NIC] = OPTION_UNSET } +#ifndef module_param_array +/* Module Parameters are always initialized to -1, so that the driver + * can tell the difference between no user specified value or the + * user asking for the default value. + * The true default values are loaded in when atl2_check_options is called. + * + * This is a GCC extension to ANSI C. + * See the item "Labeled Elements in Initializers" in the section + * "Extensions to the C Language Family" of the GCC documentation. + */ + +#define ATL2_PARAM(X, desc) \ + static const int __devinitdata X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \ + MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \ + MODULE_PARM_DESC(X, desc); +#else +#define ATL2_PARAM(X, desc) \ + static int __devinitdata X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \ + static int num_##X = 0; \ + module_param_array_named(X, X, int, &num_##X, 0); \ + MODULE_PARM_DESC(X, desc); +#endif + +/* Transmit Memory Size + * + * Valid Range: 64-2048 + * + * Default Value: 128 + */ +#define ATL2_MIN_TX_MEMSIZE 4 // 4KB +#define ATL2_MAX_TX_MEMSIZE 64 // 64KB +#define ATL2_DEFAULT_TX_MEMSIZE 8 // 8KB +ATL2_PARAM(TxMemSize, "Bytes of Transmit Memory"); + +/* Receive Memory Block Count + * + * Valid Range: 16-512 + * + * Default Value: 128 + */ +#define ATL2_MIN_RXD_COUNT 16 +#define ATL2_MAX_RXD_COUNT 512 +#define ATL2_DEFAULT_RXD_COUNT 64 +ATL2_PARAM(RxMemBlock, "Number of receive memory block"); + +/* User Specified MediaType Override + * + * Valid Range: 0-5 + * - 0 - auto-negotiate at all supported speeds + * - 1 - only link at 1000Mbps Full Duplex + * - 2 - only link at 100Mbps Full Duplex + * - 3 - only link at 100Mbps Half Duplex + * - 4 - only link at 10Mbps Full Duplex + * - 5 - only link at 10Mbps Half Duplex + * Default Value: 0 + */ +ATL2_PARAM(MediaType, "MediaType Select"); + +/* Interrupt Moderate Timer in units of 2 us + * + * Valid Range: 10-65535 + * + * Default Value: 45000(90ms) + */ +#define INT_MOD_DEFAULT_CNT 100 // 200us +#define INT_MOD_MAX_CNT 65000 +#define INT_MOD_MIN_CNT 50 +ATL2_PARAM(IntModTimer, "Interrupt Moderator Timer"); + +/* FlashVendor + * Valid Range: 0-2 + * 0 - Atmel + * 1 - SST + * 2 - ST + */ +ATL2_PARAM(FlashVendor, "SPI Flash Vendor"); + +#define AUTONEG_ADV_DEFAULT 0x2F +#define AUTONEG_ADV_MASK 0x2F +#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL + +#define FLASH_VENDOR_DEFAULT 0 +#define FLASH_VENDOR_MIN 0 +#define FLASH_VENDOR_MAX 2 + +struct atl2_option { + enum { enable_option, range_option, list_option } type; + char *name; + char *err; + int def; + union { + struct { /* range_option info */ + int min; + int max; + } r; + struct { /* list_option info */ + int nr; + struct atl2_opt_list { int i; char *str; } *p; + } l; + } arg; +}; + +static int __devinit +atl2_validate_option(int *value, struct atl2_option *opt) +{ + int i; + struct atl2_opt_list *ent; + + if(*value == OPTION_UNSET) { + *value = opt->def; + return 0; + } + + switch (opt->type) { + case enable_option: + switch (*value) { + case OPTION_ENABLED: + printk(KERN_INFO "%s Enabled\n", opt->name); + return 0; + break; + case OPTION_DISABLED: + printk(KERN_INFO "%s Disabled\n", opt->name); + return 0; + break; + } + break; + case range_option: + if(*value >= opt->arg.r.min && *value <= opt->arg.r.max) { + printk(KERN_INFO "%s set to %i\n", opt->name, *value); + return 0; + } + break; + case list_option: + for(i = 0; i < opt->arg.l.nr; i++) { + ent = &opt->arg.l.p[i]; + if(*value == ent->i) { + if(ent->str[0] != '\0') + printk(KERN_INFO "%s\n", ent->str); + return 0; + } + } + break; + default: + BUG(); + } + + printk(KERN_INFO "Invalid %s specified (%i) %s\n", + opt->name, *value, opt->err); + *value = opt->def; + return -1; +} + +/** + * atl2_check_options - Range Checking for Command Line Parameters + * @adapter: board private structure + * + * This routine checks all command line parameters for valid user + * input. If an invalid value is given, or if no user specified + * value exists, a default value is used. The final value is stored + * in a variable in the adapter structure. + **/ +void __devinit +atl2_check_options(struct atl2_adapter *adapter) +{ + int val; + struct atl2_option opt; + int bd = adapter->bd_number; + if(bd >= ATL2_MAX_NIC) { + printk(KERN_NOTICE "Warning: no configuration for board #%i\n", bd); + printk(KERN_NOTICE "Using defaults for all values\n"); +#ifndef module_param_array + bd = ATL2_MAX_NIC; +#endif + } + + /* Bytes of Transmit Memory */ + opt.type = range_option; + opt.name = "Bytes of Transmit Memory"; + opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_TX_MEMSIZE); + opt.def = ATL2_DEFAULT_TX_MEMSIZE; + opt.arg.r.min = ATL2_MIN_TX_MEMSIZE; + opt.arg.r.max = ATL2_MAX_TX_MEMSIZE; +#ifdef module_param_array + if(num_TxMemSize > bd) { +#endif + val = TxMemSize[bd]; + atl2_validate_option(&val, &opt); + adapter->txd_ring_size = ((u32) val) * 1024; +#ifdef module_param_array + } else { + adapter->txd_ring_size = ((u32)opt.def) * 1024; + } +#endif + // txs ring size: + adapter->txs_ring_size = adapter->txd_ring_size / 128; + if (adapter->txs_ring_size > 160) + adapter->txs_ring_size = 160; + + /* Receive Memory Block Count */ + opt.type = range_option; + opt.name = "Number of receive memory block"; + opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_RXD_COUNT); + opt.def = ATL2_DEFAULT_RXD_COUNT; + opt.arg.r.min = ATL2_MIN_RXD_COUNT; + opt.arg.r.max = ATL2_MAX_RXD_COUNT; +#ifdef module_param_array + if(num_RxMemBlock > bd) { +#endif + val = RxMemBlock[bd]; + atl2_validate_option(&val, &opt); + adapter->rxd_ring_size = (u32)val; //((u16)val)&~1; // even number +#ifdef module_param_array + } else { + adapter->rxd_ring_size = (u32)opt.def; + } +#endif + // init RXD Flow control value + adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size/8)*7; + adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT/8) > (adapter->rxd_ring_size/12) ? + (ATL2_MIN_RXD_COUNT/8) : (adapter->rxd_ring_size/12); + + /* Interrupt Moderate Timer */ + opt.type = range_option; + opt.name = "Interrupt Moderate Timer"; + opt.err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT); + opt.def = INT_MOD_DEFAULT_CNT; + opt.arg.r.min = INT_MOD_MIN_CNT; + opt.arg.r.max = INT_MOD_MAX_CNT; +#ifdef module_param_array + if(num_IntModTimer > bd) { +#endif + val = IntModTimer[bd]; + atl2_validate_option(&val, &opt); + adapter->imt = (u16) val; +#ifdef module_param_array + } else { + adapter->imt = (u16)(opt.def); + } +#endif + /* Flash Vendor */ + opt.type = range_option; + opt.name = "SPI Flash Vendor"; + opt.err = "using default of " __MODULE_STRING(FLASH_VENDOR_DEFAULT); + opt.def = FLASH_VENDOR_DEFAULT; + opt.arg.r.min = FLASH_VENDOR_MIN; + opt.arg.r.max = FLASH_VENDOR_MAX; +#ifdef module_param_array + if(num_FlashVendor > bd) { +#endif + val = FlashVendor[bd]; + atl2_validate_option(&val, &opt); + adapter->hw.flash_vendor = (u8) val; +#ifdef module_param_array + } else { + adapter->hw.flash_vendor = (u8)(opt.def); + } +#endif + /* MediaType */ + opt.type = range_option; + opt.name = "Speed/Duplex Selection"; + opt.err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR); + opt.def = MEDIA_TYPE_AUTO_SENSOR; + opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR; + opt.arg.r.max = MEDIA_TYPE_10M_HALF; +#ifdef module_param_array + if(num_MediaType > bd) { +#endif + val = MediaType[bd]; + atl2_validate_option(&val, &opt); + adapter->hw.MediaType = (u16) val; +#ifdef module_param_array + } else { + adapter->hw.MediaType = (u16)(opt.def); + } +#endif +}