📄 smc911x.c
字号:
if (status & INT_STS_RDFO_) { if (!IS_REV_A(lp->revision)) { SMC_GET_MAC_CR(cr); cr &= ~MAC_CR_RXEN_; SMC_SET_MAC_CR(cr); rx_overrun=1; DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name); dev->stats.rx_errors++; dev->stats.rx_fifo_errors++; } SMC_ACK_INT(INT_STS_RDFO_); } /* Handle receive condition */ if ((status & INT_STS_RSFL_) || rx_overrun) { unsigned int fifo; DBG(SMC_DEBUG_RX, "%s: RX irq\n", dev->name); fifo = SMC_GET_RX_FIFO_INF(); pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16; DBG(SMC_DEBUG_RX, "%s: Rx FIFO pkts %d, bytes %d\n", dev->name, pkts, fifo & 0xFFFF ); if (pkts != 0) {#ifdef SMC_USE_DMA unsigned int fifo; if (lp->rxdma_active){ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA active\n", dev->name); /* The DMA is already running so up the IRQ threshold */ fifo = SMC_GET_FIFO_INT() & ~0xFF; fifo |= pkts & 0xFF; DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n", dev->name, fifo & 0xff); SMC_SET_FIFO_INT(fifo); } else#endif smc911x_rcv(dev); } SMC_ACK_INT(INT_STS_RSFL_); } /* Handle transmit FIFO available */ if (status & INT_STS_TDFA_) { DBG(SMC_DEBUG_TX, "%s: TX data FIFO space available irq\n", dev->name); SMC_SET_FIFO_TDA(0xFF); lp->tx_throttle = 0;#ifdef SMC_USE_DMA if (!lp->txdma_active)#endif netif_wake_queue(dev); SMC_ACK_INT(INT_STS_TDFA_); } /* Handle transmit done condition */#if 1 if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) { DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC, "%s: Tx stat FIFO limit (%d) /GPT irq\n", dev->name, (SMC_GET_FIFO_INT() & 0x00ff0000) >> 16); smc911x_tx(dev); SMC_SET_GPT_CFG(GPT_CFG_TIMER_EN_ | 10000); SMC_ACK_INT(INT_STS_TSFL_); SMC_ACK_INT(INT_STS_TSFL_ | INT_STS_GPT_INT_); }#else if (status & INT_STS_TSFL_) { DBG(SMC_DEBUG_TX, "%s: TX status FIFO limit (%d) irq \n", dev->name, ); smc911x_tx(dev); SMC_ACK_INT(INT_STS_TSFL_); } if (status & INT_STS_GPT_INT_) { DBG(SMC_DEBUG_RX, "%s: IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n", dev->name, SMC_GET_IRQ_CFG(), SMC_GET_FIFO_INT(), SMC_GET_RX_CFG()); DBG(SMC_DEBUG_RX, "%s: Rx Stat FIFO Used 0x%02x " "Data FIFO Used 0x%04x Stat FIFO 0x%08x\n", dev->name, (SMC_GET_RX_FIFO_INF() & 0x00ff0000) >> 16, SMC_GET_RX_FIFO_INF() & 0xffff, SMC_GET_RX_STS_FIFO_PEEK()); SMC_SET_GPT_CFG(GPT_CFG_TIMER_EN_ | 10000); SMC_ACK_INT(INT_STS_GPT_INT_); }#endif /* Handle PHY interrupt condition */ if (status & INT_STS_PHY_INT_) { DBG(SMC_DEBUG_MISC, "%s: PHY irq\n", dev->name); smc911x_phy_interrupt(dev); SMC_ACK_INT(INT_STS_PHY_INT_); } } while (--timeout); /* restore mask state */ SMC_SET_INT_EN(mask); DBG(SMC_DEBUG_MISC, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout); spin_unlock_irqrestore(&lp->lock, flags); DBG(3, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout); return IRQ_HANDLED;}#ifdef SMC_USE_DMAstatic voidsmc911x_tx_dma_irq(int dma, void *data){ struct net_device *dev = (struct net_device *)data; struct smc911x_local *lp = netdev_priv(dev); struct sk_buff *skb = lp->current_tx_skb; unsigned long flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq handler\n", dev->name); /* Clear the DMA interrupt sources */ SMC_DMA_ACK_IRQ(dev, dma); BUG_ON(skb == NULL); dma_unmap_single(NULL, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE); dev->trans_start = jiffies; dev_kfree_skb_irq(skb); lp->current_tx_skb = NULL; if (lp->pending_tx_skb != NULL) smc911x_hardware_send_pkt(dev); else { DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: No pending Tx packets. DMA disabled\n", dev->name); spin_lock_irqsave(&lp->lock, flags); lp->txdma_active = 0; if (!lp->tx_throttle) { netif_wake_queue(dev); } spin_unlock_irqrestore(&lp->lock, flags); } DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq completed\n", dev->name);}static voidsmc911x_rx_dma_irq(int dma, void *data){ struct net_device *dev = (struct net_device *)data; unsigned long ioaddr = dev->base_addr; struct smc911x_local *lp = netdev_priv(dev); struct sk_buff *skb = lp->current_rx_skb; unsigned long flags; unsigned int pkts; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq handler\n", dev->name); /* Clear the DMA interrupt sources */ SMC_DMA_ACK_IRQ(dev, dma); dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE); BUG_ON(skb == NULL); lp->current_rx_skb = NULL; PRINT_PKT(skb->data, skb->len); dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, dev); dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; netif_rx(skb); spin_lock_irqsave(&lp->lock, flags); pkts = (SMC_GET_RX_FIFO_INF() & RX_FIFO_INF_RXSUSED_) >> 16; if (pkts != 0) { smc911x_rcv(dev); }else { lp->rxdma_active = 0; } spin_unlock_irqrestore(&lp->lock, flags); DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq completed. DMA RX FIFO PKTS %d\n", dev->name, pkts);}#endif /* SMC_USE_DMA */#ifdef CONFIG_NET_POLL_CONTROLLER/* * Polling receive - used by netconsole and other diagnostic tools * to allow network i/o with interrupts disabled. */static void smc911x_poll_controller(struct net_device *dev){ disable_irq(dev->irq); smc911x_interrupt(dev->irq, dev); enable_irq(dev->irq);}#endif/* Our watchdog timed out. Called by the networking layer */static void smc911x_timeout(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int status, mask; unsigned long flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); spin_lock_irqsave(&lp->lock, flags); status = SMC_GET_INT(); mask = SMC_GET_INT_EN(); spin_unlock_irqrestore(&lp->lock, flags); DBG(SMC_DEBUG_MISC, "%s: INT 0x%02x MASK 0x%02x \n", dev->name, status, mask); /* Dump the current TX FIFO contents and restart */ mask = SMC_GET_TX_CFG(); SMC_SET_TX_CFG(mask | TX_CFG_TXS_DUMP_ | TX_CFG_TXD_DUMP_); /* * Reconfiguring the PHY doesn't seem like a bad idea here, but * smc911x_phy_configure() calls msleep() which calls schedule_timeout() * which calls schedule(). Hence we use a work queue. */ if (lp->phy_type != 0) { if (schedule_work(&lp->phy_configure)) { lp->work_pending = 1; } } /* We can accept TX packets again */ dev->trans_start = jiffies; netif_wake_queue(dev);}/* * This routine will, depending on the values passed to it, * either make it accept multicast packets, go into * promiscuous mode (for TCPDUMP and cousins) or accept * a select set of multicast packets */static void smc911x_set_multicast_list(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; unsigned int multicast_table[2]; unsigned int mcr, update_multicast = 0; unsigned long flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); spin_lock_irqsave(&lp->lock, flags); SMC_GET_MAC_CR(mcr); spin_unlock_irqrestore(&lp->lock, flags); if (dev->flags & IFF_PROMISC) { DBG(SMC_DEBUG_MISC, "%s: RCR_PRMS\n", dev->name); mcr |= MAC_CR_PRMS_; } /* * Here, I am setting this to accept all multicast packets. * I don't need to zero the multicast table, because the flag is * checked before the table is */ else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) { DBG(SMC_DEBUG_MISC, "%s: RCR_ALMUL\n", dev->name); mcr |= MAC_CR_MCPAS_; } /* * This sets the internal hardware table to filter out unwanted * multicast packets before they take up memory. * * The SMC chip uses a hash table where the high 6 bits of the CRC of * address are the offset into the table. If that bit is 1, then the * multicast packet is accepted. Otherwise, it's dropped silently. * * To use the 6 bits as an offset into the table, the high 1 bit is * the number of the 32 bit register, while the low 5 bits are the bit * within that register. */ else if (dev->mc_count) { int i; struct dev_mc_list *cur_addr; /* Set the Hash perfec mode */ mcr |= MAC_CR_HPFILT_; /* start with a table of all zeros: reject all */ memset(multicast_table, 0, sizeof(multicast_table)); cur_addr = dev->mc_list; for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) { u32 position; /* do we have a pointer here? */ if (!cur_addr) break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ if (!(*cur_addr->dmi_addr & 1)) continue; /* upper 6 bits are used as hash index */ position = ether_crc(ETH_ALEN, cur_addr->dmi_addr)>>26; multicast_table[position>>5] |= 1 << (position&0x1f); } /* be sure I get rid of flags I might have set */ mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); /* now, the table can be loaded into the chipset */ update_multicast = 1; } else { DBG(SMC_DEBUG_MISC, "%s: ~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n", dev->name); mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); /* * since I'm disabling all multicast entirely, I need to * clear the multicast list */ memset(multicast_table, 0, sizeof(multicast_table)); update_multicast = 1; } spin_lock_irqsave(&lp->lock, flags); SMC_SET_MAC_CR(mcr); if (update_multicast) { DBG(SMC_DEBUG_MISC, "%s: update mcast hash table 0x%08x 0x%08x\n", dev->name, multicast_table[0], multicast_table[1]); SMC_SET_HASHL(multicast_table[0]); SMC_SET_HASHH(multicast_table[1]); } spin_unlock_irqrestore(&lp->lock, flags);}/* * Open and Initialize the board * * Set up everything, reset the card, etc.. */static intsmc911x_open(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); /* * Check that the address is valid. If its not, refuse * to bring the device up. The user must specify an * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx */ if (!is_valid_ether_addr(dev->dev_addr)) { PRINTK("%s: no valid ethernet hw addr\n", __FUNCTION__); return -EINVAL; } /* reset the hardware */ smc911x_reset(dev); /* Configure the PHY, initialize the link state */ smc911x_phy_configure(&lp->phy_configure); /* Turn on Tx + Rx */ smc911x_enable(dev); netif_start_queue(dev); return 0;}/* * smc911x_close * * this makes the board clean up everything that it can * and not talk to the outside world. Caused by * an 'ifconfig ethX down' */static int smc911x_close(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); netif_stop_queue(dev); netif_carrier_off(dev); /* clear everything */ smc911x_shutdown(dev); if (lp->phy_type != 0) { /* We need to ensure that no calls to * smc911x_phy_configure are pending. * flush_scheduled_work() cannot be called because we * are running with the netlink semaphore held (from * devinet_ioctl()) and the pending work queue * contains linkwatch_event() (scheduled by * netif_carrier_off() above). linkwatch_event() also * wants the netlink semaphore. */ while (lp->work_pending) schedule(); smc911x_phy_powerdown(dev, lp->mii.phy_id); } if (lp->pending_tx_skb) { dev_kfree_skb(lp->pending_tx_skb); lp->pending_tx_skb = NULL; } return 0;}/* * Ethtool support */static intsmc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int ret, status; unsigned long flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); cmd->maxtxpkt = 1; cmd->maxrxpkt = 1; if (lp->phy_type != 0) { spin_lock_irqsave(&lp->lock, flags); ret = mii_ethtool_gset(&lp->mii, cmd); spin_unlock_irqrestore(&lp->lock, flags); } else { cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_TP | SUPPORTED_AUI; if (lp->ctl_rspeed == 10) cmd->speed = SPEED_10; else if (lp->ctl_rspeed == 100) cmd->speed = SPEED_100; cmd->autoneg = AUTONEG_DISABLE; if (lp->mii.phy_id==1) cmd->transceiver = XCVR_INTERNAL; else cmd->transceiver = XCVR_EXTERNAL; cmd->port = 0; SMC_GET_PHY_SPECIAL(lp->mii.phy_id, status); cmd->duplex = (status & (PHY_SPECIAL_SPD_10FULL_ | PHY_SPECIAL_SPD_100FULL_)) ? DUPLEX_FULL : DUPLEX_HALF; ret = 0; } return ret;}static intsmc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd){ struct smc911x_local *lp = netdev_priv(dev); int ret; unsigned long flags; if (lp->phy_type != 0) { spin_lock_irqsave(&lp->lock, flags); ret = mii_ethtool_sset(&lp->mii, cmd); spin_unlock_irqrestore(&lp->lock, flags); } else { if (cmd->autoneg != AUTONEG_DISABLE || cmd->speed != SPEED_10 || (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) || (cmd->port != PORT_TP && cmd->port != PORT_AUI)) return -EINVAL; lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL; ret = 0; } return ret;}static voidsmc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strncpy(info->driver, CARDNAME, sizeof(info->driver)); strncpy(info->version, version, sizeof(info->version)); strncpy(info->bus_info, dev->dev.parent->bus_id, sizeof(info->bus_info));}static int smc911x_ethtool_nwayreset(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); int ret = -EINVAL; unsigned long flags; if (lp->phy_type != 0) { spin_lock_irqsave(&lp->lock, flags); ret = mii_nway_restart(&lp->mii); spin_unlock_irqrestore(&lp->lock, flags); } return ret;}static u32 smc911x_ethtool_getmsglevel(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); return lp->msg_enable;}static void smc911x_ethtool_setmsglevel(struct net_device *dev, u32 level){ struct smc911x_local *lp = netdev_priv(dev); lp->msg_enable = level;}static int smc911x_ethtool_getregslen(struct net_device *dev){ /* System regs + MAC regs + PHY regs */ return (((E2P_CMD - ID_REV)/4 + 1) + (WUCSR - MAC_CR)+1 + 32) * sizeof(u32);}static void smc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs* regs, void *buf){ unsigned long ioaddr = dev->base_addr; struct smc911x_local *lp = netdev_priv(dev); unsigned long flags; u32 reg,i,j=0; u32 *data = (u32*)buf; regs->version = lp->version; for(i=ID_REV;i<=E2P_CMD;i+=4) { data[j++] = SMC_inl(ioaddr,i); } for(i=MAC_CR;i<=WUCSR;i++) { spin_lock_irqsave(&lp->lock, flags); SMC_GET_MAC_CSR(i, reg); spin_unlock_irqrestore(&lp->lock, flags); data[j++] = reg; } for(i=0;i<=31;i++) { spin_lock_irqsave(&lp->lock, flags); SMC_GET_MII(i, lp->mii.phy_id, reg); spin_unlock_irqrestore(&lp->lock, flags); data[j++] = reg & 0xFFFF; }}static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev){ unsigned long ioaddr = dev->base_addr; unsigned int timeout; int e2p_cmd; e2p_cmd = SMC_GET_E2P_CMD(); for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) { if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) { PRINTK("%s: %s timeout waiting for EEPROM to respond\n", dev->name, __FUNCTION__); return -EFAULT; } mdelay(1); e2p_cmd = SMC_GET_E2P_CMD(); } if (timeout == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -