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;
return0;
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 *theoriginalspeedto100full
*/ 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 *alltransmitandreceiveskbuffs.
*/ 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;
}
}
return0;
}
/* This will set the receive buffer length corresponding *tothemtusizeofnetworkinterface.
*/ staticinlinevoid amd8111e_set_rx_buff_len(struct net_device *dev)
{ struct amd8111e_priv *lp = netdev_priv(dev); unsignedint mtu = dev->mtu;
/* This function will free all the previously allocated buffers, *determinenewreceivebufferlengthandwillallocatenewreceivebuffers. *Thisfunctionalsoallocatesandinitializesboththetransmitter *andreceivehardwaredescriptors.
*/ 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 *totheinputarguments
*/ 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 *interruptsinINT0
*/ 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 *transmittedbythedevice.Itwillchecktheownershipofthe *skbbeforefreeingtheskb.
*/ 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);
}
} return0;
}
/* 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>toRussNelson:Evenwith *full-sized*buffersit'spossiblefora *jabberpackettousetwobuffers,withonly *thelastcorrectlynotingtheerror.
*/ 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;
/* 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. *Itupdatespreviousinternaldriverstatisticswithnewvalues.
*/ 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 *accordingtothedatarateandthepacketrate.
*/ 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;
/* Update the statistics before closing */
amd8111e_get_stats(dev);
lp->opened = 0; return0;
}
/* This function opens new interface.It requests irq for the device, *initializesthedevice,buffersanddescriptors,andstartsthedevice.
*/ 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);
return0;
}
/* This function checks if there is any transmit descriptors *availabletoqueuemorepacket.
*/ 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 return0;
}
/* This function will queue the transmit packets to the *descriptorsandwilltriggerthesendoperation.Italso *initializesthetransmitdescriptorswithbufferphysicaladdress, *bytecount,ownershiptohardwareetc.
*/ 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;
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);
return0;
}
/* This function changes the mtu of the device. It restarts the device to *initializethedescriptorwithnewreceivebuffers.
*/ 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 *whendevicestartsnexttime
*/
WRITE_ONCE(dev->mtu, new_mtu); return0;
}
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 *withinareasonableperiod,ontheassumptionthataninterrupthave *failedortheinterfaceislockedup.Thisfunctionwillreinitialize *thehardware.
*/ 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.