📄 pcnet_cs.c
字号:
/*====================================================================== Hard reset the card. This used to pause for the same period that a 8390 reset command required, but that shouldn't be necessary.======================================================================*/static void pcnet_reset_8390(struct net_device *dev){ kio_addr_t nic_base = dev->base_addr; int i; ei_status.txing = ei_status.dmaing = 0; outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD); outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET); for (i = 0; i < 100; i++) { if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0) break; udelay(100); } outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */ if (i == 100) printk(KERN_ERR "%s: pcnet_reset_8390() did not complete.\n", dev->name); set_misc_reg(dev); } /* pcnet_reset_8390 *//*====================================================================*/static int set_config(struct net_device *dev, struct ifmap *map){ pcnet_dev_t *info = PRIV(dev); if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { if (!(info->flags & HAS_MISC_REG)) return -EOPNOTSUPP; else if ((map->port < 1) || (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]); NS8390_init(dev, 1); } return 0;}/*====================================================================*/static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = dev_id; pcnet_dev_t *info; irqreturn_t ret = ei_interrupt(irq, dev_id, regs); if (ret == IRQ_HANDLED) { info = PRIV(dev); info->stale = 0; } return ret;}static void ei_watchdog(u_long arg){ struct net_device *dev = (struct net_device *)arg; pcnet_dev_t *info = PRIV(dev); kio_addr_t nic_base = dev->base_addr; kio_addr_t mii_addr = nic_base + DLINK_GPIO; u_short link; if (!netif_device_present(dev)) goto reschedule; /* Check for pending interrupt with expired latency timer: with this, we can limp along even if the interrupt is blocked */ outb_p(E8390_NODMA+E8390_PAGE0, nic_base + E8390_CMD); if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) { if (!info->fast_poll) printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); ei_irq_wrapper(dev->irq, dev, NULL); info->fast_poll = HZ; } if (info->fast_poll) { info->fast_poll--; info->watchdog.expires = jiffies + 1; add_timer(&info->watchdog); return; } if (!(info->flags & HAS_MII)) goto reschedule; mdio_read(mii_addr, info->phy_id, 1); link = mdio_read(mii_addr, info->phy_id, 1); if (!link || (link == 0xffff)) { if (info->eth_phy) { info->phy_id = info->eth_phy = 0; } else { printk(KERN_INFO "%s: MII is missing!\n", dev->name); info->flags &= ~HAS_MII; } goto reschedule; } link &= 0x0004; if (link != info->link_status) { u_short p = mdio_read(mii_addr, info->phy_id, 5); printk(KERN_INFO "%s: %s link beat\n", dev->name, (link) ? "found" : "lost"); if (link && (info->flags & IS_DL10022)) { /* Disable collision detection on full duplex links */ outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG); } else if (link && (info->flags & IS_DL10019)) { /* Disable collision detection on full duplex links */ write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0); } if (link) { if (info->phy_id == info->eth_phy) { if (p) printk(KERN_INFO "%s: autonegotiation complete: " "%sbaseT-%cD selected\n", dev->name, ((p & 0x0180) ? "100" : "10"), ((p & 0x0140) ? 'F' : 'H')); else printk(KERN_INFO "%s: link partner did not " "autonegotiate\n", dev->name); } NS8390_init(dev, 1); } info->link_status = link; } if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) { link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004; if (((info->phy_id == info->pna_phy) && link) || ((info->phy_id != info->pna_phy) && !link)) { /* isolate this MII and try flipping to the other one */ mdio_write(mii_addr, info->phy_id, 0, 0x0400); info->phy_id ^= info->pna_phy ^ info->eth_phy; printk(KERN_INFO "%s: switched to %s transceiver\n", dev->name, (info->phy_id == info->eth_phy) ? "ethernet" : "PNA"); mdio_write(mii_addr, info->phy_id, 0, (info->phy_id == info->eth_phy) ? 0x1000 : 0); info->link_status = 0; info->mii_reset = jiffies; } }reschedule: info->watchdog.expires = jiffies + HZ; add_timer(&info->watchdog);}/*====================================================================*/static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, "pcnet_cs");}static struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo,};/*====================================================================*/static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ pcnet_dev_t *info = PRIV(dev); u16 *data = (u16 *)&rq->ifr_ifru; kio_addr_t mii_addr = dev->base_addr + DLINK_GPIO; switch (cmd) { case SIOCGMIIPHY: data[0] = info->phy_id; case SIOCGMIIREG: /* Read MII PHY register. */ data[3] = mdio_read(mii_addr, data[0], data[1] & 0x1f); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(mii_addr, data[0], data[1] & 0x1f, data[2]); return 0; } return -EOPNOTSUPP;}/*====================================================================*/static void dma_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page){ kio_addr_t nic_base = dev->base_addr; if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; } ei_status.dmaing |= 0x01; outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD); outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); outb_p(0, nic_base + EN0_RCNTHI); outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ outb_p(ring_page, nic_base + EN0_RSARHI); outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD); insw(nic_base + PCNET_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); /* Fix for big endian systems */ hdr->count = le16_to_cpu(hdr->count); outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01;}/*====================================================================*/static void dma_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset){ kio_addr_t nic_base = dev->base_addr; int xfer_count = count; char *buf = skb->data;#ifdef PCMCIA_DEBUG if ((ei_debug > 4) && (count != 4)) printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4);#endif if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; } ei_status.dmaing |= 0x01; outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD); outb_p(count & 0xff, nic_base + EN0_RCNTLO); outb_p(count >> 8, nic_base + EN0_RCNTHI); outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD); insw(nic_base + PCNET_DATAPORT,buf,count>>1); if (count & 0x01) buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++; /* This was for the ALPHA version only, but enough people have been encountering problems that it is still here. */#ifdef PCMCIA_DEBUG if (ei_debug > 4) { /* DMA termination address check... */ int addr, tries = 20; do { /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken for Rx on some cards! */ int high = inb_p(nic_base + EN0_RSARHI); int low = inb_p(nic_base + EN0_RSARLO); addr = (high << 8) + low; if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff)) break; } while (--tries > 0); if (tries <= 0) printk(KERN_NOTICE "%s: RX transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, ring_offset + xfer_count, addr); }#endif outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01;} /* dma_block_input *//*====================================================================*/static void dma_block_output(struct net_device *dev, int count, const u_char *buf, const int start_page){ kio_addr_t nic_base = dev->base_addr; pcnet_dev_t *info = PRIV(dev);#ifdef PCMCIA_DEBUG int retries = 0;#endif u_long dma_start;#ifdef PCMCIA_DEBUG if (ei_debug > 4) printk(KERN_DEBUG "%s: [bo=%d]\n", dev->name, count);#endif /* Round the count up for word writes. Do we need to do this? What effect will an odd byte count have on the 8390? I should check someday. */ if (count & 0x01) count++; if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_output." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; } ei_status.dmaing |= 0x01; /* We should already be in page 0, but to be safe... */ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);#ifdef PCMCIA_DEBUG retry:#endif outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Now the normal output. */ outb_p(count & 0xff, nic_base + EN0_RCNTLO); outb_p(count >> 8, nic_base + EN0_RCNTHI); outb_p(0x00, nic_base + EN0_RSARLO); outb_p(start_page, nic_base + EN0_RSARHI); outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD); outsw(nic_base + PCNET_DATAPORT, buf, count>>1); dma_start = jiffies;#ifdef PCMCIA_DEBUG /* This was for the ALPHA version only, but enough people have been encountering problems that it is still here. */ if (ei_debug > 4) { /* DMA termination address check... */ int addr, tries = 20; do { int high = inb_p(nic_base + EN0_RSARHI); int low = inb_p(nic_base + EN0_RSARLO); addr = (high << 8) + low; if ((start_page << 8) + count == addr) break; } while (--tries > 0); if (tries <= 0) { printk(KERN_NOTICE "%s: Tx packet transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, (start_page << 8) + count, addr); if (retries++ == 0) goto retry; } }#endif while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) { printk(KERN_NOTICE "%s: timeout waiting for Tx RDC.\n", dev->name); pcnet_reset_8390(dev); NS8390_init(dev, 1); break; } outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ if (info->flags & DELAY_OUTPUT) udelay((long)delay_time); ei_status.dmaing &= ~0x01;}/*====================================================================*/static int setup_dma_config(dev_link_t *link, int start_pg, int stop_pg)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -