📄 smc91c92_cs.c
字号:
/* error ... */ smc->stats.rx_errors++; if (rx_status & RS_ALGNERR) smc->stats.rx_frame_errors++; if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) smc->stats.rx_length_errors++; if (rx_status & RS_BADCRC) smc->stats.rx_crc_errors++; } /* Let the MMU free the memory of this packet. */ outw(MC_RELEASE, ioaddr + MMU_CMD); return;}/*====================================================================*/static struct net_device_stats *smc_get_stats(struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); /* Nothing to update - the 91c92 is a pretty primative chip. */ return &smc->stats;}/*====================================================================== Calculate values for the hardware multicast filter hash table.======================================================================*/static void fill_multicast_tbl(int count, struct dev_mc_list *addrs, u_char *multicast_table){ struct dev_mc_list *mc_addr; for (mc_addr = addrs; mc_addr && count-- > 0; mc_addr = mc_addr->next) { u_int position = ether_crc(6, mc_addr->dmi_addr);#ifndef final_version /* Verify multicast address. */ if ((mc_addr->dmi_addr[0] & 1) == 0) continue;#endif multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); }}/*====================================================================== Set the receive mode. This routine is used by both the protocol level to notify us of promiscuous/multicast mode changes, and by the open/reset code to initialize the Rx registers. We always set the multicast list and leave the receiver running.======================================================================*/static void set_rx_mode(struct net_device *dev){ kio_addr_t ioaddr = dev->base_addr; struct smc_private *smc = netdev_priv(dev); u_int multicast_table[ 2 ] = { 0, }; unsigned long flags; u_short rx_cfg_setting; if (dev->flags & IFF_PROMISC) { printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name); rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; } else if (dev->flags & IFF_ALLMULTI) rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; else { if (dev->mc_count) { fill_multicast_tbl(dev->mc_count, dev->mc_list, (u_char *)multicast_table); } rx_cfg_setting = RxStripCRC | RxEnable; } /* Load MC table and Rx setting into the chip without interrupts. */ spin_lock_irqsave(&smc->lock, flags); SMC_SELECT_BANK(3); outl(multicast_table[0], ioaddr + MULTICAST0); outl(multicast_table[1], ioaddr + MULTICAST4); SMC_SELECT_BANK(0); outw(rx_cfg_setting, ioaddr + RCR); SMC_SELECT_BANK(2); spin_unlock_irqrestore(&smc->lock, flags); return;}/*====================================================================== Senses when a card's config changes. Here, it's coax or TP.======================================================================*/static int s9k_config(struct net_device *dev, struct ifmap *map){ struct smc_private *smc = netdev_priv(dev); if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { if (smc->cfg & CFG_MII_SELECT) return -EOPNOTSUPP; else if (map->port > 2) return -EINVAL; dev->if_port = map->port; printk(KERN_INFO "%s: switched to %s port\n", dev->name, if_names[dev->if_port]); smc_reset(dev); } return 0;}/*====================================================================== Reset the chip, reloading every register that might be corrupted.======================================================================*//* Set transceiver type, perhaps to something other than what the user specified in dev->if_port.*/static void smc_set_xcvr(struct net_device *dev, int if_port){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u_short saved_bank; saved_bank = inw(ioaddr + BANK_SELECT); SMC_SELECT_BANK(1); if (if_port == 2) { outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG); if ((smc->manfid == MANFID_OSITECH) && (smc->cardid != PRODID_OSITECH_SEVEN)) set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002); } else { outw(smc->cfg, ioaddr + CONFIG); if ((smc->manfid == MANFID_OSITECH) && (smc->cardid != PRODID_OSITECH_SEVEN)) mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001); } SMC_SELECT_BANK(saved_bank);}static void smc_reset(struct net_device *dev){ kio_addr_t ioaddr = dev->base_addr; struct smc_private *smc = netdev_priv(dev); int i; DEBUG(0, "%s: smc91c92 reset called.\n", dev->name); /* The first interaction must be a write to bring the chip out of sleep mode. */ SMC_SELECT_BANK(0); /* Reset the chip. */ outw(RCR_SOFTRESET, ioaddr + RCR); udelay(10); /* Clear the transmit and receive configuration registers. */ outw(RCR_CLEAR, ioaddr + RCR); outw(TCR_CLEAR, ioaddr + TCR); /* Set the Window 1 control, configuration and station addr registers. No point in writing the I/O base register ;-> */ SMC_SELECT_BANK(1); /* Automatically release succesfully transmitted packets, Accept link errors, counter and Tx error interrupts. */ outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, ioaddr + CONTROL); smc_set_xcvr(dev, dev->if_port); if ((smc->manfid == MANFID_OSITECH) && (smc->cardid != PRODID_OSITECH_SEVEN)) outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) | (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00), ioaddr - 0x10 + OSITECH_AUI_PWR); /* Fill in the physical address. The databook is wrong about the order! */ for (i = 0; i < 6; i += 2) outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i], ioaddr + ADDR0 + i); /* Reset the MMU */ SMC_SELECT_BANK(2); outw(MC_RESET, ioaddr + MMU_CMD); outw(0, ioaddr + INTERRUPT); /* Re-enable the chip. */ SMC_SELECT_BANK(0); outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) | TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR); set_rx_mode(dev); if (smc->cfg & CFG_MII_SELECT) { SMC_SELECT_BANK(3); /* Reset MII */ mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000); /* Advertise 100F, 100H, 10F, 10H */ mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1); /* Restart MII autonegotiation */ mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000); mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200); } /* Enable interrupts. */ SMC_SELECT_BANK(2); outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8, ioaddr + INTERRUPT);}/*====================================================================== Media selection timer routine======================================================================*/static void media_check(u_long arg){ struct net_device *dev = (struct net_device *) arg; struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u_short i, media, saved_bank; u_short link; saved_bank = inw(ioaddr + BANK_SELECT); if (!netif_device_present(dev)) goto reschedule; SMC_SELECT_BANK(2); /* need MC_RESET to keep the memory consistent. errata? */ if (smc->rx_ovrn) { outw(MC_RESET, ioaddr + MMU_CMD); smc->rx_ovrn = 0; } i = inw(ioaddr + INTERRUPT); SMC_SELECT_BANK(0); media = inw(ioaddr + EPH) & EPH_LINK_OK; SMC_SELECT_BANK(1); media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1; /* Check for pending interrupt with watchdog flag set: with this, we can limp along even if the interrupt is blocked */ if (smc->watchdog++ && ((i>>8) & i)) { if (!smc->fast_poll) printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); smc_interrupt(dev->irq, smc, NULL); smc->fast_poll = HZ; } if (smc->fast_poll) { smc->fast_poll--; smc->media.expires = jiffies + HZ/100; add_timer(&smc->media); SMC_SELECT_BANK(saved_bank); return; } if (smc->cfg & CFG_MII_SELECT) { if (smc->mii_if.phy_id < 0) goto reschedule; SMC_SELECT_BANK(3); link = mdio_read(dev, smc->mii_if.phy_id, 1); if (!link || (link == 0xffff)) { printk(KERN_INFO "%s: MII is missing!\n", dev->name); smc->mii_if.phy_id = -1; goto reschedule; } link &= 0x0004; if (link != smc->link_status) { u_short p = mdio_read(dev, smc->mii_if.phy_id, 5); printk(KERN_INFO "%s: %s link beat\n", dev->name, (link) ? "found" : "lost"); smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40)) ? TCR_FDUPLX : 0); if (link) { printk(KERN_INFO "%s: autonegotiation complete: " "%sbaseT-%cD selected\n", dev->name, ((p & 0x0180) ? "100" : "10"), (smc->duplex ? 'F' : 'H')); } SMC_SELECT_BANK(0); outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR); smc->link_status = link; } goto reschedule; } /* Ignore collisions unless we've had no rx's recently */ if (time_after(jiffies, dev->last_rx + HZ)) { if (smc->tx_err || (smc->media_status & EPH_16COL)) media |= EPH_16COL; } smc->tx_err = 0; if (media != smc->media_status) { if ((media & smc->media_status & 1) && ((smc->media_status ^ media) & EPH_LINK_OK)) printk(KERN_INFO "%s: %s link beat\n", dev->name, (smc->media_status & EPH_LINK_OK ? "lost" : "found")); else if ((media & smc->media_status & 2) && ((smc->media_status ^ media) & EPH_16COL)) printk(KERN_INFO "%s: coax cable %s\n", dev->name, (media & EPH_16COL ? "problem" : "ok")); if (dev->if_port == 0) { if (media & 1) { if (media & EPH_LINK_OK) printk(KERN_INFO "%s: flipped to 10baseT\n", dev->name); else smc_set_xcvr(dev, 2); } else { if (media & EPH_16COL) smc_set_xcvr(dev, 1); else printk(KERN_INFO "%s: flipped to 10base2\n", dev->name); } } smc->media_status = media; }reschedule: smc->media.expires = jiffies + HZ; add_timer(&smc->media); SMC_SELECT_BANK(saved_bank);}static int smc_link_ok(struct net_device *dev){ kio_addr_t ioaddr = dev->base_addr; struct smc_private *smc = netdev_priv(dev); if (smc->cfg & CFG_MII_SELECT) { return mii_link_ok(&smc->mii_if); } else { SMC_SELECT_BANK(0); return inw(ioaddr + EPH) & EPH_LINK_OK; }}static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd){ u16 tmp; kio_addr_t ioaddr = dev->base_addr; ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); SMC_SELECT_BANK(1); tmp = inw(ioaddr + CONFIG); ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; ecmd->transceiver = XCVR_INTERNAL; ecmd->speed = SPEED_10; ecmd->phy_address = ioaddr + MGMT; SMC_SELECT_BANK(0); tmp = inw(ioaddr + TCR); ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; return 0;}static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd){ u16 tmp; kio_addr_t ioaddr = dev->base_addr; if (ecmd->speed != SPEED_10) return -EINVAL; if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) return -EINVAL; if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI) return -EINVAL; if (ecmd->transceiver != XCVR_INTERNAL) return -EINVAL; if (ecmd->port == PORT_AUI) smc_set_xcvr(dev, 1); else smc_set_xcvr(dev, 0); SMC_SELECT_BANK(0); tmp = inw(ioaddr + TCR); if (ecmd->duplex == DUPLEX_FULL) tmp |= TCR_FDUPLX; else tmp &= ~TCR_FDUPLX; outw(tmp, ioaddr + TCR); return 0;}static int check_if_running(struct net_device *dev){ if (!netif_running(dev)) return -EINVAL; return 0;}static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION);}static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u16 saved_bank = inw(ioaddr + BANK_SELECT); int ret; SMC_SELECT_BANK(3); spin_lock_irq(&smc->lock); if (smc->cfg & CFG_MII_SELECT) ret = mii_ethtool_gset(&smc->mii_if, ecmd); else ret = smc_netdev_get_ecmd(dev, ecmd); spin_unlock_irq(&smc->lock); SMC_SELECT_BANK(saved_bank); return ret;}static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u16 saved_bank = inw(ioaddr + BANK_SELECT); int ret; SMC_SELECT_BANK(3); spin_lock_irq(&smc->lock); if (smc->cfg & CFG_MII_SELECT) ret = mii_ethtool_sset(&smc->mii_if, ecmd); else ret = smc_netdev_set_ecmd(dev, ecmd); spin_unlock_irq(&smc->lock); SMC_SELECT_BANK(saved_bank); return
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -