📄 smc911x.c
字号:
* now, or set the card to generates an interrupt when ready * for the packet. */static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; unsigned int free; unsigned long flags; DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __FUNCTION__); BUG_ON(lp->pending_tx_skb != NULL); free = SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TDFREE_; DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free); /* Turn off the flow when running out of space in FIFO */ if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) { DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n", dev->name, free); spin_lock_irqsave(&lp->lock, flags); /* Reenable when at least 1 packet of size MTU present */ SMC_SET_FIFO_TDA((SMC911X_TX_FIFO_LOW_THRESHOLD)/64); lp->tx_throttle = 1; netif_stop_queue(dev); spin_unlock_irqrestore(&lp->lock, flags); } /* Drop packets when we run out of space in TX FIFO * Account for overhead required for: * * Tx command words 8 bytes * Start offset 15 bytes * End padding 15 bytes */ if (unlikely(free < (skb->len + 8 + 15 + 15))) { printk("%s: No Tx free space %d < %d\n", dev->name, free, skb->len); lp->pending_tx_skb = NULL; dev->stats.tx_errors++; dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; }#ifdef SMC_USE_DMA { /* If the DMA is already running then defer this packet Tx until * the DMA IRQ starts it */ spin_lock_irqsave(&lp->lock, flags); if (lp->txdma_active) { DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name); lp->pending_tx_skb = skb; netif_stop_queue(dev); spin_unlock_irqrestore(&lp->lock, flags); return 0; } else { DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name); lp->txdma_active = 1; } spin_unlock_irqrestore(&lp->lock, flags); }#endif lp->pending_tx_skb = skb; smc911x_hardware_send_pkt(dev); return 0;}/* * This handles a TX status interrupt, which is only called when: * - a TX error occurred, or * - TX of a packet completed. */static void smc911x_tx(struct net_device *dev){ unsigned long ioaddr = dev->base_addr; struct smc911x_local *lp = netdev_priv(dev); unsigned int tx_status; DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __FUNCTION__); /* Collect the TX status */ while (((SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TSUSED_) >> 16) != 0) { DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n", dev->name, (SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TSUSED_) >> 16); tx_status = SMC_GET_TX_STS_FIFO(); dev->stats.tx_packets++; dev->stats.tx_bytes+=tx_status>>16; DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n", dev->name, (tx_status & 0xffff0000) >> 16, tx_status & 0x0000ffff); /* count Tx errors, but ignore lost carrier errors when in * full-duplex mode */ if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx && !(tx_status & 0x00000306))) { dev->stats.tx_errors++; } if (tx_status & TX_STS_MANY_COLL_) { dev->stats.collisions+=16; dev->stats.tx_aborted_errors++; } else { dev->stats.collisions+=(tx_status & TX_STS_COLL_CNT_) >> 3; } /* carrier error only has meaning for half-duplex communication */ if ((tx_status & (TX_STS_LOC_ | TX_STS_NO_CARR_)) && !lp->ctl_rfduplx) { dev->stats.tx_carrier_errors++; } if (tx_status & TX_STS_LATE_COLL_) { dev->stats.collisions++; dev->stats.tx_aborted_errors++; } }}/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*//* * Reads a register from the MII Management serial interface */static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg){ unsigned long ioaddr = dev->base_addr; unsigned int phydata; SMC_GET_MII(phyreg, phyaddr, phydata); DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n", __FUNCTION__, phyaddr, phyreg, phydata); return phydata;}/* * Writes a register to the MII Management serial interface */static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg, int phydata){ unsigned long ioaddr = dev->base_addr; DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", __FUNCTION__, phyaddr, phyreg, phydata); SMC_SET_MII(phyreg, phyaddr, phydata);}/* * Finds and reports the PHY address (115 and 117 have external * PHY interface 118 has internal only */static void smc911x_phy_detect(struct net_device *dev){ unsigned long ioaddr = dev->base_addr; struct smc911x_local *lp = netdev_priv(dev); int phyaddr; unsigned int cfg, id1, id2; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); lp->phy_type = 0; /* * Scan all 32 PHY addresses if necessary, starting at * PHY#1 to PHY#31, and then PHY#0 last. */ switch(lp->version) { case 0x115: case 0x117: cfg = SMC_GET_HW_CFG(); if (cfg & HW_CFG_EXT_PHY_DET_) { cfg &= ~HW_CFG_PHY_CLK_SEL_; cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; SMC_SET_HW_CFG(cfg); udelay(10); /* Wait for clocks to stop */ cfg |= HW_CFG_EXT_PHY_EN_; SMC_SET_HW_CFG(cfg); udelay(10); /* Wait for clocks to stop */ cfg &= ~HW_CFG_PHY_CLK_SEL_; cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; SMC_SET_HW_CFG(cfg); udelay(10); /* Wait for clocks to stop */ cfg |= HW_CFG_SMI_SEL_; SMC_SET_HW_CFG(cfg); for (phyaddr = 1; phyaddr < 32; ++phyaddr) { /* Read the PHY identifiers */ SMC_GET_PHY_ID1(phyaddr & 31, id1); SMC_GET_PHY_ID2(phyaddr & 31, id2); /* Make sure it is a valid identifier */ if (id1 != 0x0000 && id1 != 0xffff && id1 != 0x8000 && id2 != 0x0000 && id2 != 0xffff && id2 != 0x8000) { /* Save the PHY's address */ lp->mii.phy_id = phyaddr & 31; lp->phy_type = id1 << 16 | id2; break; } } } default: /* Internal media only */ SMC_GET_PHY_ID1(1, id1); SMC_GET_PHY_ID2(1, id2); /* Save the PHY's address */ lp->mii.phy_id = 1; lp->phy_type = id1 << 16 | id2; } DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n", dev->name, id1, id2, lp->mii.phy_id);}/* * Sets the PHY to a configuration as determined by the user. * Called with spin_lock held. */static int smc911x_phy_fixed(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int phyaddr = lp->mii.phy_id; int bmcr; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); /* Enter Link Disable state */ SMC_GET_PHY_BMCR(phyaddr, bmcr); bmcr |= BMCR_PDOWN; SMC_SET_PHY_BMCR(phyaddr, bmcr); /* * Set our fixed capabilities * Disable auto-negotiation */ bmcr &= ~BMCR_ANENABLE; if (lp->ctl_rfduplx) bmcr |= BMCR_FULLDPLX; if (lp->ctl_rspeed == 100) bmcr |= BMCR_SPEED100; /* Write our capabilities to the phy control register */ SMC_SET_PHY_BMCR(phyaddr, bmcr); /* Re-Configure the Receive/Phy Control register */ bmcr &= ~BMCR_PDOWN; SMC_SET_PHY_BMCR(phyaddr, bmcr); return 1;}/* * smc911x_phy_reset - reset the phy * @dev: net device * @phy: phy address * * Issue a software reset for the specified PHY and * wait up to 100ms for the reset to complete. We should * not access the PHY for 50ms after issuing the reset. * * The time to wait appears to be dependent on the PHY. * */static int smc911x_phy_reset(struct net_device *dev, int phy){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int timeout; unsigned long flags; unsigned int reg; DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __FUNCTION__); spin_lock_irqsave(&lp->lock, flags); reg = SMC_GET_PMT_CTRL(); reg &= ~0xfffff030; reg |= PMT_CTRL_PHY_RST_; SMC_SET_PMT_CTRL(reg); spin_unlock_irqrestore(&lp->lock, flags); for (timeout = 2; timeout; timeout--) { msleep(50); spin_lock_irqsave(&lp->lock, flags); reg = SMC_GET_PMT_CTRL(); spin_unlock_irqrestore(&lp->lock, flags); if (!(reg & PMT_CTRL_PHY_RST_)) { /* extra delay required because the phy may * not be completed with its reset * when PHY_BCR_RESET_ is cleared. 256us * should suffice, but use 500us to be safe */ udelay(500); break; } } return reg & PMT_CTRL_PHY_RST_;}/* * smc911x_phy_powerdown - powerdown phy * @dev: net device * @phy: phy address * * Power down the specified PHY */static void smc911x_phy_powerdown(struct net_device *dev, int phy){ unsigned long ioaddr = dev->base_addr; unsigned int bmcr; /* Enter Link Disable state */ SMC_GET_PHY_BMCR(phy, bmcr); bmcr |= BMCR_PDOWN; SMC_SET_PHY_BMCR(phy, bmcr);}/* * smc911x_phy_check_media - check the media status and adjust BMCR * @dev: net device * @init: set true for initialisation * * Select duplex mode depending on negotiation state. This * also updates our carrier state. */static void smc911x_phy_check_media(struct net_device *dev, int init){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int phyaddr = lp->mii.phy_id; unsigned int bmcr, cr; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) { /* duplex state has changed */ SMC_GET_PHY_BMCR(phyaddr, bmcr); SMC_GET_MAC_CR(cr); if (lp->mii.full_duplex) { DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name); bmcr |= BMCR_FULLDPLX; cr |= MAC_CR_RCVOWN_; } else { DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name); bmcr &= ~BMCR_FULLDPLX; cr &= ~MAC_CR_RCVOWN_; } SMC_SET_PHY_BMCR(phyaddr, bmcr); SMC_SET_MAC_CR(cr); }}/* * Configures the specified PHY through the MII management interface * using Autonegotiation. * Calls smc911x_phy_fixed() if the user has requested a certain config. * If RPC ANEG bit is set, the media selection is dependent purely on * the selection by the MII (either in the MII BMCR reg or the result * of autonegotiation.) If the RPC ANEG bit is cleared, the selection * is controlled by the RPC SPEED and RPC DPLX bits. */static void smc911x_phy_configure(struct work_struct *work){ struct smc911x_local *lp = container_of(work, struct smc911x_local, phy_configure); struct net_device *dev = lp->netdev; unsigned long ioaddr = dev->base_addr; int phyaddr = lp->mii.phy_id; int my_phy_caps; /* My PHY capabilities */ int my_ad_caps; /* My Advertised capabilities */ int status; unsigned long flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __FUNCTION__); /* * We should not be called if phy_type is zero. */ if (lp->phy_type == 0) goto smc911x_phy_configure_exit_nolock; if (smc911x_phy_reset(dev, phyaddr)) { printk("%s: PHY reset timed out\n", dev->name); goto smc911x_phy_configure_exit_nolock; } spin_lock_irqsave(&lp->lock, flags); /* * Enable PHY Interrupts (for register 18) * Interrupts listed here are enabled */ SMC_SET_PHY_INT_MASK(phyaddr, PHY_INT_MASK_ENERGY_ON_ | PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ | PHY_INT_MASK_LINK_DOWN_); /* If the user requested no auto neg, then go set his request */ if (lp->mii.force_media) { smc911x_phy_fixed(dev); goto smc911x_phy_configure_exit; } /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */ SMC_GET_PHY_BMSR(phyaddr, my_phy_caps); if (!(my_phy_caps & BMSR_ANEGCAPABLE)) { printk(KERN_INFO "Auto negotiation NOT supported\n"); smc911x_phy_fixed(dev); goto smc911x_phy_configure_exit; } /* CSMA capable w/ both pauses */ my_ad_caps = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; if (my_phy_caps & BMSR_100BASE4) my_ad_caps |= ADVERTISE_100BASE4; if (my_phy_caps & BMSR_100FULL) my_ad_caps |= ADVERTISE_100FULL; if (my_phy_caps & BMSR_100HALF) my_ad_caps |= ADVERTISE_100HALF; if (my_phy_caps & BMSR_10FULL) my_ad_caps |= ADVERTISE_10FULL; if (my_phy_caps & BMSR_10HALF) my_ad_caps |= ADVERTISE_10HALF; /* Disable capabilities not selected by our user */ if (lp->ctl_rspeed != 100) my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF); if (!lp->ctl_rfduplx) my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL); /* Update our Auto-Neg Advertisement Register */ SMC_SET_PHY_MII_ADV(phyaddr, my_ad_caps); lp->mii.advertising = my_ad_caps; /* * Read the register back. Without this, it appears that when * auto-negotiation is restarted, sometimes it isn't ready and * the link does not come up. */ udelay(10); SMC_GET_PHY_MII_ADV(phyaddr, status); DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps); DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps); /* Restart auto-negotiation process in order to advertise my caps */ SMC_SET_PHY_BMCR(phyaddr, BMCR_ANENABLE | BMCR_ANRESTART); smc911x_phy_check_media(dev, 1);smc911x_phy_configure_exit: spin_unlock_irqrestore(&lp->lock, flags);smc911x_phy_configure_exit_nolock: lp->work_pending = 0;}/* * smc911x_phy_interrupt * * Purpose: Handle interrupts relating to PHY register 18. This is * called from the "hard" interrupt handler under our private spinlock. */static void smc911x_phy_interrupt(struct net_device *dev){ struct smc911x_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int phyaddr = lp->mii.phy_id; int status; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); if (lp->phy_type == 0) return; smc911x_phy_check_media(dev, 0); /* read to clear status bits */ SMC_GET_PHY_INT_SRC(phyaddr,status); DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n", dev->name, status & 0xffff); DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n", dev->name, SMC_GET_AFC_CFG());}/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*//* * This is the main routine of the driver, to handle the device when * it needs some attention. */static irqreturn_t smc911x_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; unsigned long ioaddr = dev->base_addr; struct smc911x_local *lp = netdev_priv(dev); unsigned int status, mask, timeout; unsigned int rx_overrun=0, cr, pkts; unsigned long flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); spin_lock_irqsave(&lp->lock, flags); /* Spurious interrupt check */ if ((SMC_GET_IRQ_CFG() & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) != (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) { spin_unlock_irqrestore(&lp->lock, flags); return IRQ_NONE; } mask = SMC_GET_INT_EN(); SMC_SET_INT_EN(0); /* set a timeout value, so I don't stay here forever */ timeout = 8; do { status = SMC_GET_INT(); DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n", dev->name, status, mask, status & ~mask); status &= mask; if (!status) break; /* Handle SW interrupt condition */ if (status & INT_STS_SW_INT_) { SMC_ACK_INT(INT_STS_SW_INT_); mask &= ~INT_EN_SW_INT_EN_; } /* Handle various error conditions */ if (status & INT_STS_RXE_) { SMC_ACK_INT(INT_STS_RXE_); dev->stats.rx_errors++; } if (status & INT_STS_RXDFH_INT_) { SMC_ACK_INT(INT_STS_RXDFH_INT_); dev->stats.rx_dropped+=SMC_GET_RX_DROP(); } /* Undocumented interrupt-what is the right thing to do here? */ if (status & INT_STS_RXDF_INT_) { SMC_ACK_INT(INT_STS_RXDF_INT_); } /* Rx Data FIFO exceeds set level */ if (status & INT_STS_RDFL_) { if (IS_REV_A(lp->revision)) { rx_overrun=1; SMC_GET_MAC_CR(cr); cr &= ~MAC_CR_RXEN_; SMC_SET_MAC_CR(cr); DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name); dev->stats.rx_errors++; dev->stats.rx_fifo_errors++; } SMC_ACK_INT(INT_STS_RDFL_); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -