📄 rtl8139.c
字号:
} } if (phy_idx == 0) { printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " "transceiver.\n", dev->name); tp->phys[0] = -1; } } else { tp->phys[0] = -1; } /* Put the chip into low-power mode. */ outb(0xC0, ioaddr + Cfg9346); outb(0x03, ioaddr + Config1); outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */ /* The lower four bits are the media type. */ if (options > 0) { tp->full_duplex = (options & 16) ? 1 : 0; tp->default_port = options & 15; if (tp->default_port) tp->medialock = 1; }#ifdef MODULE if (card_idx >= 0) { if (full_duplex[card_idx] >= 0) tp->full_duplex = full_duplex[card_idx]; }#endif /* The Rtl8129-specific entries in the device structure. */ dev->open = &rtl8129_open; dev->hard_start_xmit = &rtl8129_start_xmit; dev->stop = &rtl8129_close; dev->get_stats = &rtl8129_get_stats; dev->set_multicast_list = &set_rx_mode; return dev;}/* Serial EEPROM section. *//* EEPROM_Ctrl bits. */#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */#define EE_CS 0x08 /* EEPROM chip select. */#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */#define EE_WRITE_0 0x00#define EE_WRITE_1 0x02#define EE_DATA_READ 0x01 /* EEPROM chip data out. */#define EE_ENB (0x80 | EE_CS)/* Delay between EEPROM clock transitions. No extra delay is needed with 33Mhz PCI, but 66Mhz is untested. */#ifdef _LINUX_DELAY_H#define eeprom_delay(nanosec) udelay(1)#else#define eeprom_delay(nanosec) do { ; } while (0)#endif/* The EEPROM commands include the alway-set leading bit. */#define EE_WRITE_CMD (5 << 6)#define EE_READ_CMD (6 << 6)#define EE_ERASE_CMD (7 << 6)static int read_eeprom(int ioaddr, int location){ int i; unsigned retval = 0; int ee_addr = ioaddr + Cfg9346; int read_cmd = location | EE_READ_CMD; outb(EE_ENB & ~EE_CS, ee_addr); outb(EE_ENB, ee_addr); /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outb(EE_ENB | dataval, ee_addr); eeprom_delay(100); outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(150); outb(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ eeprom_delay(250); } outb(EE_ENB, ee_addr); for (i = 16; i > 0; i--) { outb(EE_ENB | EE_SHIFT_CLK, ee_addr); eeprom_delay(100); retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); outb(EE_ENB, ee_addr); eeprom_delay(100); } /* Terminate the EEPROM access. */ outb(~EE_CS, ee_addr); return retval;}/* MII serial management: mostly bogus for now. *//* Read and write the MII management registers using software-generated serial MDIO protocol. The maximum data clock rate is 2.5 Mhz. The minimum timing is usually met by back-to-back PCI I/O cycles, but we insert a delay to avoid "overclocking" issues. */#define MDIO_DIR 0x80#define MDIO_DATA_OUT 0x04#define MDIO_DATA_IN 0x02#define MDIO_CLK 0x01#ifdef _LINUX_DELAY_H#define mdio_delay() udelay(1) /* Really 400ns. */#else#define mdio_delay() do { ; } while (0)#endif/* Syncronize the MII management interface by shifting 32 one bits out. */static void mdio_sync(int ioaddr){ int i; int mdio_addr = ioaddr + MII_SMI; for (i = 32; i >= 0; i--) { outb(MDIO_DIR | MDIO_DATA_OUT, mdio_addr); mdio_delay(); outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr); mdio_delay(); } return;}static int mdio_read(int ioaddr, int phy_id, int location){ int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; int mdio_addr = ioaddr + MII_SMI; mdio_sync(ioaddr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; outb(MDIO_DIR | dataval, mdio_addr); mdio_delay(); outb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr); mdio_delay(); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) { outb(0, mdio_addr); mdio_delay(); retval = (retval << 1) | ((inb(mdio_addr) & MDIO_DATA_IN) ? 1 : 0); outb(MDIO_CLK, mdio_addr); mdio_delay(); } return (retval>>1) & 0xffff;}static intrtl8129_open(struct device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int ioaddr = dev->base_addr; int i; int full_duplex = 0; /* Soft reset the chip. */ outb(CmdReset, ioaddr + ChipCmd); if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } MOD_INC_USE_COUNT; tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL); tp->rx_ring = kmalloc(RX_BUF_LEN + 16, GFP_KERNEL); if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { free_irq (dev->irq, dev); if (tp->tx_bufs) kfree(tp->tx_bufs); if (rtl8129_debug > 0) printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n", dev->name, RX_BUF_LEN); return -ENOMEM; } rtl8129_init_ring(dev); /* Check that the chip has finished the reset. */ for (i = 1000; i > 0; i--) if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + MAC0 + i); /* Must enable Tx/Rx before setting transfer thresholds! */ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); full_duplex = tp->full_duplex; if (tp->phys[0] >= 0 || tp->chip_id == 0x8139) { u16 mii_reg5; if (tp->chip_id == 0x8139) mii_reg5 = inw(ioaddr + NWayLPAR); else mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); if (mii_reg5 == 0xffff) ; /* Not there */ else if ((mii_reg5 & 0x0100) == 0x0100 || (mii_reg5 & 0x00C0) == 0x0040) full_duplex = 1; if (rtl8129_debug > 1) printk(KERN_INFO"%s: Setting %s%s-duplex based on" " auto-negotiated partner ability %4.4x.\n", dev->name, mii_reg5 == 0 ? "" : (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ", full_duplex ? "full" : "half", mii_reg5); } outb(0xC0, ioaddr + Cfg9346); outb(full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf); /* Start the chip's Tx and Rx process. */ outl(0, ioaddr + RxMissed); set_rx_mode(dev); outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; /* Enable all known interrupts by setting the interrupt mask. */ outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); if (rtl8129_debug > 1) printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData), full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&tp->timer); tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ tp->timer.data = (unsigned long)dev; tp->timer.function = &rtl8129_timer; /* timer handler */ add_timer(&tp->timer); return 0;}static void rtl8129_timer(unsigned long data){ struct device *dev = (struct device *)data; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int ioaddr = dev->base_addr; int next_tick = 0; if (tp->chip_id == 0x8139) { u16 mii_reg5 = inw(ioaddr + NWayLPAR); if ((mii_reg5 & 0x0100) == 0x0100 || (mii_reg5 & 0x00C0) == 0x0040) if ( ! tp->full_duplex) { tp->full_duplex = 1; if (rtl8129_debug > 0) printk(KERN_INFO "%s: Switching to full-duplex based on " "link partner ability of %4.4x.\n", dev->name, mii_reg5); outb(0xC0, ioaddr + Cfg9346); outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); } } if (rtl8129_debug > 2) { if (tp->chip_id == 0x8129) printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n", dev->name, inb(ioaddr + GPPinData)); else printk(KERN_DEBUG"%s: Media selection tick, Link partner %4.4x.\n", dev->name, inw(ioaddr + NWayLPAR)); printk(KERN_DEBUG"%s: Other registers are IntMask %4.4x IntStatus %4.4x" " RxStatus %4.4x.\n", dev->name, inw(ioaddr + IntrMask), inw(ioaddr + IntrStatus), inl(ioaddr + RxEarlyStatus)); printk(KERN_DEBUG"%s: Chip config %2.2x %2.2x.\n", dev->name, inb(ioaddr + Config0), inb(ioaddr + Config1)); } if (next_tick) { tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); }}static void rtl8129_tx_timeout(struct device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int ioaddr = dev->base_addr; int i; /* Disable interrupts by clearing the interrupt mask. */ outw(0x0000, ioaddr + IntrMask); if (rtl8129_debug > 0) printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x.\n", dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus)); /* Emit info to figure out what went wrong. */ for (i = 0; i < NUM_TX_DESC; i++) printk(KERN_DEBUG"%s: Tx descriptor %d is %8.8x.%s\n", dev->name, i, inl(ioaddr + TxStat0 + i*4), i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); if (tp->chip_id == 0x8129) { int mii_reg; printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); for (mii_reg = 0; mii_reg < 8; mii_reg++) printk(" %4.4x", mdio_read(ioaddr, tp->phys[0], mii_reg)); printk(".\n"); } else { printk(KERN_DEBUG"%s: MII status register is %4.4x.\n", dev->name, inw(ioaddr + BMSR)); } /* Soft reset the chip. */ outb(CmdReset, ioaddr + ChipCmd); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + MAC0 + i); { /* Save the unsent Tx packets. */ struct sk_buff *saved_skb[NUM_TX_DESC], *skb; int j = 0; for (; tp->cur_tx - tp->dirty_tx > 0 ; tp->dirty_tx++) saved_skb[j++] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; tp->dirty_tx = tp->cur_tx = 0; for (i = 0; i < j; i++) { skb = tp->tx_skbuff[i] = saved_skb[i]; if ((long)skb->data & 3) { /* Must use alignment buffer. */ memcpy(tp->tx_buf[i], skb->data, skb->len); outl(virt_to_bus(tp->tx_buf[i]), ioaddr + TxAddr0 + i*4); } else outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + i*4); /* Note: the chip doesn't have auto-pad! */ outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), ioaddr + TxStat0 + i*4); } tp->cur_tx = i; while (i < NUM_TX_DESC) tp->tx_skbuff[i] = 0; if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ dev->tbusy = 0; } else { tp->tx_full = 1; } } /* Must enable Tx/Rx before setting transfer thresholds! */ set_rx_mode(dev); outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); outl((TX_DMA_BURST<<8), ioaddr + TxConfig); dev->trans_start = jiffies; tp->stats.tx_errors++; /* Enable all known interrupts by setting the interrupt mask. */ outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); return;}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static voidrtl8129_init_ring(struct device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int i; tp->tx_full = 0; tp->cur_rx = tp->cur_tx = 0; tp->dirty_rx = tp->dirty_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_skbuff[i] = 0; tp->tx_buf[i] = &tp->tx_bufs[i*TX_BUF_SIZE]; }}static intrtl8129_start_xmit(struct sk_buff *skb, struct device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int ioaddr = dev->base_addr; int 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; rtl8129_tx_timeout(dev); return 1; } /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; tp->tx_skbuff[entry] = skb; if ((long)skb->data & 3) { /* Must use alignment buffer. */ memcpy(tp->tx_buf[entry], skb->data, skb->len); outl(virt_to_bus(tp->tx_buf[entry]), ioaddr + TxAddr0 + entry*4); } else outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + entry*4); /* Note: the chip doesn't have auto-pad! */ outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), ioaddr + TxStat0 + entry*4); if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ dev->tbusy = 0; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -