1 /* $NetBSD: 3c509.c,v 1.6 1999/02/19 19:30:46 drochner Exp $ */ 2 3 /* stripped down from freebsd:sys/i386/netboot/3c509.c */ 4 5 /************************************************************************** 6 NETBOOT - BOOTP/TFTP Bootstrap Program 7 8 Author: Martin Renters. 9 Date: Mar 22 1995 10 11 This code is based heavily on David Greenman's if_ed.c driver and 12 Andres Vega Garcia's if_ep.c driver. 13 14 Copyright (C) 1993-1994, David Greenman, Martin Renters. 15 Copyright (C) 1993-1995, Andres Vega Garcia. 16 Copyright (C) 1995, Serge Babkin. 17 This software may be used, modified, copied, distributed, and sold, in 18 both source and binary form provided that the above copyright and these 19 terms are retained. Under no circumstances are the authors responsible for 20 the proper functioning of this software, nor do the authors assume any 21 responsibility for damages incurred with its use. 22 23 3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) 24 25 3c509.c,v 1.2 1995/05/30 07:58:52 rgrimes Exp 26 27 ***************************************************************************/ 28 29 #include <sys/types.h> 30 #include <machine/pio.h> 31 32 #include <lib/libsa/stand.h> 33 #include <lib/libkern/libkern.h> 34 35 #include <libi386.h> 36 #ifdef _STANDALONE 37 #include <bootinfo.h> 38 #endif 39 40 #include "etherdrv.h" 41 #include "3c509.h" 42 43 int ether_medium; 44 unsigned short eth_base; 45 46 extern void epreset __P((void)); 47 extern int ep_get_e __P((int)); 48 49 static int send_ID_sequence __P((int)); 50 static int get_eeprom_data __P((int, int)); 51 52 u_char eth_myaddr[6]; 53 54 static struct mtabentry { 55 int address_cfg; /* configured connector */ 56 int config_bit; /* connector present */ 57 char *name; 58 } mediatab[] = { /* indexed by media type - etherdrv.h */ 59 {3, IS_BNC, "BNC"}, 60 {0, IS_UTP, "UTP"}, 61 {1, IS_AUI, "AUI"}, 62 }; 63 64 #ifdef _STANDALONE 65 static struct btinfo_netif bi_netif; 66 #endif 67 68 #ifndef _STANDALONE 69 extern int mapio __P((void)); 70 #endif 71 72 /************************************************************************** 73 ETH_PROBE - Look for an adapter 74 ***************************************************************************/ 75 int EtherInit(myadr) 76 unsigned char *myadr; 77 { 78 /* common variables */ 79 int i; 80 /* variables for 3C509 */ 81 int data, j, id_port = EP_ID_PORT; 82 u_short k; 83 /* int ep_current_tag = EP_LAST_TAG + 1; */ 84 short *p; 85 struct mtabentry *m; 86 87 #ifndef _STANDALONE 88 if (mapio()) { 89 printf("no IO access\n"); 90 return (0); 91 } 92 #endif 93 94 /********************************************************* 95 Search for 3Com 509 card 96 ***********************************************************/ 97 /* 98 ep_current_tag--; 99 */ 100 101 /* Look for the ISA boards. Init and leave them actived */ 102 /* search for the first card, ignore all others */ 103 outb(id_port, 0xc0); /* Global reset */ 104 delay(1000); 105 /* 106 for (i = 0; i < EP_MAX_BOARDS; i++) { 107 */ 108 outb(id_port, 0); 109 outb(id_port, 0); 110 send_ID_sequence(id_port); 111 112 data = get_eeprom_data(id_port, EEPROM_MFG_ID); 113 if (data != MFG_ID) 114 return 0; 115 116 /* resolve contention using the Ethernet address */ 117 for (j = 0; j < 3; j++) 118 data = get_eeprom_data(id_port, j); 119 120 eth_base = 121 (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200; 122 outb(id_port, EP_LAST_TAG); /* tags board */ 123 outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); 124 /* 125 ep_current_tag--; 126 break; 127 } 128 129 if(i==EP_MAX_BOARDS)return 0; 130 */ 131 132 /* 133 * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be 134 * 0x9[0-f]50 135 */ 136 GO_WINDOW(0); 137 k = ep_get_e(EEPROM_PROD_ID); 138 if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) 139 return 0; 140 141 printf("3C5x9 board on ISA at 0x%x - ", eth_base); 142 143 /* test for presence of connectors */ 144 i = inw(IS_BASE + EP_W0_CONFIG_CTRL); 145 j = inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14; 146 147 for(ether_medium = 0, m = mediatab; 148 ether_medium < sizeof(mediatab) / sizeof(mediatab[0]); 149 ether_medium++, m++) { 150 if(j == m->address_cfg) { 151 if(!(i & m->config_bit)) { 152 printf("%s not present\n", m->name); 153 return(0); 154 } 155 printf("using %s\n", m->name); 156 goto ok; 157 } 158 } 159 printf("unknown connector\n"); 160 return(0); 161 162 ok: 163 /* 164 * Read the station address from the eeprom 165 */ 166 p = (u_short *) eth_myaddr; 167 for (i = 0; i < 3; i++) { 168 u_short help; 169 GO_WINDOW(0); 170 help=ep_get_e(i); 171 p[i] = ((help & 0xff) << 8) | ((help & 0xff00) >> 8); 172 GO_WINDOW(2); 173 outw(BASE + EP_W2_ADDR_0 + (i * 2), help); 174 } 175 for(i = 0; i < 6; i++) 176 myadr[i] = eth_myaddr[i]; 177 178 epreset(); 179 180 #ifdef _STANDALONE 181 strncpy(bi_netif.ifname, "ep", sizeof(bi_netif.ifname)); 182 bi_netif.bus = BI_BUS_ISA; 183 bi_netif.addr.iobase = eth_base; 184 185 BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); 186 #endif 187 188 return(1); 189 } 190 191 static int 192 send_ID_sequence(port) 193 int port; 194 { 195 int cx, al; 196 197 for (al = 0xff, cx = 0; cx < 255; cx++) { 198 outb(port, al); 199 al <<= 1; 200 if (al & 0x100) 201 al ^= 0xcf; 202 } 203 return (1); 204 } 205 206 /* 207 * We get eeprom data from the id_port given an offset into the eeprom. 208 * Basically; after the ID_sequence is sent to all of the cards; they enter 209 * the ID_CMD state where they will accept command requests. 0x80-0xbf loads 210 * the eeprom data. We then read the port 16 times and with every read; the 211 * cards check for contention (ie: if one card writes a 0 bit and another 212 * writes a 1 bit then the host sees a 0. At the end of the cycle; each card 213 * compares the data on the bus; if there is a difference then that card goes 214 * into ID_WAIT state again). In the meantime; one bit of data is returned in 215 * the AX register which is conveniently returned to us by inb(). Hence; we 216 * read 16 times getting one bit of data with each read. 217 */ 218 static int 219 get_eeprom_data(id_port, offset) 220 int id_port; 221 int offset; 222 { 223 int i, data = 0; 224 outb(id_port, 0x80 + offset); 225 delay(1000); 226 for (i = 0; i < 16; i++) 227 data = (data << 1) | (inw(id_port) & 1); 228 return (data); 229 } 230