/* tulip.c: A DEC 21040-family ethernet driver for linux. */ /* Written 1994-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. This driver is for the SMC EtherPower PCI ethernet adapter. It should work with most other DEC 21*40-based ethercards. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 Support and updates available at http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html */ //#define DUMM_TULIP 1 //done in the Makefile to create tulip_old.o #include #ifdef MODULE #ifdef MODVERSIONS #include #endif #include #include #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #endif #include #include #include #include #include #include #include #include #include #include #if 1 //def CONFIG_ARCH_VNC #include #include /* this call prefers aligned addresses, and the end address in non-inclusive */ #define FLUSH_DATA_CACHE(obj,len) flush_cache_area(((unsigned int)obj) & ~0x1f, \ ((unsigned int)obj + len + 0x1f) & ~0x1f, 0) #else #include #define FLUSH_DATA_CACHE(obj,len) //nothing #endif /* CONFIG_ARCH_VNC */ #include /* Processor type for cache alignment. */ #include #include #include #include #include #include #if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 #ifdef MODULE #if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) char kernel_version[] = UTS_RELEASE; #endif #else #undef MOD_INC_USE_COUNT #define MOD_INC_USE_COUNT #undef MOD_DEC_USE_COUNT #define MOD_DEC_USE_COUNT #endif #endif /* 1.3.38 */ #if (LINUX_VERSION_CODE >= 0x10344) #define NEW_MULTICAST #include #endif /* Offsets to the Command and Status Registers, "CSRs". All accesses must be longword instructions and quadword aligned. */ enum tulip_offsets { CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; extern unsigned int board_id; extern unsigned int serial_num; //#ifdef CONFIG_ARCH_VNC //* //* VNC has memory mapped access to the 21143's CSRs, //* therefore we have to redefine register input and output. //* #undef outl(val,port) #undef inl(port) extern __inline__ void __mem_outl(unsigned int value, unsigned int addr) { *(int*)(addr) = value; } extern __inline__ unsigned long __mem_inl(unsigned int addr) { return *(unsigned long*)(addr); } #define outl(val,port) __mem_outl((val),(port)) #define inl(port) __mem_inl((port)) //#endif /* CONFIG_ARCH_VNC */ /* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ /* EEPROM_Ctrl bits. */ #define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ #define EE_CS 0x01 /* EEPROM chip select. */ #define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ #define EE_WRITE_0 0x01 #define EE_WRITE_1 0x05 #define EE_DATA_READ 0x08 /* EEPROM chip data out. */ #define EE_ENB (0x4800 | EE_CS) #define EE_ENB_W (0x2800 | EE_CS) /* Delay between EEPROM clock transitions. The 1.2 code is a "nasty" timing loop, but PC compatible machines are *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ #ifdef _LINUX_DELAY_H #define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) #else #define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} 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 short retval = 0; int ee_addr = ioaddr + CSR9; int read_cmd = location | EE_READ_CMD; outl(EE_ENB & ~EE_CS, ee_addr); outl(EE_ENB, ee_addr); /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outl(EE_ENB | dataval, ee_addr); eeprom_delay(100); outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(150); outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ eeprom_delay(250); } outl(EE_ENB, ee_addr); for (i = 16; i > 0; i--) { outl(EE_ENB | EE_SHIFT_CLK, ee_addr); eeprom_delay(100); retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); outl(EE_ENB, ee_addr); eeprom_delay(100); } /* Terminate the EEPROM access. */ outl(EE_ENB & ~EE_CS, ee_addr); return retval; } #define EE_WRITE_EN_CMD (4 << 6) static void write_eeprom(int ioaddr, int location, int value) { int i; unsigned short retval = 0; int ee_addr = ioaddr + CSR9; int write_cmd = ((location | EE_WRITE_CMD) << 16) | (value & 0x0FFFF); outl(EE_ENB_W & ~EE_CS, ee_addr); outl(EE_ENB_W, ee_addr); /* Shift the write command bits out. */ for (i = 26; i >= 0; i--) { short dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outl(EE_ENB_W | dataval, ee_addr); eeprom_delay(150); outl(EE_ENB_W | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(250); outl(EE_ENB_W | dataval, ee_addr); /* Finish EEPROM a clock tick. */ eeprom_delay(100); } outl(EE_ENB_W & ~EE_CS, ee_addr); eeprom_delay(250); outl(EE_ENB_W, ee_addr); eeprom_delay(250); do { retval = (inl(ee_addr) & EE_DATA_READ) ? 1 : 0; if (retval) break; eeprom_delay(1000); } while(1); /* Terminate the EEPROM access. */ outl(EE_ENB_W & ~EE_CS, ee_addr); } static void write_en_ds_eeprom(int ioaddr, int enable) { int i; int ee_addr = ioaddr + CSR9; int write_cmd = EE_WRITE_EN_CMD | (enable?0x30:0x00); outl(EE_ENB_W & ~EE_CS, ee_addr); outl(EE_ENB_W, ee_addr); /* Shift the write command bits out. */ for (i = 10; i >= 0; i--) { short dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outl(EE_ENB_W | dataval, ee_addr); eeprom_delay(150); outl(EE_ENB_W | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(250); outl(EE_ENB_W | dataval, ee_addr); /* Finish EEPROM a clock tick. */ eeprom_delay(100); } /* Terminate the EEPROM access. */ outl(EE_ENB_W & ~EE_CS, ee_addr); } #define CRC2_POLYNOMIAL 0x04c11db7 static unsigned int calc_checksum(unsigned short *eeprom) { int crc = -1; /* NB really 32 bits, int32 or __s32. */ int i, bit; for (i = 0; i < 63; i++) /* Note: loc. 63 is the CRC. */ for (bit = 0; bit < 16; bit++) { int msb = crc < 0; crc <<= 1; if (((eeprom[i]>>bit) ^ msb) & 1) crc ^= CRC2_POLYNOMIAL; } return crc; } int init_module(void) { int i; unsigned short sum = 0; int ioaddr; unsigned char vnc_eeprom[128] = /* Serial EEPROM contents. */ { #if DUMM_TULIP //old fasion SROM 0x00,0x10,0x57,0xC0,0x08,0x09,0,0,0,0,0,0,0,0,0,0, 0x00,0x10,0x57,0xC0,0x08,0x09,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 #else 0x4E,0x57, /* sub vendor ID 'NW' */ 0x54,0x55, /* subsystem ID 'TU'*/ 0,0,0,0,0,0,0,0,0,0,0,0, /* 12 reserved */ 0xBB, /* ID_BLOCK_CRC */ 0x00, /* reserved */ 0x03, /* SROM format */ 0x01, /* chip count */ 0x00,0x10,0x57,0xC0,0x08,0x09, /* MAC address */ 0x00, /* chip 0 device number */ 0x1E,0x00, /* Chip_0 info offset */ 0x00, /* filler... */ 0x00,0x08, /* connection type = 0x0800 == AutoSense */ 0x01, /* block count */ 0x99, /* 13+12 bytes in extended format */ 0x03, /* MII PHY chip block */ 0x00, /* PHY number */ 0x02, /* GPR length */ 0x2F,0x08, /* init sequence */ 0x2F,0x00, /* set ports to OUTPUTS, GEP1 as active LED */ 0x04, /* Reset length */ 0x2F,0x08, /* first do an init sequence */ 0x2F,0x00, /* set ports to OUTPUTS, GEP1 as active LED */ 0x0E,0x00, /* reset sequence */ 0x0F,0x00, /* toggle output GEP0 */ 0x00,0x78, /* Media Capabilities */ 0xE0,0x01, /* Advertisement */ 0x00,0x50, /* FullDuplex bitmap (both 10 and 100 are OK) */ 0x00,0x00, /* Transmit Threshold (no limit) */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x34,0x12 /* CRC32 */ #endif }; /* char *config_reg = (char *)(IO_BASE_PCI_CONFIG + 0x00100000); ioaddr = *(int *)(config_reg + PCI_BASE_ADDRESS_1); //* //* VNC has memory mapped i/o for the DC21143 //* ioaddr += 0xC1400000; // mapped PCI memory address */ ioaddr = 0xE1400000; // mapped PCI memory address #if DUMM_TULIP printk("Restoring old tulip for board rev. %X serial #: %d...\n",board_id, serial_num); #else printk("Upgrading board rev. %X serial #: %d...\n",board_id, serial_num); #endif { #if DUMM_TULIP vnc_eeprom[4] = (serial_num>>8) & 0xFF; vnc_eeprom[5] = serial_num & 0xFF; vnc_eeprom[20] = (serial_num>>8) & 0xFF; vnc_eeprom[21] = serial_num & 0xFF; #else vnc_eeprom[24] = (serial_num>>8) & 0xFF; vnc_eeprom[25] = serial_num & 0xFF; i = calc_checksum((unsigned short *)&vnc_eeprom); vnc_eeprom[126] = i & 0xFF; vnc_eeprom[127] = (i>>8) & 0xFF; #endif #if 1 // Write hardcoded MAC address for test only. write_en_ds_eeprom(ioaddr, 1); for (i=0; i<128/2; i++) { write_eeprom(ioaddr, i, *(int*)(vnc_eeprom+(i*2))); } write_en_ds_eeprom(ioaddr, 0); #endif printk(KERN_DEBUG "read_eeprom:"); for (i = 0; i < 64; i++) { printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ", read_eeprom(ioaddr, i)); } printk("\n"); } printk("Done OK..\n"); return (0); } void cleanup_module(void) { }