1 /* $NetBSD: 3c509.c,v 1.10 2008/12/14 18:46:33 christos 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 unsigned ether_medium;
44 unsigned short eth_base;
45
46 extern void epreset(void);
47 extern int ep_get_e(int);
48
49 static int send_ID_sequence(int);
50 static int get_eeprom_data(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(void);
70 #endif
71
72 /**************************************************************************
73 ETH_PROBE - Look for an adapter
74 ***************************************************************************/
75 int
EtherInit(unsigned char * myadr)76 EtherInit(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 u_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)
130 return 0;
131 */
132
133 /*
134 * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
135 * 0x9[0-f]50
136 */
137 GO_WINDOW(0);
138 k = (u_int)ep_get_e(EEPROM_PROD_ID);
139 if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
140 return 0;
141
142 printf("3C5x9 board on ISA at 0x%x - ", eth_base);
143
144 /* test for presence of connectors */
145 i = inw(IS_BASE + EP_W0_CONFIG_CTRL);
146 j = inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14;
147
148 for (ether_medium = 0, m = mediatab;
149 ether_medium < sizeof(mediatab) / sizeof(mediatab[0]);
150 ether_medium++, m++) {
151 if (j == m->address_cfg) {
152 if (!(i & m->config_bit)) {
153 printf("%s not present\n", m->name);
154 return 0;
155 }
156 printf("using %s\n", m->name);
157 goto ok;
158 }
159 }
160 printf("unknown connector\n");
161 return 0;
162
163 ok:
164 /*
165 * Read the station address from the eeprom
166 */
167 p = (u_short *) eth_myaddr;
168 for (i = 0; i < 3; i++) {
169 u_short help;
170 GO_WINDOW(0);
171 help = ep_get_e(i);
172 p[i] = ((help & 0xff) << 8) | ((help & 0xff00) >> 8);
173 GO_WINDOW(2);
174 outw(BASE + EP_W2_ADDR_0 + (i * 2), help);
175 }
176 for (i = 0; i < 6; i++)
177 myadr[i] = eth_myaddr[i];
178
179 epreset();
180
181 #ifdef _STANDALONE
182 strncpy(bi_netif.ifname, "ep", sizeof(bi_netif.ifname));
183 bi_netif.bus = BI_BUS_ISA;
184 bi_netif.addr.iobase = eth_base;
185
186 BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif));
187 #endif
188
189 return 1;
190 }
191
192 static int
send_ID_sequence(int port)193 send_ID_sequence(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
get_eeprom_data(int id_port,int offset)219 get_eeprom_data(int id_port, int offset)
220 {
221 int i, data = 0;
222 outb(id_port, 0x80 + offset);
223 delay(1000);
224 for (i = 0; i < 16; i++)
225 data = (data << 1) | (inw(id_port) & 1);
226 return data;
227 }
228