~kris/9p

9hist

ref: a5f00a0f4a4a30691580403dd5046a18c685ee92 9hist/pc/ether503.c -rw-r--r-- 8.6 KiB
a5f00a0f — David du Colombier Plan 9 from Bell Labs 1993-07-29 33 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"

#include "ether.h"

/*
 * 3Com 503 (EtherLink II).
 * This driver doesn't work under load, the card gets blasted by
 * overwrite-warning interrupts and eventually just hangs. Always clearing
 * the Ovw bit in the DP8390 Isr along with the Rxe and Prx bits stops
 * the overwrite-warning interrupts, but the card eventually hangs anyway.
 * I really don't understand why this card doesn't work,
 * there is plenty of evidence that other systems manage to use it.
 * The only good thing about this driver is that both shared
 * memory and polled I/O work (given the above restraints), although
 * the polled I/O should be tuned.
 */
enum {					/* Gate Array Registers */
	Pstr		= 0x400,	/* Page Start Register */
	Pspr		= 0x401,	/* Page Stop Register */
	Dqtr		= 0x402,	/* Drq Timer Register */
	Bcfr		= 0x403,	/* Base Configuration Register */
	Pcfr		= 0x404,	/* EPROM Configuration Register */
	Gacfr		= 0x405,	/* GA Configuration Register */
	Ctrl		= 0x406,	/* Control Register */
	Streg		= 0x407,	/* Status Register */
	Idcfr		= 0x408,	/* Interrupt/DMA Configuration Register */
	Damsb		= 0x409,	/* DMA Address Register MSB */
	Dalsb		= 0x40A,	/* DMA Address Register LSB */
	Vptr2		= 0x40B,	/* Vector Pointer Register 2 */
	Vptr1		= 0x40C,	/* Vector Pointer Register 1 */
	Vptr0		= 0x40D,	/* Vector Pointer Register 0 */
	Rfmsb		= 0x40E,	/* Register File Access MSB */
	Rflsb		= 0x40F,	/* Register File Access LSB */
};

enum {					/* Pcfr */
	C8xxx		= 0x10,		/* memory address 0xC8000 */
	CCxxx		= 0x20,		/* memory address 0xCC000 */
	D8xxx		= 0x40,		/* memory address 0xD8000 */
	DCxxx		= 0x80,		/* memory address 0xDC000 */
};

enum {					/* Gacfr */
	Mbs0		= 0x01,		/* memory bank select 0 */
	Mbs1		= 0x02,		/* memory bank select 1 */
	Mbs2		= 0x04,		/* memory bank select 2 */
	Rsel		= 0x08,		/* RAM select */
	Test		= 0x10,		/* test */
	Ows		= 0x20,		/* 0 wait state */
	Tcm		= 0x40,		/* terminal count mask */
	Nim		= 0x80,		/* MIC interrupt mask */
};

enum {					/* Ctrl */
	Rst		= 0x01,		/* software reset */
	Xsel		= 0x02,		/* transceiver select (BNC = 1, AUI = 0) */
	Ealo		= 0x04,		/* ethernet address low */
	Eahi		= 0x08,		/* ethernet address high */
	Share		= 0x10,		/* interrupt share */
	Dbsel		= 0x20,		/* double buffer select */
	Ddir		= 0x40,		/* DMA direction */
	Start		= 0x80,		/* DMA start */
};

enum {					/* Streg */
	Rev0		= 0x01,		/* gate array revision bit 0 */
	Rev1		= 0x02,		/* gate array revision bit 1 */
	Rev2		= 0x04,		/* gate array revision bit 2 */
	Dip		= 0x08,		/* DMA in progress */
	Dtc		= 0x10,		/* DMA terminal count */
	Oflw		= 0x20,		/* overflow */
	Uflw		= 0x40,		/* underflow */
	Dprdy		= 0x80,		/* data port ready */
};

enum {					/* Idcfr */
	Drq1		= 0x01,		/* DMA request 1 */
	Drq2		= 0x02,		/* DMA request 2 */
	Drq3		= 0x04,		/* DMA request 3 */
	Irq2		= 0x10,		/* interrupt request 2 */
	Irq3		= 0x20,		/* interrupt request 3 */
	Irq4		= 0x40,		/* interrupt request 4 */
	Irq5		= 0x80,		/* interrupt request 5 */
};

enum {
	RamOffset	= 0x2000,	/* where the card sees the shared memory */
};

typedef struct {
	ulong	io;			/* I/O base address */
	uchar	irq;			/* interrupt level */
	uchar	bnc;			/* use BNC connector */
} Table;

/*
 * The only configuration info we can get from the card is the
 * I/O base address and the shared-memory address. So let's make
 * a little hard-wired configuration table.
 */
static Table table[] = {
	{ 0x300, Irq2, 0, },		/* external transceiver */
	{ 0x310, Irq3, 0, },
	{ 0x330, Irq4, 0, },
	{ 0x350, Irq5, 0, },

	{ 0x250, Irq2, Xsel, },		/* BNC */
	{ 0x280, Irq3, Xsel, },
	{ 0x2A0, Irq4, Xsel, },
	{ 0x2E0, Irq5, Xsel, },

	{ 0 },
};


static int
reset(Ctlr *ctlr)
{
	Table *tp;
	int i;
	uchar cfr;

	/*
	 * The Gate Array Ctrl register bits <3,2> determine what is
	 * mapped at the I/O base address. The options are the DP8390,
	 * PROM bytes 0-15 or PROM bytes 16-31.
	 * At power-on, the I/O base address contains PROM bytes 16-31.
	 */
	for(tp = table; ctlr->card.io = tp->io; tp++){
		/*
		 * Toggle the software reset bit, this should initialise the
		 * Ctrl register to 0x0A (Eahi|Xsel).
		 * Hopefully there is no other card at the gate-array address.
		 */
		outb(ctlr->card.io+Ctrl, Rst);
		delay(1);
		outb(ctlr->card.io+Ctrl, 0);
		if(inb(ctlr->card.io+Ctrl) == (Eahi|Xsel))
			break;
	}
	if(ctlr->card.io == 0)
		return -1;

	/*
	 * Map PROM bytes 0-15 to the I/O base address
	 * and read the ethernet address.
	 * When done, map the DP8390 at the I/O base address
	 * and set the transceiver type and irq.
	 */
	outb(ctlr->card.io+Ctrl, Ealo|Xsel);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		ctlr->ea[i] = inb(ctlr->card.io+i);

	outb(ctlr->card.io+Gacfr, Nim|Tcm);

	outb(ctlr->card.io+Ctrl, 0|tp->bnc);
	outb(ctlr->card.io+Idcfr, tp->irq);

	if(tp->irq == Irq2)
		ctlr->card.irq = 2;
	else if(tp->irq == Irq3)
		ctlr->card.irq = 3;
	else if(tp->irq == Irq4)
		ctlr->card.irq = 4;
	else if(tp->irq == Irq5)
		ctlr->card.irq = 5;
	else
		return -1;

	/*
	 * Try to configure the shared memory.
	 * Assume this is an 8K card.
	 */
	ctlr->card.ram = 1;
	cfr = inb(ctlr->card.io+Pcfr);
	if(cfr & C8xxx)
		ctlr->card.ramstart = KZERO|0xC8000;
	else if(cfr & CCxxx)
		ctlr->card.ramstart = KZERO|0xCC000;
	else if(cfr & D8xxx)
		ctlr->card.ramstart = KZERO|0xD8000;
	else if(cfr & DCxxx)
		ctlr->card.ramstart = KZERO|0xDC000;
	else
		ctlr->card.ram = 0;

	ctlr->card.ramstop = ctlr->card.ramstart + 8*1024;

	/*
	 * Finish initialising the gate-array.
	 * The Pstr and Pspr registers must be the same as those
	 * given to the DP8390.
	 * Gacfr is set to enable shared memory and block DMA
	 * interrupts.
	 */
	outb(ctlr->card.io+Pstr, ctlr->card.pstart);
	outb(ctlr->card.io+Pspr, ctlr->card.pstop);

	outb(ctlr->card.io+Dqtr, 8);
	outb(ctlr->card.io+Damsb, ctlr->card.tstart);
	outb(ctlr->card.io+Dalsb, 0);

	outb(ctlr->card.io+Vptr2, 0xFF);
	outb(ctlr->card.io+Vptr1, 0xFF);
	outb(ctlr->card.io+Vptr0, 0x00);

	/*
	 * Finally, init the 8390, set the ethernet address
	 * and turn on the shared memory if any.
	 */
	ctlr->card.dp8390 = ctlr->card.io;
	dp8390reset(ctlr);
	dp8390setea(ctlr);

	if(ctlr->card.ram)
		outb(ctlr->card.io+Gacfr, Tcm|Rsel|Mbs0);
	else
		outb(ctlr->card.io+Gacfr, Tcm);

	return 0;
}

static void*
read(Ctlr *ctlr, void *to, ulong from, ulong len)
{
	uchar *addr, ctrl;

	/*
	 * If the interface has shared memory, just do a memmove.
	 * In this case, 'from' is an index into the shared memory.
	 * Due to 'hardware considerations', the memory is seen by
	 * the adapter at RamOffset, so we have to adjust.
	 */
	if(ctlr->card.ram)
		return memmove(to, (void*)(ctlr->card.ramstart+from-RamOffset), len);

	/*
	 * Polled I/O. This should be tuned.
	 * Set up the dma registers and pump the fifo.
	 */
	outb(ctlr->card.io+Dalsb, from & 0xFF);
	outb(ctlr->card.io+Damsb, (from>>8) & 0xFF);
	ctrl = inb(ctlr->card.io+Ctrl);
	outb(ctlr->card.io+Ctrl, Start|ctrl);
	addr = to;
	while(len){
		/*
		 * The fifo is 8 bytes deep, we have to wait
		 * until it's ready before stuffing it.
		 * The fifo is in its default configuration,
		 * so we can do word accesses.
		 */
		while((inb(ctlr->card.io+Streg) & Dprdy) == 0)
			;
		if(len >= 8){
			inss(ctlr->card.io+Rfmsb, addr, 4);
			addr += 8;
			len -= 8;
		}
		else{
			insb(ctlr->card.io+Rfmsb, addr, len);
			break;
		}
	}
	outb(ctlr->card.io+Ctrl, ctrl);
	return to;
}

static void*
write(Ctlr *ctlr, ulong to, void *from, ulong len)
{
	uchar *addr, ctrl;

	/*
	 * See the comments in 'read()' above.
	 */
	if(ctlr->card.ram)
		return memmove((void*)(ctlr->card.ramstart+to-RamOffset), from, len);

	outb(ctlr->card.io+Dalsb, to & 0xFF);
	outb(ctlr->card.io+Damsb, (to>>8) & 0xFF);
	ctrl = inb(ctlr->card.io+Ctrl);
	outb(ctlr->card.io+Ctrl, Start|Ddir|ctrl);
	addr = from;
	while(len){
		while((inb(ctlr->card.io+Streg) & Dprdy) == 0)
			;
		if(len >= 8){
			outss(ctlr->card.io+Rfmsb, addr, 4);
			addr += 8;
			len -= 8;
		}
		else{
			outsb(ctlr->card.io+Rfmsb, addr, len);
			break;
		}
	}
	outb(ctlr->card.io+Ctrl, ctrl);
	return (void*)to;
}

Card ether503 = {
	"3Com503",			/* ident */

	reset,				/* reset */
	0,				/* init */
	dp8390attach,			/* attach */
	dp8390mode,			/* mode */

	read,				/* read */
	write,				/* write */

	dp8390receive,			/* receive */
	dp8390transmit,			/* transmit */
	dp8390intr,			/* interrupt */
	dp8390watch,			/* watch */
	dp8390overflow,			/* overflow */

	0,				/* io */
	0,				/* irq */
	0,				/* bit16 */

	0,				/* ram */
	0,				/* ramstart */
	0,				/* ramstop */

	0,				/* dp8390 */
	0,				/* data */
	0,				/* software bndry */
	RamOffset/Dp8390BufSz,		/* tstart */
	(RamOffset+0x600)/Dp8390BufSz,	/* pstart */
	(RamOffset+8*1024)/Dp8390BufSz,	/* pstop */
};