/** * 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.
*/ staticvoid atl2_set_multi(struct net_device *netdev)
{ struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; struct netdev_hw_addr *ha;
u32 rctl;
u32 hash_value;
/* Check for Promiscuous and All Multicast modes */
rctl = ATL2_READ_REG(hw, REG_MAC_CTRL);
/* clear the old settings from the multicast hash table */
ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
/* comoute mc addresses' hash value ,and put it into hash table */
netdev_for_each_mc_addr(ha, netdev) {
hash_value = atl2_hash_mc_addr(hw, ha->addr);
atl2_hash_set(hw, hash_value);
}
}
/** * atl2_configure - Configure Transmit&Receive Unit after Reset * @adapter: board private structure * * Configure the Tx /Rx unit of the MAC after a reset.
*/ staticint 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));
static netdev_features_t atl2_fix_features(struct net_device *netdev,
netdev_features_t features)
{ /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx.
*/ if (features & NETIF_F_HW_VLAN_CTAG_RX)
features |= NETIF_F_HW_VLAN_CTAG_TX; else
features &= ~NETIF_F_HW_VLAN_CTAG_TX;
if (rxd->status.ok && rxd->status.pkt_size <= 60)
netdev->stats.rx_length_errors++; if (rxd->status.mcast)
netdev->stats.multicast++; if (rxd->status.crc)
netdev->stats.rx_crc_errors++; if (rxd->status.align)
netdev->stats.rx_frame_errors++;
}
/* advance write ptr */ if (++adapter->rxd_write_ptr == adapter->rxd_ring_size)
adapter->rxd_write_ptr = 0;
} while (1);
if (txs->defer)
netdev->stats.collisions++; if (txs->abort_col)
netdev->stats.tx_aborted_errors++; if (txs->late_col)
netdev->stats.tx_window_errors++; if (txs->underrun)
netdev->stats.tx_fifo_errors++;
} while (1);
if (free_hole) { if (netif_queue_stopped(adapter->netdev) &&
netif_carrier_ok(adapter->netdev))
netif_wake_queue(adapter->netdev);
}
}
/* 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);
}
/** * 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.
*/ staticint 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;
#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.
*/ staticint atl2_close(struct net_device *netdev)
{ struct atl2_adapter *adapter = netdev_priv(netdev);
/** * 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
*/ staticint atl2_change_mtu(struct net_device *netdev, int new_mtu)
{ struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw;
/* set MTU */
WRITE_ONCE(netdev->mtu, new_mtu);
hw->max_frame_size = new_mtu;
ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ETH_HLEN +
VLAN_HLEN + ETH_FCS_LEN);
return 0;
}
/** * atl2_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure * @p: pointer to an address structure * * Returns 0 on success, negative on failure
*/ staticint 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;
/* MII_BMSR must read twise */
atl2_read_phy_reg(hw, MII_BMSR, &phy_data);
atl2_read_phy_reg(hw, MII_BMSR, &phy_data); if (!(phy_data&BMSR_LSTATUS)) { /* link down */ if (netif_carrier_ok(netdev)) { /* old link state: Up */
u32 value; /* disable rx */
value = ATL2_READ_REG(hw, REG_MAC_CTRL);
value &= ~MAC_CTRL_RX_EN;
ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
adapter->link_speed = SPEED_0;
netif_carrier_off(netdev);
netif_stop_queue(netdev);
} return 0;
}
/* Link Up */
ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); if (ret_val) return ret_val; switch (hw->MediaType) { case MEDIA_TYPE_100M_FULL: if (speed != SPEED_100 || duplex != FULL_DUPLEX)
reconfig = 1; break; case MEDIA_TYPE_100M_HALF: if (speed != SPEED_100 || duplex != HALF_DUPLEX)
reconfig = 1; break; case MEDIA_TYPE_10M_FULL: if (speed != SPEED_10 || duplex != FULL_DUPLEX)
reconfig = 1; break; case MEDIA_TYPE_10M_HALF: if (speed != SPEED_10 || duplex != HALF_DUPLEX)
reconfig = 1; break;
} /* link result is our setting */ if (reconfig == 0) { if (adapter->link_speed != speed ||
adapter->link_duplex != duplex) {
adapter->link_speed = speed;
adapter->link_duplex = duplex;
atl2_setup_mac_ctrl(adapter);
printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n",
atl2_driver_name, netdev->name,
adapter->link_speed,
adapter->link_duplex == FULL_DUPLEX ? "Full Duplex" : "Half Duplex");
}
if (!netif_carrier_ok(netdev)) { /* Link down -> Up */
netif_carrier_on(netdev);
netif_wake_queue(netdev);
} return 0;
}
/* change original link status */ if (netif_carrier_ok(netdev)) {
u32 value; /* disable rx */
value = ATL2_READ_REG(hw, REG_MAC_CTRL);
value &= ~MAC_CTRL_RX_EN;
ATL2_WRITE_REG(hw, REG_MAC_CTRL, value);
/* auto-neg, insert timer to re-config phy
* (if interval smaller than 5 seconds, something strange) */ if (!test_bit(__ATL2_DOWN, &adapter->flags)) { if (!test_and_set_bit(0, &adapter->cfg_phy))
mod_timer(&adapter->phy_config_timer,
round_jiffies(jiffies + 5 * HZ));
}
return 0;
}
/** * atl2_link_chg_task - deal with link change event Out of interrupt context * @work: pointer to work struct with private info
*/ staticvoid atl2_link_chg_task(struct work_struct *work)
{ struct atl2_adapter *adapter; unsignedlong flags;
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.
*/ staticint atl2_probe(struct pci_dev *pdev, conststruct pci_device_id *ent)
{ struct net_device *netdev; struct atl2_adapter *adapter; staticint cards_found = 0; unsignedlong mmio_start; int mmio_len; int err;
err = pci_enable_device(pdev); if (err) return err;
/* * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA * until the kernel has the proper infrastructure to support 64-bit DMA * on these devices.
*/ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) &&
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) {
printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n");
err = -EIO; goto err_dma;
}
/* Mark all PCI regions associated with PCI device
* pdev as being reserved by owner atl2_driver_name */
err = pci_request_regions(pdev, atl2_driver_name); if (err) goto err_pci_reg;
/* Enables bus-mastering on the device and calls
* pcibios_set_master to do the needed arch specific settings */
pci_set_master(pdev);
/* Init PHY as early as possible due to power saving issue */
atl2_phy_init(&adapter->hw);
/* reset the controller to
* put the device in a known good starting state */
if (atl2_reset_hw(&adapter->hw)) {
err = -EIO; goto err_reset;
}
/* copy the MAC address out of the EEPROM */
atl2_read_mac_addr(&adapter->hw);
eth_hw_addr_set(netdev, adapter->hw.mac_addr); if (!is_valid_ether_addr(netdev->dev_addr)) {
err = -EIO; goto err_eeprom;
}
/** * atl2_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 */ staticvoid 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);
if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) { /* link is down, so only LINK CHG WOL event enable */
ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl);
ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0);
/* * It's sane for this to be empty, but we might want to take advantage of this.
*/ staticvoid atl2_set_msglevel(struct net_device *netdev, u32 data)
{
}
if (eeprom->offset & 3) { /* need read/modify/write of first changed EEPROM word */ /* only the second byte of the word is being modified */ if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) {
ret_val = -EIO; goto out;
}
ptr++;
} if (((eeprom->offset + eeprom->len) & 3)) { /* * need read/modify/write of last changed EEPROM word * only the first byte of the word is being modified
*/ if (!atl2_read_eeprom(hw, last_dword * 4,
&(eeprom_buff[last_dword - first_dword]))) {
ret_val = -EIO; goto out;
}
}
/* Device's eeprom is always little-endian, word addressable */
memcpy(ptr, bytes, eeprom->len);
for (i = 0; i < last_dword - first_dword + 1; i++) { if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) {
ret_val = -EIO; goto out;
}
}
out:
kfree(eeprom_buff); return ret_val;
}
/* Issue Soft Reset to the MAC. This will reset the chip's * transmit, receive, DMA. It will not effect * the current PCI configuration. The global reset bit is self- * clearing, and should clear within a microsecond.
*/
ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST);
wmb();
msleep(1); /* delay about 1ms */
/* Wait at least 10ms for All module to be Idle */ for (i = 0; i < 10; i++) {
icr = ATL2_READ_REG(hw, REG_IDLE_STATUS); if (!icr) break;
msleep(1); /* delay 1 ms */
cpu_relax();
}
/* * Hashes an address to determine its location in the multicast table * * hw - Struct containing variables accessed by shared code * mc_addr - the multicast address to hash * * atl2_hash_mc_addr * purpose * set hash value for a multicast address * hash calcu processing : * 1. calcu 32bit CRC for multicast address * 2. reverse crc with MSB to LSB
*/ static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr)
{
u32 crc32, value; int i;
value = 0;
crc32 = ether_crc_le(6, mc_addr);
for (i = 0; i < 32; i++)
value |= (((crc32 >> i) & 1) << (31 - i));
return value;
}
/* * Sets the bit in the multicast table corresponding to the hash value. * * hw - Struct containing variables accessed by shared code * hash_value - Multicast address hash value
*/ staticvoid 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);
/******************************************************************** * Performs basic configuration of the adapter. * * hw - Struct containing variables accessed by shared code * Assumes that the controller has previously been reset and is in a * post-reset uninitialized state. Initializes multicast table, * and Calls routines to setup link * Leaves the transmit and receive units disabled and uninitialized.
********************************************************************/ static s32 atl2_init_hw(struct atl2_hw *hw)
{
u32 ret_val = 0;
atl2_init_pcie(hw);
/* Zero out the Multicast HASH table */ /* clear the old settings from the multicast hash table */
ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
atl2_init_flash_opcode(hw);
ret_val = atl2_phy_init(hw);
return ret_val;
}
/* * Detects the current speed and duplex settings of the hardware. * * hw - Struct containing variables accessed by shared code * speed - Speed of the connection * duplex - Duplex setting of the connection
*/ static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed,
u16 *duplex)
{
s32 ret_val;
u16 phy_data;
/* Read PHY Specific Status Register (17) */
ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data); if (ret_val) return ret_val;
if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED)) return ATLX_ERR_PHY_RES;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.