/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver * Copyright (C) 2004 Advanced Micro Devices * * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ] * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c] * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ] * Derived from the lance driver written 1993,1994,1995 by Donald Becker. * Copyright 1993 United States Government as represented by the * Director, National Security Agency.[ pcnet32.c ] * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ] * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. *
Module Name:
amd8111e.c
Abstract:
AMD8111 based 10/100 Ethernet Controller Driver.
Environment:
Kernel Mode
Revision History: 3.0.0 Initial Revision. 3.0.1 1. Dynamic interrupt coalescing. 2. Removed prev_stats. 3. MII support. 4. Dynamic IPG support 3.0.2 05/29/2003 1. Bug fix: Fixed failure to send jumbo packets larger than 4k. 2. Bug fix: Fixed VLAN support failure. 3. Bug fix: Fixed receive interrupt coalescing bug. 4. Dynamic IPG support is disabled by default. 3.0.3 06/05/2003 1. Bug fix: Fixed failure to close the interface if SMP is enabled. 3.0.4 12/09/2003 1. Added set_mac_address routine for bonding driver support. 2. Tested the driver for bonding support 3. Bug fix: Fixed mismach in actual receive buffer length and length indicated to the h/w. 4. Modified amd8111e_rx() routine to receive all the received packets in the first interrupt. 5. Bug fix: Corrected rx_errors reported in get_stats() function. 3.0.5 03/22/2004 1. Added NAPI support
do {
reg_val = readl(mmio + PHY_ACCESS);
udelay(30); /* It takes 30 us to read/write the data */
} while (--repeat && (reg_val & PHY_CMD_ACTIVE));
if (reg_val & PHY_RD_ERR) goto err_phy_write;
return 0;
err_phy_write: return -EINVAL;
}
/* This is the mii register read function provided to the mii interface. */ staticint amd8111e_mdio_read(struct net_device *dev, int phy_id, int reg_num)
{ struct amd8111e_priv *lp = netdev_priv(dev); unsignedint reg_val;
/* This is the mii register write function provided to the mii interface. */ staticvoid amd8111e_mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
{ struct amd8111e_priv *lp = netdev_priv(dev);
amd8111e_write_phy(lp, phy_id, reg_num, val);
}
/* This function will set PHY speed. During initialization sets * the original speed to 100 full
*/ staticvoid amd8111e_set_ext_phy(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev);
u32 bmcr, advert, tmp;
/* Determine mii register values to set the speed */
advert = amd8111e_mdio_read(dev, lp->ext_phy_addr, MII_ADVERTISE);
tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); switch (lp->ext_phy_option) { default: case SPEED_AUTONEG: /* advertise all values */
tmp |= (ADVERTISE_10HALF | ADVERTISE_10FULL |
ADVERTISE_100HALF | ADVERTISE_100FULL); break; case SPEED10_HALF:
tmp |= ADVERTISE_10HALF; break; case SPEED10_FULL:
tmp |= ADVERTISE_10FULL; break; case SPEED100_HALF:
tmp |= ADVERTISE_100HALF; break; case SPEED100_FULL:
tmp |= ADVERTISE_100FULL; break;
}
/* This function will unmap skb->data space and will free * all transmit and receive skbuffs.
*/ staticint amd8111e_free_skbs(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); struct sk_buff *rx_skbuff; int i;
/* Freeing transmit skbs */ for (i = 0; i < NUM_TX_BUFFERS; i++) { if (lp->tx_skbuff[i]) {
dma_unmap_single(&lp->pci_dev->dev,
lp->tx_dma_addr[i],
lp->tx_skbuff[i]->len, DMA_TO_DEVICE);
dev_kfree_skb(lp->tx_skbuff[i]);
lp->tx_skbuff[i] = NULL;
lp->tx_dma_addr[i] = 0;
}
} /* Freeing previously allocated receive buffers */ for (i = 0; i < NUM_RX_BUFFERS; i++) {
rx_skbuff = lp->rx_skbuff[i]; if (rx_skbuff) {
dma_unmap_single(&lp->pci_dev->dev,
lp->rx_dma_addr[i],
lp->rx_buff_len - 2, DMA_FROM_DEVICE);
dev_kfree_skb(lp->rx_skbuff[i]);
lp->rx_skbuff[i] = NULL;
lp->rx_dma_addr[i] = 0;
}
}
return 0;
}
/* This will set the receive buffer length corresponding * to the mtu size of networkinterface.
*/ staticinlinevoid amd8111e_set_rx_buff_len(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); unsignedint mtu = dev->mtu;
if (mtu > ETH_DATA_LEN) { /* MTU + ethernet header + FCS * + optional VLAN tag + skb reserve space 2
*/
lp->rx_buff_len = mtu + ETH_HLEN + 10;
lp->options |= OPTION_JUMBO_ENABLE;
} else {
lp->rx_buff_len = PKT_BUFF_SZ;
lp->options &= ~OPTION_JUMBO_ENABLE;
}
}
/* This function will free all the previously allocated buffers, * determine new receive buffer length and will allocate new receive buffers. * This function also allocates and initializes both the transmitter * and receive hardware descriptors.
*/ staticint amd8111e_init_ring(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); int i;
/* This function will set the interrupt coalescing according * to the input arguments
*/ staticint amd8111e_set_coalesce(struct net_device *dev, enum coal_mode cmod)
{ unsignedint timeout; unsignedint event_count;
/* This function initializes the device registers and starts the device. */ staticint amd8111e_restart(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); void __iomem *mmio = lp->mmio; int i, reg_val;
/* stop the chip */
writel(RUN, mmio + CMD0);
if (amd8111e_init_ring(dev)) return -ENOMEM;
/* enable the port manager and set auto negotiation always */
writel((u32)VAL1 | EN_PMGR, mmio + CMD3);
writel((u32)XPHYANE | XPHYRST, mmio + CTRL2);
if (lp->options & OPTION_JUMBO_ENABLE)
writel(VAL2 | JUMBO, mmio + CMD3); #if AMD8111E_VLAN_TAG_USED
writel(VAL2 | VSIZE | VL_TAG_DEL, mmio + CMD3); #endif /* Set default value to CTRL1 Register */
writel(CTRL1_DEFAULT, mmio + CTRL1);
/* To avoid PCI posting bug */
readl(mmio + CMD2);
}
/* This function disables the interrupt and clears all the pending * interrupts in INT0
*/ staticvoid amd8111e_disable_interrupt(struct amd8111e_priv *lp)
{
u32 intr0;
/* This function frees the transmiter and receiver descriptor rings. */ staticvoid amd8111e_free_ring(struct amd8111e_priv *lp)
{ /* Free transmit and receive descriptor rings */ if (lp->rx_ring) {
dma_free_coherent(&lp->pci_dev->dev, sizeof(struct amd8111e_rx_dr) * NUM_RX_RING_DR,
lp->rx_ring, lp->rx_ring_dma_addr);
lp->rx_ring = NULL;
}
if (lp->tx_ring) {
dma_free_coherent(&lp->pci_dev->dev, sizeof(struct amd8111e_tx_dr) * NUM_TX_RING_DR,
lp->tx_ring, lp->tx_ring_dma_addr);
lp->tx_ring = NULL;
}
}
/* This function will free all the transmit skbs that are actually * transmitted by the device. It will check the ownership of the * skb before freeing the skb.
*/ staticint amd8111e_tx(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); int tx_index; int status; /* Complete all the transmit packet */ while (lp->tx_complete_idx != lp->tx_idx) {
tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
status = le16_to_cpu(lp->tx_ring[tx_index].tx_flags);
if (status & OWN_BIT) break; /* It still hasn't been Txed */
lp->tx_ring[tx_index].buff_phy_addr = 0;
/* We must free the original skb */ if (lp->tx_skbuff[tx_index]) {
dma_unmap_single(&lp->pci_dev->dev,
lp->tx_dma_addr[tx_index],
lp->tx_skbuff[tx_index]->len,
DMA_TO_DEVICE);
dev_consume_skb_irq(lp->tx_skbuff[tx_index]);
lp->tx_skbuff[tx_index] = NULL;
lp->tx_dma_addr[tx_index] = 0;
}
lp->tx_complete_idx++; /*COAL update tx coalescing parameters */
lp->coal_conf.tx_packets++;
lp->coal_conf.tx_bytes +=
le16_to_cpu(lp->tx_ring[tx_index].buff_count);
if (netif_queue_stopped(dev) &&
lp->tx_complete_idx > lp->tx_idx - NUM_TX_BUFFERS + 2) { /* The ring is no longer full, clear tbusy. */ /* lp->tx_full = 0; */
netif_wake_queue(dev);
}
} return 0;
}
/* This function handles the driver receive operation in polling mode */ staticint amd8111e_rx_poll(struct napi_struct *napi, int budget)
{ struct amd8111e_priv *lp = container_of(napi, struct amd8111e_priv, napi); struct net_device *dev = lp->amd8111e_net_dev; int rx_index = lp->rx_idx & RX_RING_DR_MOD_MASK; void __iomem *mmio = lp->mmio; struct sk_buff *skb, *new_skb; int min_pkt_len, status; int num_rx_pkt = 0; short pkt_len; #if AMD8111E_VLAN_TAG_USED short vtag; #endif
while (num_rx_pkt < budget) {
status = le16_to_cpu(lp->rx_ring[rx_index].rx_flags); if (status & OWN_BIT) break;
/* There is a tricky error noted by John Murphy, * <murf@perftech.com> to Russ Nelson: Even with * full-sized * buffers it's possible for a * jabber packet to use two buffers, with only * the last correctly noting the error.
*/ if (status & ERR_BIT) { /* resetting flags */
lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; goto err_next_pkt;
} /* check for STP and ENP */ if (!((status & STP_BIT) && (status & ENP_BIT))) { /* resetting flags */
lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; goto err_next_pkt;
}
pkt_len = le16_to_cpu(lp->rx_ring[rx_index].msg_count) - 4;
#if AMD8111E_VLAN_TAG_USED
vtag = status & TT_MASK; /* MAC will strip vlan tag */ if (vtag != 0)
min_pkt_len = MIN_PKT_LEN - 4; else #endif
min_pkt_len = MIN_PKT_LEN;
if (pkt_len < min_pkt_len) {
lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
lp->drv_rx_errors++; goto err_next_pkt;
}
new_skb = netdev_alloc_skb(dev, lp->rx_buff_len); if (!new_skb) { /* if allocation fail, * ignore that pkt and go to next one
*/
lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
lp->drv_rx_errors++; goto err_next_pkt;
}
/* This function will indicate the link status to the kernel. */ staticint amd8111e_link_change(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); int status0, speed;
/* read the link change */
status0 = readl(lp->mmio + STAT0);
if (status0 & LINK_STATS) { if (status0 & AUTONEG_COMPLETE)
lp->link_config.autoneg = AUTONEG_ENABLE; else
lp->link_config.autoneg = AUTONEG_DISABLE;
/* This function reads the mib counters. */ staticint amd8111e_read_mib(void __iomem *mmio, u8 MIB_COUNTER)
{ unsignedint status; unsignedint data; unsignedint repeat = REPEAT_CNT;
writew(MIB_RD_CMD | MIB_COUNTER, mmio + MIB_ADDR); do {
status = readw(mmio + MIB_ADDR);
udelay(2); /* controller takes MAX 2 us to get mib data */
} while (--repeat && (status & MIB_CMD_ACTIVE));
data = readl(mmio + MIB_DATA); return data;
}
/* This function reads the mib registers and returns the hardware statistics. * It updates previous internal driver statistics with new values.
*/ staticstruct net_device_stats *amd8111e_get_stats(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); void __iomem *mmio = lp->mmio; unsignedlong flags; struct net_device_stats *new_stats = &dev->stats;
if (!lp->opened) return new_stats;
spin_lock_irqsave(&lp->lock, flags);
/* Reset the mibs for collecting new statistics */ /* writew(MIB_CLEAR, mmio + MIB_ADDR);*/
spin_unlock_irqrestore(&lp->lock, flags);
return new_stats;
}
/* This function recalculate the interrupt coalescing mode on every interrupt * according to the datarate and the packet rate.
*/ staticint amd8111e_calc_coalesce(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); struct amd8111e_coalesce_conf *coal_conf = &lp->coal_conf; int tx_pkt_rate; int rx_pkt_rate; int tx_data_rate; int rx_data_rate; int rx_pkt_size; int tx_pkt_size;
/* This function closes the network interface and updates * the statistics so that most recent statistics will be * available after the interface is down.
*/ staticint amd8111e_close(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev);
netif_stop_queue(dev);
/* Update the statistics before closing */
amd8111e_get_stats(dev);
lp->opened = 0; return 0;
}
/* This function opens new interface.It requests irq for the device, * initializes the device,buffers and descriptors, and starts the device.
*/ staticint amd8111e_open(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev);
if (amd8111e_restart(dev)) {
spin_unlock_irq(&lp->lock);
napi_disable(&lp->napi); if (dev->irq)
free_irq(dev->irq, dev); return -ENOMEM;
} /* Start ipg timer */ if (lp->options & OPTION_DYN_IPG_ENABLE) {
add_timer(&lp->ipg_data.ipg_timer);
netdev_info(dev, "Dynamic IPG Enabled\n");
}
lp->opened = 1;
spin_unlock_irq(&lp->lock);
netif_start_queue(dev);
return 0;
}
/* This function checks if there is any transmit descriptors * available to queue more packet.
*/ staticint amd8111e_tx_queue_avail(struct amd8111e_priv *lp)
{ int tx_index = lp->tx_idx & TX_BUFF_MOD_MASK; if (lp->tx_skbuff[tx_index]) return -1; else return 0;
}
/* This function will queue the transmit packets to the * descriptors and will trigger the send operation. It also * initializes the transmit descriptors with buffer physical address, * byte count, ownership to hardware etc.
*/ static netdev_tx_t amd8111e_start_xmit(struct sk_buff *skb, struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); int tx_index; unsignedlong flags;
/* This function sets promiscuos mode, all-multi mode or the multicast address * list to the device.
*/ staticvoid amd8111e_set_multicast_list(struct net_device *dev)
{ struct netdev_hw_addr *ha; struct amd8111e_priv *lp = netdev_priv(dev);
u32 mc_filter[2]; int bit_num;
eth_hw_addr_set(dev, addr->sa_data);
spin_lock_irq(&lp->lock); /* Setting the MAC address to the device */ for (i = 0; i < ETH_ALEN; i++)
writeb(dev->dev_addr[i], lp->mmio + PADR + i);
spin_unlock_irq(&lp->lock);
return 0;
}
/* This function changes the mtu of the device. It restarts the device to * initialize the descriptor with new receive buffers.
*/ staticint amd8111e_change_mtu(struct net_device *dev, int new_mtu)
{ struct amd8111e_priv *lp = netdev_priv(dev); int err;
if (!netif_running(dev)) { /* new_mtu will be used * when device starts next time
*/
WRITE_ONCE(dev->mtu, new_mtu); return 0;
}
spin_lock_irq(&lp->lock);
/* stop the chip */
writel(RUN, lp->mmio + CMD0);
WRITE_ONCE(dev->mtu, new_mtu);
err = amd8111e_restart(dev);
spin_unlock_irq(&lp->lock); if (!err)
netif_start_queue(dev); return err;
}
/* This function is called when a packet transmission fails to complete * within a reasonable period, on the assumption that an interrupt have * failed or the interface is locked up. This function will reinitialize * the hardware.
*/ staticvoid amd8111e_tx_timeout(struct net_device *dev, unsignedint txqueue)
{ struct amd8111e_priv *lp = netdev_priv(dev); int err;
if (lp->options & OPTION_WOL_ENABLE) { /* enable wol */ if (lp->options & OPTION_WAKE_MAGIC_ENABLE)
amd8111e_enable_magicpkt(lp); if (lp->options & OPTION_WAKE_PHY_ENABLE)
amd8111e_enable_link_change(lp);
/* Initializing MAC address */ for (i = 0; i < ETH_ALEN; i++)
addr[i] = readb(lp->mmio + PADR + i);
eth_hw_addr_set(dev, addr);
/* Setting user defined parametrs */
lp->ext_phy_option = speed_duplex[card_idx]; if (coalesce[card_idx])
lp->options |= OPTION_INTR_COAL_ENABLE; if (dynamic_ipg[card_idx++])
lp->options |= OPTION_DYN_IPG_ENABLE;
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.