📄 via-rhine.c
字号:
struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);#ifdef VIA_USE_IO pciaddr = pdev->base_address[0];#else pciaddr = pdev->base_address[1];#endif irq = pdev->irq;#else u32 pci_memaddr; u8 pci_irq_line; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_irq_line);#ifdef VIA_USE_IO pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_memaddr); pciaddr = pci_memaddr;#else pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_memaddr); pciaddr = pci_memaddr;#endif irq = pci_irq_line;#endif } if (debug > 2) printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n", pci_tbl[chip_idx].name, pciaddr, irq); if (pci_tbl[chip_idx].flags & PCI_USES_IO) { if (check_region(pciaddr, pci_tbl[chip_idx].io_size)) continue; ioaddr = pciaddr & ~3; } else if ((ioaddr = (long)ioremap(pciaddr & ~0xf, pci_tbl[chip_idx].io_size)) == 0) { printk(KERN_INFO "Failed to map PCI address %#lx.\n", pciaddr); continue; } pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); new_command = pci_command | (pci_tbl[chip_idx].flags & 7); if (pci_command != new_command) { printk(KERN_INFO " The PCI BIOS has not enabled the" " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", pci_bus, pci_device_fn, pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, irq, chip_idx, cards_found); if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { u8 pci_latency; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < min_pci_latency) { printk(KERN_INFO " PCI latency timer (CFLT) is " "unreasonably low at %d. Setting to %d clocks.\n", pci_latency, min_pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, min_pci_latency); } } dev = 0; cards_found++; } return cards_found ? 0 : -ENODEV;}#ifndef MODULEint via_rhine_probe(struct device *dev){ return pci_etherdev_probe(dev, pci_tbl);}#endifstatic struct device *via_probe1(int pci_bus, int pci_devfn, struct device *dev, long ioaddr, int irq, int chip_id, int card_idx){ static int did_version = 0; /* Already printed version info */ struct netdev_private *np; int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; if (debug > 0 && did_version++ == 0) printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); dev = init_etherdev(dev, 0); printk(KERN_INFO "%s: %s at 0x%lx, ", dev->name, pci_tbl[chip_id].name, ioaddr); /* Ideally we would be read the EEPROM but access may be locked. */ for (i = 0; i <6; i++) dev->dev_addr[i] = readb(ioaddr + StationAddr + i); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);#ifdef VIA_USE_IO request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name);#endif /* Reset the chip to erase previous misconfiguration. */ writew(CmdReset, ioaddr + ChipCmd); dev->base_addr = ioaddr; dev->irq = irq; /* Make certain the descriptor lists are cache-aligned. */ np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31); memset(np, 0, sizeof(*np)); dev->priv = np; np->next_module = root_net_dev; root_net_dev = dev; np->pci_bus = pci_bus; np->pci_devfn = pci_devfn; np->chip_id = chip_id; if (dev->mem_start) option = dev->mem_start; /* The lower four bits are the media type. */ if (option > 0) { if (option & 0x200) np->full_duplex = 1; np->default_port = option & 15; if (np->default_port) np->medialock = 1; } if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) np->full_duplex = 1; if (np->full_duplex) np->duplex_lock = 1; /* The chip-specific entries in the device structure. */ dev->open = &netdev_open; dev->hard_start_xmit = &start_tx; dev->stop = &netdev_close; dev->get_stats = &get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &mii_ioctl; if (cap_tbl[np->chip_id].flags & CanHaveMII) { int phy, phy_idx = 0; np->phys[0] = 1; /* Standard for this chip. */ for (phy = 1; phy < 32 && phy_idx < 4; phy++) { int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { np->phys[phy_idx++] = phy; np->advertising = mdio_read(dev, phy, 4); printk(KERN_INFO "%s: MII PHY found at address %d, status " "0x%4.4x advertising %4.4x Link %4.4x.\n", dev->name, phy, mii_status, np->advertising, mdio_read(dev, phy, 5)); } } np->mii_cnt = phy_idx; } return dev;}/* Read and write over the MII Management Data I/O (MDIO) interface. */static int mdio_read(struct device *dev, int phy_id, int regnum){ long ioaddr = dev->base_addr; int boguscnt = 1024; /* Wait for a previous command to complete. */ while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) ; writeb(0x00, ioaddr + MIICmd); writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writeb(0x40, ioaddr + MIICmd); /* Trigger read */ boguscnt = 1024; while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0) ; return readw(ioaddr + MIIData);}static void mdio_write(struct device *dev, int phy_id, int regnum, int value){ long ioaddr = dev->base_addr; int boguscnt = 1024; /* Wait for a previous command to complete. */ while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) ; writeb(0x00, ioaddr + MIICmd); writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writew(value, ioaddr + MIIData); writeb(0x20, ioaddr + MIICmd); /* Trigger write. */ return;}static int netdev_open(struct device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; int i; /* Reset the chip. */ writew(CmdReset, ioaddr + ChipCmd); if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) return -EAGAIN; if (debug > 1) printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", dev->name, dev->irq); MOD_INC_USE_COUNT; init_ring(dev); writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); for (i = 0; i < 6; i++) writeb(dev->dev_addr[i], ioaddr + StationAddr + i); /* Initialize other registers. */ writew(0x0006, ioaddr + PCIConfig); /* Tune configuration??? */ /* Configure the FIFO thresholds. */ writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */ np->tx_thresh = 0x20; np->rx_thresh = 0x60; /* Written in set_rx_mode(). */ if (dev->if_port == 0) dev->if_port = np->default_port; dev->tbusy = 0; dev->interrupt = 0; np->in_interrupt = 0; set_rx_mode(dev); dev->start = 1; /* Enable interrupts by setting the interrupt mask. */ writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped| IntrTxDone | IntrTxAbort | IntrTxUnderrun | IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange, ioaddr + IntrEnable); np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll; writew(np->chip_cmd, ioaddr + ChipCmd); check_duplex(dev); if (debug > 2) printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x " "MII status: %4.4x.\n", dev->name, readw(ioaddr + ChipCmd), mdio_read(dev, np->phys[0], 1)); /* Set the timer to check for link beat. */ init_timer(&np->timer); np->timer.expires = RUN_AT(1); np->timer.data = (unsigned long)dev; np->timer.function = &netdev_timer; /* timer handler */ add_timer(&np->timer); return 0;}static void check_duplex(struct device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; int mii_reg5 = mdio_read(dev, np->phys[0], 5); int duplex; if (np->duplex_lock || mii_reg5 == 0xffff) return; duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040; if (np->full_duplex != duplex) { np->full_duplex = duplex; if (debug) printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" " partner capability of %4.4x.\n", dev->name, duplex ? "full" : "half", np->phys[0], mii_reg5); if (duplex) np->chip_cmd |= CmdFDuplex; else np->chip_cmd &= ~CmdFDuplex; writew(np->chip_cmd, ioaddr + ChipCmd); }}static void netdev_timer(unsigned long data){ struct device *dev = (struct device *)data; struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; int next_tick = 10*HZ; if (debug > 3) { printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", dev->name, readw(ioaddr + IntrStatus)); } check_duplex(dev); np->timer.expires = RUN_AT(next_tick); add_timer(&np->timer);}static void tx_timeout(struct device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status " "%4.4x, resetting...\n", dev->name, readw(ioaddr + IntrStatus), mdio_read(dev, np->phys[0], 1)); /* Perhaps we should reinitialize the hardware here. */ dev->if_port = 0; /* Stop and restart the chip's Tx processes . */ /* Trigger an immediate transmit demand. */ dev->trans_start = jiffies; np->stats.tx_errors++; return;}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static void init_ring(struct device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; int i; np->tx_full = 0; np->cur_rx = np->cur_tx = 0; np->dirty_rx = np->dirty_tx = 0; np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); np->rx_head_desc = &np->rx_ring[0]; for (i = 0; i < RX_RING_SIZE; i++) { np->rx_ring[i].rx_status = 0; np->rx_ring[i].rx_length = 0; np->rx_ring[i].desc_length = np->rx_buf_sz; np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]); np->rx_skbuff[i] = 0; } /* Mark the last entry as wrapping the ring. */ np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]); /* Fill in the Rx buffers. */ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); np->rx_skbuff[i] = skb; if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ np->rx_ring[i].addr = virt_to_bus(skb->tail); np->rx_ring[i].rx_status = 0; np->rx_ring[i].rx_length = DescOwn; } np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); for (i = 0; i < TX_RING_SIZE; i++) { np->tx_skbuff[i] = 0; np->tx_ring[i].tx_own = 0; np->tx_ring[i].desc_length = 0x00e08000; np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]); np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL); } np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]); return;}static int start_tx(struct sk_buff *skb, struct device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; unsigned entry; /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start < TX_TIMEOUT) return 1; tx_timeout(dev); return 1; } /* Caution: the write order is important here, set the field with the "ownership" bits last. */ /* Calculate the next Tx descriptor entry. */ entry = np->cur_tx % TX_RING_SIZE; np->tx_skbuff[entry] = skb; if ((long)skb->data & 3) { /* Must use alignment buffer. */ if (np->tx_buf[entry] == NULL && (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) return 1; memcpy(np->tx_buf[entry], skb->data, skb->len); np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]); } else np->tx_ring[entry].addr = virt_to_bus(skb->data); np->tx_ring[entry].desc_length = 0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); np->tx_ring[entry].tx_own = DescOwn; np->cur_tx++; /* Non-x86 Todo: explicitly flush cache lines here. */ /* Wake the potentially-idle transmit channel. */ writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1) clear_bit(0, (void*)&dev->tbusy); /* Typical path */ else np->tx_full = 1; dev->trans_start = jiffies; if (debug > 4) { printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -