📄 tulip_cb.c
字号:
s32 status; s32 length; u32 buffer1, buffer2;};struct tulip_tx_desc { s32 status; s32 length; u32 buffer1, buffer2; /* We use only buffer 1. */};enum desc_status_bits { DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,};/* Ring-wrap flag in length field, use for last ring entry. 0x01000000 means chain on buffer2 address, 0x02000000 means use the ring start address in CSR2/3. Note: Some work-alike chips do not function correctly in chained mode. The ASIX chip works only in chained mode. Thus we indicates ring mode, but always write the 'next' field for chained mode as well.*/#define DESC_RING_WRAP 0x02000000#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */struct medialeaf { u8 type; u8 media; unsigned char *leafdata;};struct mediatable { u16 defaultmedia; u8 leafcount, csr12dir; /* General purpose pin directions. */ unsigned has_mii:1, has_nonmii:1, has_reset:6; u32 csr15dir, csr15val; /* 21143 NWay setting. */ struct medialeaf mleaf[0];};struct mediainfo { struct mediainfo *next; int info_type; int index; unsigned char *info;};struct tulip_private { char devname[8]; /* Used only for kernel debugging. */ const char *product_name; struct net_device *next_module; struct tulip_rx_desc rx_ring[RX_RING_SIZE]; struct tulip_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE];#ifdef CARDBUS /* The X3201-3 requires double word aligned tx bufs */ struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];#endif /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; void *priv_addr; char *rx_buffs; /* Address of temporary Rx buffers. */ u8 setup_buf[96*sizeof(u16) + 7]; u16 *setup_frame; /* Pseudo-Tx frame to init address table. */ int chip_id; int revision; int flags; struct net_device_stats stats; struct timer_list timer; /* Media selection timer. */ unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int full_duplex_lock:1; unsigned int fake_addr:1; /* Multiport board faked address. */ unsigned int default_port:4; /* Last dev->if_port value. */ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ unsigned int mediasense:1; /* Media sensing in progress. */ unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */ unsigned int open:1; unsigned int reap:1; unsigned int csr0; /* CSR0 setting. */ unsigned int csr6; /* Current CSR6 control settings. */ unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ void (*link_change)(struct net_device *dev, int csr5); u16 to_advertise; /* NWay capabilities advertised. */ u16 lpar; /* 21143 Link partner ability. */ u16 advertising[4]; signed char phys[4], mii_cnt; /* MII device addresses. */ struct mediatable *mtable; int cur_index; /* Current media index. */ int saved_if_port; unsigned char pci_bus, pci_devfn; int ttimer; int susp_rx; unsigned long nir; int pad0, pad1; /* Used for 8-byte alignment */};static void parse_eeprom(struct net_device *dev);static int read_eeprom(long ioaddr, int location, int addr_len);static int mdio_read(struct net_device *dev, int phy_id, int location);static void mdio_write(struct net_device *dev, int phy_id, int location, int value);static void select_media(struct net_device *dev, int startup);static void tulip_up(struct net_device *dev);static void tulip_down(struct net_device *dev);static int tulip_open(struct net_device *dev);/* Chip-specific media selection (timer functions prototyped above). */static void t21142_lnk_change(struct net_device *dev, int csr5);static void t21142_start_nway(struct net_device *dev);static void pnic_lnk_change(struct net_device *dev, int csr5);static void pnic_do_nway(struct net_device *dev);static void tulip_tx_timeout(struct net_device *dev);static void tulip_init_ring(struct net_device *dev);static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);static int tulip_refill_rx(struct net_device *dev);static int tulip_rx(struct net_device *dev);static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);static int tulip_close(struct net_device *dev);static struct net_device_stats *tulip_get_stats(struct net_device *dev);static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static void set_rx_mode(struct net_device *dev);/* The Xircom cards are picky about when certain bits in CSR6 can be manipulated. Keith Owens <kaos@ocs.com.au>. */static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx){ const int strict_bits = 0x0060e202; int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; long flags; save_flags(flags); cli(); if (chip_idx != X3201_3) { outl(newcsr6, ioaddr + CSR6); restore_flags(flags); return; } newcsr6 &= 0x726cfecb; /* mask out the reserved CSR6 bits that always */ /* read 0 on the Xircom cards */ newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */ currcsr6 = inl(ioaddr + CSR6); if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || ((currcsr6 & ~0x2002) == 0)) { outl(newcsr6, ioaddr + CSR6); /* safe */ restore_flags(flags); return; } /* make sure the transmitter and receiver are stopped first */ currcsr6 &= ~0x2002; while (1) { csr5 = inl(ioaddr + CSR5); if (csr5 == 0xffffffff) break; /* cannot read csr5, card removed? */ csr5_22_20 = csr5 & 0x700000; csr5_19_17 = csr5 & 0x0e0000; if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) break; /* both are stopped or suspended */ if (!--attempts) { printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts," "csr5=0x%08x\n", csr5); outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ restore_flags(flags); return; } outl(currcsr6, ioaddr + CSR6); udelay(50); } /* now it is safe to change csr6 */ outl(newcsr6, ioaddr + CSR6); restore_flags(flags);}/* A list of all installed Tulip devices. */static struct net_device *root_tulip_dev = NULL;static struct net_device *tulip_probe1(int pci_bus, int pci_devfn, struct net_device *dev, long ioaddr, int irq, int chip_idx, int board_idx){ static int did_version = 0; /* Already printed version info. */ struct tulip_private *tp; /* See note below on the multiport cards. */ static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; static int last_irq = 0; static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ u8 chip_rev; int i; unsigned short sum; u8 ee_data[EEPROM_SIZE]; if (tulip_debug > 0 && did_version++ == 0) printk(KERN_INFO "%s", version); dev = init_etherdev(dev, 0); /* Make certain the data structures are quadword aligned. */ { void *mem = kmalloc(sizeof(*tp) + 7, GFP_KERNEL | GFP_DMA); tp = (void *)(((long)mem + 7) & ~7); memset(tp, 0, sizeof(*tp)); tp->priv_addr = mem; } dev->priv = tp; tp->next_module = root_tulip_dev; root_tulip_dev = dev; pcibios_read_config_byte(pci_bus, pci_devfn, PCI_REVISION_ID, &chip_rev); /* Bring the 21041/21143 out of sleep mode. Caution: Snooze mode does not work with some boards! */ if (tulip_tbl[chip_idx].flags & HAS_PWRDWN) pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x00000000); printk(KERN_INFO "%s: %s rev %d at %#3lx,", dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr); /* Stop the chip's Tx and Rx processes. */ outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx); /* Clear the missed-packet counter. */ (volatile int)inl(ioaddr + CSR8); if (chip_idx == DC21041 && inl(ioaddr + CSR9) & 0x8000) { printk(" 21040 compatible mode,"); chip_idx = DC21040; } /* The station address ROM is read byte serially. The register must be polled, waiting for the value to be read bit serially from the EEPROM. */ sum = 0; if (chip_idx == DC21040) { outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ for (i = 0; i < 6; i++) { int value, boguscnt = 100000; do value = inl(ioaddr + CSR9); while (value < 0 && --boguscnt > 0); dev->dev_addr[i] = value; sum += value & 0xff; } } else if (chip_idx == LC82C168) { for (i = 0; i < 3; i++) { int value, boguscnt = 100000; outl(0x600 | i, ioaddr + 0x98); do value = inl(ioaddr + CSR9); while (value < 0 && --boguscnt > 0); put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i); sum += value & 0xffff; } } else if (chip_idx == COMET || chip_idx == CENTAUR) { /* No need to read the EEPROM. */ put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr); put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4)); for (i = 0; i < 6; i ++) sum += dev->dev_addr[i]; } else if (chip_idx == X3201_3) { /* Xircom has its address stored in the CIS * we access it through the boot rom interface for now * this might not work, as the CIS is not parsed but I * (danilo) use the offset I found on my card's CIS !!! * * Doug Ledford: I changed this routine around so that it * walks the CIS memory space, parsing the config items, and * finds the proper lan_node_id tuple and uses the data * stored there. */ unsigned char j, tuple, link, data_id, data_count; outl(1<<12, ioaddr + CSR9); /* enable boot rom access */ for (i = 0x100; i < 0x1f7; i += link+2) { outl(i, ioaddr + CSR10); tuple = inl(ioaddr + CSR9) & 0xff; outl(i + 1, ioaddr + CSR10); link = inl(ioaddr + CSR9) & 0xff; outl(i + 2, ioaddr + CSR10); data_id = inl(ioaddr + CSR9) & 0xff; outl(i + 3, ioaddr + CSR10); data_count = inl(ioaddr + CSR9) & 0xff; if ( (tuple == 0x22) && (data_id == 0x04) && (data_count == 0x06) ) { /* * This is it. We have the data we want. */ for (j = 0; j < 6; j++) { outl(i + j + 4, ioaddr + CSR10); dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; } break; } else if (link == 0) { break; } } sum = 1; // to make check below fail! } else { /* A serial EEPROM interface, we read now and sort it out later. */ int sa_offset = 0; int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; for (i = 0; i < sizeof(ee_data)/2; i++) ((u16 *)ee_data)[i] = le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size)); /* DEC now has a specificiation (see Notes) but early board makers just put the address in the first EEPROM locations. */ /* This does memcmp(eedata, eedata+16, 8) */ for (i = 0; i < 8; i ++) if (ee_data[i] != ee_data[16+i]) sa_offset = 20; if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { sa_offset = 2; /* Grrr, damn Matrox boards. */ multiport_cnt = 4; } for (i = 0; i < 6; i ++) { dev->dev_addr[i] = ee_data[i + sa_offset]; sum += ee_data[i + sa_offset]; } } /* Lite-On boards have the address byte-swapped. */ if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0) && dev->dev_addr[1] == 0x00) for (i = 0; i < 6; i+=2) { char tmp = dev->dev_addr[i]; dev->dev_addr[i] = dev->dev_addr[i+1]; dev->dev_addr[i+1] = tmp; } /* On the Zynx 315 Etherarray and other multiport boards only the first Tulip has an EEPROM. The addresses of the subsequent ports are derived from the first. Many PCI BIOSes also incorrectly report the IRQ line, so we correct that here as well. */ if (sum == 0 || sum == 6*0xff) { printk(" EEPROM not present,"); for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1;#if defined(__i386__) /* Patch up x86 BIOS bug. */ if (last_irq) irq = last_irq;#endif } for (i = 0; i < 6; i++) printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]); printk(", IRQ %d.\n", irq); last_irq = irq; /* We do a request_region() only to register /proc/ioports info. */ /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */ request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name); dev->base_addr = ioaddr; dev->irq = irq; tp->pci_bus = pci_bus; tp->pci_devfn = pci_devfn; tp->chip_id = chip_idx; tp->revision = chip_rev; tp->flags = tulip_tbl[chip_idx].flags; tp->csr0 = csr0; tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7); /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. And the ASIX must have a burst limit or horrible things happen. */ if ((chip_idx == DC21143 && chip_rev == 65) || (chip_idx == X3201_3)) tp->csr0 &= ~0x01000000; else if (chip_idx == AX88140) tp->csr0 |= 0x2000;#ifdef TULIP_FULL_DUPLEX tp->full_duplex = 1; tp->full_duplex_lock = 1;#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -