📄 ioc3-eth.c
字号:
ioc3_alloc_rings(dev, ip, ioc3); ioc3_clean_rx_ring(ip); ioc3_clean_tx_ring(ip); /* Now the rx ring base, consume & produce registers. */ ring = (0xa5UL << 56) | ((unsigned long)ip->rxr & TO_PHYS_MASK); ioc3->erbr_h = ring >> 32; ioc3->erbr_l = ring & 0xffffffff; ioc3->ercir = (ip->rx_ci << 3); ioc3->erpir = (ip->rx_pi << 3) | ERPIR_ARM; ring = (0xa5UL << 56) | ((unsigned long)ip->txr & TO_PHYS_MASK); ip->txqlen = 0; /* nothing queued */ /* Now the tx ring base, consume & produce registers. */ ioc3->etbr_h = ring >> 32; ioc3->etbr_l = ring & 0xffffffff; ioc3->etpir = (ip->tx_pi << 7); ioc3->etcir = (ip->tx_ci << 7); ioc3->etcir; /* Flush */}static inline voidioc3_ssram_disc(struct ioc3_private *ip){ struct ioc3 *ioc3 = ip->regs; volatile u32 *ssram0 = &ioc3->ssram[0x0000]; volatile u32 *ssram1 = &ioc3->ssram[0x4000]; unsigned int pattern = 0x5555; /* Assume the larger size SSRAM and enable parity checking */ ioc3->emcr |= (EMCR_BUFSIZ | EMCR_RAMPAR); *ssram0 = pattern; *ssram1 = ~pattern & IOC3_SSRAM_DM; if ((*ssram0 & IOC3_SSRAM_DM) != pattern || (*ssram1 & IOC3_SSRAM_DM) != (~pattern & IOC3_SSRAM_DM)) { /* set ssram size to 64 KB */ ip->emcr = EMCR_RAMPAR; ioc3->emcr &= ~EMCR_BUFSIZ; } else { ip->emcr = EMCR_BUFSIZ | EMCR_RAMPAR; }}static void ioc3_init(struct ioc3_private *ip){ struct net_device *dev = ip->dev; struct ioc3 *ioc3 = ip->regs; del_timer(&ip->ioc3_timer); /* Kill if running */ ioc3->emcr = EMCR_RST; /* Reset */ ioc3->emcr; /* Flush WB */ udelay(4); /* Give it time ... */ ioc3->emcr = 0; ioc3->emcr; /* Misc registers */ ioc3->erbar = 0; ioc3->etcsr = (17<<ETCSR_IPGR2_SHIFT) | (11<<ETCSR_IPGR1_SHIFT) | 21; ioc3->etcdc; /* Clear on read */ ioc3->ercsr = 15; /* RX low watermark */ ioc3->ertr = 0; /* Interrupt immediately */ ioc3->emar_h = (dev->dev_addr[5] << 8) | dev->dev_addr[4]; ioc3->emar_l = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | dev->dev_addr[0]; ioc3->ehar_h = ip->ehar_h; ioc3->ehar_l = ip->ehar_l; ioc3->ersr = 42; /* XXX should be random */ ioc3_init_rings(ip->dev, ip, ioc3); ip->emcr |= ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN | EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN; ioc3->emcr = ip->emcr; ioc3->eier = EISR_RXTIMERINT | EISR_RXOFLO | EISR_RXBUFOFLO | EISR_RXMEMERR | EISR_RXPARERR | EISR_TXBUFUFLO | EISR_TXEXPLICIT | EISR_TXMEMERR; ioc3->eier;}static inline void ioc3_stop(struct ioc3_private *ip){ struct ioc3 *ioc3 = ip->regs; ioc3->emcr = 0; /* Shutup */ ioc3->eier = 0; /* Disable interrupts */ ioc3->eier; /* Flush */}static intioc3_open(struct net_device *dev){ struct ioc3_private *ip = dev->priv; if (request_irq(dev->irq, ioc3_interrupt, SA_SHIRQ, ioc3_str, dev)) { printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq); return -EAGAIN; } ip->ehar_h = 0; ip->ehar_l = 0; ioc3_init(ip); netif_start_queue(dev); return 0;}static intioc3_close(struct net_device *dev){ struct ioc3_private *ip = dev->priv; del_timer(&ip->ioc3_timer); netif_stop_queue(dev); ioc3_stop(ip); free_irq(dev->irq, dev); ioc3_free_rings(ip); return 0;}/* * MENET cards have four IOC3 chips, which are attached to two sets of * PCI slot resources each: the primary connections are on slots * 0..3 and the secondaries are on 4..7 * * All four ethernets are brought out to connectors; six serial ports * (a pair from each of the first three IOC3s) are brought out to * MiniDINs; all other subdevices are left swinging in the wind, leave * them disabled. */static inline int ioc3_is_menet(struct pci_dev *pdev){ struct pci_dev *dev; return pdev->bus->parent == NULL && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(0, 0))) && dev->vendor == PCI_VENDOR_ID_SGI && dev->device == PCI_DEVICE_ID_SGI_IOC3 && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(1, 0))) && dev->vendor == PCI_VENDOR_ID_SGI && dev->device == PCI_DEVICE_ID_SGI_IOC3 && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(2, 0))) && dev->vendor == PCI_VENDOR_ID_SGI && dev->device == PCI_DEVICE_ID_SGI_IOC3;}static void inline ioc3_serial_probe(struct pci_dev *pdev, struct ioc3 *ioc3){ struct serial_struct req; /* * We need to recognice and treat the fourth MENET serial as it * does not have an SuperIO chip attached to it, therefore attempting * to access it will result in bus errors. We call something an * MENET if PCI slot 0, 1, 2 and 3 of a master PCI bus all have an IOC3 * in it. This is paranoid but we want to avoid blowing up on a * showhorn PCI box that happens to have 4 IOC3 cards in it so it's * not paranoid enough ... */ if (ioc3_is_menet(pdev) && PCI_SLOT(pdev->devfn) == 3) return; /* Register to interrupt zero because we share the interrupt with the serial driver which we don't properly support yet. */ memset(&req, 0, sizeof(req)); req.irq = 0; req.flags = IOC3_COM_FLAGS; req.io_type = SERIAL_IO_MEM; req.iomem_reg_shift = 0; req.baud_base = IOC3_BAUD; req.iomem_base = (unsigned char *) &ioc3->sregs.uarta; register_serial(&req); req.iomem_base = (unsigned char *) &ioc3->sregs.uartb; register_serial(&req);}static int __devinit ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *ent){ struct net_device *dev = NULL; struct ioc3_private *ip; struct ioc3 *ioc3; unsigned long ioc3_base, ioc3_size; u32 vendor, model, rev; int err; dev = alloc_etherdev(sizeof(struct ioc3_private)); if (!dev) return -ENOMEM; err = pci_request_regions(pdev, "ioc3"); if (err) goto out_free; SET_MODULE_OWNER(dev); ip = dev->priv; ip->dev = dev; dev->irq = pdev->irq; ioc3_base = pci_resource_start(pdev, 0); ioc3_size = pci_resource_len(pdev, 0); ioc3 = (struct ioc3 *) ioremap(ioc3_base, ioc3_size); if (!ioc3) { printk(KERN_CRIT "ioc3eth(%s): ioremap failed, goodbye.\n", pdev->slot_name); err = -ENOMEM; goto out_res; } ip->regs = ioc3;#ifdef CONFIG_SERIAL ioc3_serial_probe(pdev, ioc3);#endif spin_lock_init(&ip->ioc3_lock); ioc3_stop(ip); ioc3_init(ip); init_timer(&ip->ioc3_timer); ioc3_mii_init(ip); if (ip->phy == -1) { printk(KERN_CRIT "ioc3-eth(%s): Didn't find a PHY, goodbye.\n", pdev->slot_name); err = -ENODEV; goto out_stop; } ioc3_ssram_disc(ip); ioc3_get_eaddr(ip); /* The IOC3-specific entries in the device structure. */ dev->open = ioc3_open; dev->hard_start_xmit = ioc3_start_xmit; dev->tx_timeout = ioc3_timeout; dev->watchdog_timeo = 5 * HZ; dev->stop = ioc3_close; dev->get_stats = ioc3_get_stats; dev->do_ioctl = ioc3_ioctl; dev->set_multicast_list = ioc3_set_multicast_list; err = register_netdev(dev); if (err) goto out_stop; vendor = (ip->sw_physid1 << 12) | (ip->sw_physid2 >> 4); model = (ip->sw_physid2 >> 4) & 0x3f; rev = ip->sw_physid2 & 0xf; printk(KERN_INFO "%s: Using PHY %d, vendor 0x%x, model %d, " "rev %d.\n", dev->name, ip->phy, vendor, model, rev); printk(KERN_INFO "%s: IOC3 SSRAM has %d kbyte.\n", dev->name, ip->emcr & EMCR_BUFSIZ ? 128 : 64); return 0;out_stop: ioc3_stop(ip); free_irq(dev->irq, dev); ioc3_free_rings(ip);out_res: pci_release_regions(pdev);out_free: kfree(dev); return err;}static void __devexit ioc3_remove_one (struct pci_dev *pdev){ struct net_device *dev = pci_get_drvdata(pdev); struct ioc3_private *ip = dev->priv; struct ioc3 *ioc3 = ip->regs; iounmap(ioc3); pci_release_regions(pdev); kfree(dev);}static struct pci_device_id ioc3_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID }, { 0 }};MODULE_DEVICE_TABLE(pci, ioc3_pci_tbl);static struct pci_driver ioc3_driver = { name: "ioc3-eth", id_table: ioc3_pci_tbl, probe: ioc3_probe, remove: __devexit_p(ioc3_remove_one),};static int __init ioc3_init_module(void){ return pci_module_init(&ioc3_driver);}static void __exit ioc3_cleanup_module(void){ pci_unregister_driver(&ioc3_driver);}static intioc3_start_xmit(struct sk_buff *skb, struct net_device *dev){ unsigned long data; struct ioc3_private *ip = dev->priv; struct ioc3 *ioc3 = ip->regs; unsigned int len; struct ioc3_etxd *desc; int produce; spin_lock_irq(&ip->ioc3_lock); data = (unsigned long) skb->data; len = skb->len; produce = ip->tx_pi; desc = &ip->txr[produce]; if (len <= 104) { /* Short packet, let's copy it directly into the ring. */ memcpy(desc->data, skb->data, skb->len); if (len < ETH_ZLEN) { /* Very short packet, pad with zeros at the end. */ memset(desc->data + len, 0, ETH_ZLEN - len); len = ETH_ZLEN; } desc->cmd = len | ETXD_INTWHENDONE | ETXD_D0V; desc->bufcnt = len; } else if ((data ^ (data + len)) & 0x4000) { unsigned long b2, s1, s2; b2 = (data | 0x3fffUL) + 1UL; s1 = b2 - data; s2 = data + len - b2; desc->cmd = len | ETXD_INTWHENDONE | ETXD_B1V | ETXD_B2V; desc->bufcnt = (s1 << ETXD_B1CNT_SHIFT) | (s2 << ETXD_B2CNT_SHIFT); desc->p1 = (0xa5UL << 56) | (data & TO_PHYS_MASK); desc->p2 = (0xa5UL << 56) | (data & TO_PHYS_MASK); } else { /* Normal sized packet that doesn't cross a page boundary. */ desc->cmd = len | ETXD_INTWHENDONE | ETXD_B1V; desc->bufcnt = len << ETXD_B1CNT_SHIFT; desc->p1 = (0xa5UL << 56) | (data & TO_PHYS_MASK); } BARRIER(); dev->trans_start = jiffies; ip->tx_skbs[produce] = skb; /* Remember skb */ produce = (produce + 1) & 127; ip->tx_pi = produce; ioc3->etpir = produce << 7; /* Fire ... */ ip->txqlen++; if (ip->txqlen > 127) netif_stop_queue(dev); spin_unlock_irq(&ip->ioc3_lock); return 0;}static void ioc3_timeout(struct net_device *dev){ struct ioc3_private *ip = dev->priv; printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name); ioc3_stop(ip); ioc3_init(ip); ioc3_mii_init(ip); dev->trans_start = jiffies; netif_wake_queue(dev);}/* * Given a multicast ethernet address, this routine calculates the * address's bit index in the logical address filter mask */#define CRC_MASK 0xedb88320static inline unsigned intioc3_hash(const unsigned char *addr){ unsigned int temp = 0; unsigned char byte; unsigned int crc; int bits, len; len = ETH_ALEN; for (crc = ~0; --len >= 0; addr++) { byte = *addr; for (bits = 8; --bits >= 0; ) { if ((byte ^ crc) & 1) crc = (crc >> 1) ^ CRC_MASK; else crc >>= 1; byte >>= 1; } } crc &= 0x3f; /* bit reverse lowest 6 bits for hash index */ for (bits = 6; --bits >= 0; ) { temp <<= 1; temp |= (crc & 0x1); crc >>= 1; } return temp;}/* We provide both the mii-tools and the ethtool ioctls. */static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct ioc3_private *ip = dev->priv; struct ethtool_cmd *ep_user = (struct ethtool_cmd *) rq->ifr_data; u16 *data = (u16 *)&rq->ifr_data; struct ioc3 *ioc3 = ip->regs; struct ethtool_cmd ecmd; switch (cmd) { case SIOCGMIIPHY: /* Get the address of the PHY in use. */ if (ip->phy == -1) return -ENODEV; data[0] = ip->phy; return 0; case SIOCGMIIREG: { /* Read a PHY register. */ unsigned int phy = data[0]; unsigned int reg = data[1]; if (phy > 0x1f || reg > 0x1f) return -EINVAL; spin_lock_irq(&ip->ioc3_lock); while (ioc3->micr & MICR_BUSY); ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg | MICR_READTRIG; while (ioc3->micr & MICR_BUSY); data[3] = (ioc3->midr_r & MIDR_DATA_MASK); spin_unlock_irq(&ip->ioc3_lock); return 0; case SIOCSMIIREG: /* Write a PHY register. */ phy = data[0]; reg = data[1]; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (phy > 0x1f || reg > 0x1f) return -EINVAL; spin_lock_irq(&ip->ioc3_lock); while (ioc3->micr & MICR_BUSY); ioc3->midr_w = data[2]; ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg; while (ioc3->micr & MICR_BUSY); spin_unlock_irq(&ip->ioc3_lock); return 0; } case SIOCETHTOOL: if (copy_from_user(&ecmd, ep_user, sizeof(ecmd))) return -EFAULT; if (ecmd.cmd == ETHTOOL_GSET) { ecmd.supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); ecmd.port = PORT_TP; ecmd.transceiver = XCVR_INTERNAL; ecmd.phy_address = ip->phy; /* Record PHY settings. */ spin_lock_irq(&ip->ioc3_lock); ip->sw_bmcr = mii_read(ip, MII_BMCR); ip->sw_lpa = mii_read(ip, MII_LPA); spin_unlock_irq(&ip->ioc3_lock); if (ip->sw_bmcr & BMCR_ANENABLE) { ecmd.autoneg = AUTONEG_ENABLE; ecmd.speed = (ip->sw_lpa & (LPA_100HALF | LPA_100FULL)) ? SPEED_100 : SPEED_10; if (ecmd.speed == SPEED_100) ecmd.duplex = (ip->sw_lpa & (LPA_100FULL)) ? DUPLEX_FULL : DUPLEX_HALF; else ecmd.duplex = (ip->sw_lpa & (LPA_10FULL)) ? DUPLEX_FULL : DUPLEX_HALF; } else { ecmd.autoneg = AUTONEG_DISABLE; ecmd.speed = (ip->sw_bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; ecmd.duplex = (ip->sw_bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } if (copy_to_user(ep_user, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } else if (ecmd.cmd == ETHTOOL_SSET) { if (!capable(CAP_NET_ADMIN)) return -EPERM; /* Verify the settings we care about. */ if (ecmd.autoneg != AUTONEG_ENABLE && ecmd.autoneg != AUTONEG_DISABLE) return -EINVAL; if (ecmd.autoneg == AUTONEG_DISABLE && ((ecmd.speed != SPEED_100 && ecmd.speed != SPEED_10) || (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL))) return -EINVAL; /* Ok, do it to it. */ del_timer(&ip->ioc3_timer); spin_lock_irq(&ip->ioc3_lock); ioc3_start_auto_negotiation(ip, &ecmd); spin_unlock_irq(&ip->ioc3_lock); return 0; } else default: return -EOPNOTSUPP; } return -EOPNOTSUPP;}static void ioc3_set_multicast_list(struct net_device *dev){ struct dev_mc_list *dmi = dev->mc_list; struct ioc3_private *ip = dev->priv; struct ioc3 *ioc3 = ip->regs; u64 ehar = 0; int i; netif_stop_queue(dev); /* Lock out others. */ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); ip->emcr |= EMCR_PROMISC; ioc3->emcr = ip->emcr; ioc3->emcr; } else { ip->emcr &= ~EMCR_PROMISC; ioc3->emcr = ip->emcr; /* Clear promiscuous. */ ioc3->emcr; if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) { /* Too many for hashing to make sense or we want all multicast packets anyway, so skip computing all the hashes and just accept all packets. */ ip->ehar_h = 0xffffffff; ip->ehar_l = 0xffffffff; } else { for (i = 0; i < dev->mc_count; i++) { char *addr = dmi->dmi_addr; dmi = dmi->next; if (!(*addr & 1)) continue; ehar |= (1UL << ioc3_hash(addr)); } ip->ehar_h = ehar >> 32; ip->ehar_l = ehar & 0xffffffff; } ioc3->ehar_h = ip->ehar_h; ioc3->ehar_l = ip->ehar_l; } netif_wake_queue(dev); /* Let us get going again. */}MODULE_AUTHOR("Ralf Baechle <ralf@oss.sgi.com>");MODULE_DESCRIPTION("SGI IOC3 Ethernet driver");MODULE_LICENSE("GPL");module_init(ioc3_init_module);module_exit(ioc3_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -