📄 921x_4_linux.c
字号:
}
/* NAPI poll function */
static int smsc911x_poll(struct net_device *dev, int *budget)
{
struct smsc911x_data *pdata = netdev_priv(dev);
int npackets = 0;
int quota = min(dev->quota, *budget);
while (npackets < quota) {
unsigned int pktlength;
unsigned int pktwords;
unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
/* break out of while loop if there are no more packets waiting */
if (!rxstat)
break;
pktlength = ((rxstat & 0x3FFF0000) >> 16);
pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
smsc911x_rx_counterrors(pdata, rxstat);
if (likely((rxstat & RX_STS_ES_) == 0)) {
struct sk_buff *skb;
skb = dev_alloc_skb(pktlength + NET_IP_ALIGN);
if (likely(skb)) {
skb->data = skb->head;
skb->tail = skb->head;
/* Align IP on 16B boundary */
skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, pktlength - 4);
smsc911x_rx_readfifo(pdata,
(unsigned int *)skb->head,
pktwords);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
netif_receive_skb(skb);
/* Update counters */
pdata->stats.rx_packets++;
pdata->stats.rx_bytes += (pktlength - 4);
dev->last_rx = jiffies;
npackets++;
continue;
} else {
SMSC_WARNING("Unable to allocate sk_buff "
"for rx packet, in PIO path");
pdata->stats.rx_dropped++;
}
}
/* At this point, the packet is to be read out
* of the fifo and discarded */
smsc911x_rx_fastforward(pdata, pktwords);
}
pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
smsc911x_reg_write(INT_STS_RSFL_, pdata, INT_STS);
*budget -= npackets;
dev->quota -= npackets;
if (npackets < quota) {
unsigned int temp;
/* We processed all packets available. Tell NAPI it can
* stop polling then re-enable rx interrupts */
netif_rx_complete(dev);
temp = smsc911x_reg_read(pdata, INT_EN);
temp |= INT_EN_RSFL_EN_;
smsc911x_reg_write(temp, pdata, INT_EN);
return 0;
}
/* There are still packets waiting */
return 1;
}
/* Returns hash bit number for given MAC address
* Example:
* 01 00 5E 00 00 01 -> returns bit number 31 */
static unsigned int smsc911x_hash(char addr[ETH_ALEN])
{
unsigned int crc;
unsigned int result;
crc = ether_crc(ETH_ALEN, addr);
result = (crc >> 26) & 0x3f;
return result;
}
static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
{
/* Performs the multicast & mac_cr update. This is called when
* safe on the current hardware, and with the phy_lock held */
unsigned int mac_cr = smsc911x_mac_read(pdata, MAC_CR);
mac_cr |= pdata->set_bits_mask;
mac_cr &= ~(pdata->clear_bits_mask);
smsc911x_mac_write(pdata, MAC_CR, mac_cr);
smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
SMSC_TRACE("maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X", mac_cr,
pdata->hashhi, pdata->hashlo);
}
static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
{
unsigned int mac_cr;
/* This function is only called for older LAN911x devices
* (revA or revB), where MAC_CR, HASHH and HASHL should not
* be modified during Rx - newer devices immediately update the
* registers.
*
* This is called from interrupt context */
spin_lock(&pdata->phy_lock);
/* Check Rx has stopped */
if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
SMSC_WARNING("Rx not stopped\n");
/* Perform the update - safe to do now Rx has stopped */
smsc911x_rx_multicast_update(pdata);
/* Re-enable Rx */
mac_cr = smsc911x_mac_read(pdata, MAC_CR);
mac_cr |= MAC_CR_RXEN_;
smsc911x_mac_write(pdata, MAC_CR, mac_cr);
pdata->multicast_update_pending = 0;
spin_unlock(&pdata->phy_lock);
}
static int smsc911x_soft_reset(struct smsc911x_data *pdata)
{
unsigned int timeout;
unsigned int temp;
/* Reset the LAN911x */
smsc911x_reg_write(HW_CFG_SRST_, pdata, HW_CFG);
timeout = 10;
do {
udelay(10);
temp = smsc911x_reg_read(pdata, HW_CFG);
} while ((--timeout) && (temp & HW_CFG_SRST_));
if (unlikely(temp & HW_CFG_SRST_)) {
SMSC_WARNING("Failed to complete reset");
return -ENODEV;
}
return 0;
}
static int smsc911x_open(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
unsigned int mac_high16;
unsigned int mac_low32;
unsigned int timeout;
unsigned int temp;
spin_lock_init(&pdata->phy_lock);
/* Reset the LAN911x */
if (smsc911x_soft_reset(pdata))
return -ENODEV;
smsc911x_reg_write(0x00050000, pdata, HW_CFG);
smsc911x_reg_write(0x006E3740, pdata, AFC_CFG);
/* Make sure EEPROM has finished loading before setting GPIO_CFG */
timeout = 50;
while ((timeout--) &&
(smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
udelay(10);
}
if (unlikely(timeout == 0)) {
SMSC_WARNING("Timed out waiting for EEPROM "
"busy bit to clear\n");
}
#if USE_DEBUG >= 1
smsc911x_reg_write(0x00670700, pdata, GPIO_CFG);
#else
smsc911x_reg_write(0x70070000, pdata, GPIO_CFG);
#endif
/* Initialise irqs, but leave all sources disabled */
smsc911x_reg_write(0, pdata, INT_EN);
smsc911x_reg_write(0xFFFFFFFF, pdata, INT_STS);
/* Set interrupt deassertion to 100uS */
smsc911x_reg_write(((10 << 24) | INT_CFG_IRQ_EN_), pdata, INT_CFG);
/*
* intcfg |= INT_CFG_IRQ_POL_; use this to set IRQ_POL bit
* intcfg |= INT_CFG_IRQ_TYPE_; use this to set IRQ_TYPE bit
*/
SMSC_TRACE("Testing irq handler using IRQ %d", dev->irq);
pdata->software_irq_signal = 0;
smp_wmb();
temp = smsc911x_reg_read(pdata, INT_EN);
temp |= INT_EN_SW_INT_EN_;
smsc911x_reg_write(temp, pdata, INT_EN);
timeout = 1000;
while (timeout--) {
smp_rmb();
if (pdata->software_irq_signal)
break;
//msleep(1);
mdelay(1);
}
if (!pdata->software_irq_signal) {
printk(KERN_WARNING "%s: ISR failed signaling test (IRQ %d)\n",
dev->name, dev->irq);
return -ENODEV;
}
SMSC_TRACE("IRQ handler passed test using IRQ %d", dev->irq);
printk(KERN_INFO "%s: SMSC911x/921x identified at %#08lx, IRQ: %d\n",
dev->name, (unsigned long)pdata->ioaddr, dev->irq);
spin_lock_irq(&pdata->phy_lock);
/* Read mac address from EEPROM */
mac_high16 = smsc911x_mac_read(pdata, ADDRH);
mac_low32 = smsc911x_mac_read(pdata, ADDRL);
/* Generate random MAC address if eeprom values are invalid */
if ((mac_high16 == 0x0000FFFF) && (mac_low32 == 0xFFFFFFFF)) {
u8 random_mac[6];
random_ether_addr(random_mac);
mac_high16 = (random_mac[5] << 8) | random_mac[4];
mac_low32 = (random_mac[3] << 24) | (random_mac[2] << 16) |
(random_mac[1] << 8) | random_mac[0];
smsc911x_mac_write(pdata, ADDRH, mac_high16);
smsc911x_mac_write(pdata, ADDRL, mac_low32);
SMSC_TRACE("MAC Address is set to random_ether_addr");
} else {
SMSC_TRACE("Mac Address is read from LAN911x EEPROM");
}
spin_unlock_irq(&pdata->phy_lock);
dev->dev_addr[0] = (u8)(mac_low32);
dev->dev_addr[1] = (u8)(mac_low32 >> 8);
dev->dev_addr[2] = (u8)(mac_low32 >> 16);
dev->dev_addr[3] = (u8)(mac_low32 >> 24);
dev->dev_addr[4] = (u8)(mac_high16);
dev->dev_addr[5] = (u8)(mac_high16 >> 8);
printk(KERN_INFO
"%s: SMSC911x MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
netif_carrier_off(dev);
if (!smsc911x_phy_initialise(dev)) {
SMSC_WARNING("Failed to initialize PHY");
return -ENODEV;
}
temp = smsc911x_reg_read(pdata, HW_CFG);
/* Preserve TX FIFO size and external PHY configuration */
temp &= (HW_CFG_TX_FIF_SZ_|0x00000FFF);
temp |= HW_CFG_SF_;
smsc911x_reg_write(temp, pdata, HW_CFG);
temp = smsc911x_reg_read(pdata, FIFO_INT);
temp |= FIFO_INT_TX_AVAIL_LEVEL_;
temp &= ~(FIFO_INT_RX_STS_LEVEL_);
smsc911x_reg_write(temp, pdata, FIFO_INT);
/* set RX Data offset to 2 bytes for alignment */
smsc911x_reg_write((2 << 8), pdata, RX_CFG);
temp = smsc911x_reg_read(pdata, INT_EN);
temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_PHY_INT_EN_);
smsc911x_reg_write(temp, pdata, INT_EN);
spin_lock_irq(&pdata->phy_lock);
temp = smsc911x_mac_read(pdata, MAC_CR);
temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
smsc911x_mac_write(pdata, MAC_CR, temp);
spin_unlock_irq(&pdata->phy_lock);
smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
netif_start_queue(dev);
return 0;
}
/* Entry point for stopping the interface */
static int smsc911x_stop(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
pdata->stop_link_poll = 1;
del_timer_sync(&pdata->link_poll_timer);
smsc911x_reg_write((smsc911x_reg_read(pdata, INT_CFG) &
(~INT_CFG_IRQ_EN_)), pdata, INT_CFG);
netif_stop_queue(dev);
/* At this point all Rx and Tx activity is stopped */
pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
smsc911x_tx_update_txcounters(pdata);
SMSC_TRACE("<--Simp911x_stop");
return 0;
}
/* Entry point for transmitting a packet */
static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
unsigned int freespace;
unsigned int tx_cmd_a;
unsigned int tx_cmd_b;
unsigned int temp;
u32 wrsz;
u32 bufp;
freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
SMSC_WARNING("Tx data fifo low, space available: %d",
freespace);
/* Word alignment adjustment */
tx_cmd_a = ((((unsigned int)(skb->data)) & 0x03) << 16);
tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
tx_cmd_a |= (unsigned int)skb->len;
tx_cmd_b = ((unsigned int)skb->len) << 16;
tx_cmd_b |= (unsigned int)skb->len;
smsc911x_reg_write(tx_cmd_a, pdata, TX_DATA_FIFO);
smsc911x_reg_write(tx_cmd_b, pdata, TX_DATA_FIFO);
bufp = ((u32)skb->data) & 0xFFFFFFFC;
wrsz = (u32)skb->len + 3;
wrsz += ((u32)skb->data) & 0x3;
wrsz >>= 2;
smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
dev_kfree_skb(skb);
freespace -= (skb->len + 32);
dev->trans_start = jiffies;
if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
smsc911x_tx_update_txcounters(pdata);
if (freespace < TX_FIFO_LOW_THRESHOLD) {
netif_stop_queue(dev);
temp = smsc911x_reg_read(pdata, FIFO_INT);
temp &= 0x00FFFFFF;
temp |= 0x32000000;
smsc911x_reg_write(temp, pdata, FIFO_INT);
}
return NETDEV_TX_OK;
}
/* Entry point for getting status counters */
static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
smsc911x_tx_update_txcounters(pdata);
return &pdata->stats;
}
/* Entry point for setting addressing modes */
static void smsc911x_set_multicast_list(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
unsigned long flags;
if (dev->flags & IFF_PROMISC) {
/* Enabling promiscuous mode */
pdata->set_bits_mask = MAC_CR_PRMS_;
pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
pdata->hashhi = 0;
pdata->hashlo = 0;
} else if (dev->flags & IFF_ALLMULTI) {
/* Enabling all multicast mode */
pdata->set_bits_mask = MAC_CR_MCPAS_;
pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
pdata->hashhi = 0;
pdata->hashlo = 0;
} else if (dev->mc_count > 0) {
/* Enabling specific multicast addresses */
unsigned int hash_high = 0;
unsigned int hash_low = 0;
unsigned int count = 0;
struct dev_mc_list *mc_list = dev->mc_list;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -