1/*
2* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
3*
4* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
5* All rights reserved.
6* Mar. 14, 2000
7*
8*  This software may be used, modified, copied, distributed, and sold, in
9*  both source and binary form provided that the above copyright and these
10*  terms are retained. Under no circumstances are the authors responsible for
11*  the proper functioning of this software, nor do the authors assume any
12*  responsibility for damages incurred with its use.
13*
14* This code is based on Martin Renters' etherboot-4.4.3 3c509.c and
15* Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
16*
17*  Copyright (C) 1993-1994, David Greenman, Martin Renters.
18*  Copyright (C) 1993-1995, Andres Vega Garcia.
19*  Copyright (C) 1995, Serge Babkin.
20*
21*  Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
22*
23*/
24
25/* #define EDEBUG */
26
27#include "etherboot.h"
28#include "nic.h"
29#include "pci.h"
30#include "3c595.h"
31#include "timer.h"
32
33static unsigned short	eth_nic_base, eth_asic_base;
34static unsigned short	vx_connector, vx_connectors;
35
36static struct connector_entry {
37  int bit;
38  char *name;
39} conn_tab[VX_CONNECTORS] = {
40#define CONNECTOR_UTP   0
41  { 0x08, "utp"},
42#define CONNECTOR_AUI   1
43  { 0x20, "aui"},
44/* dummy */
45  { 0, "???"},
46#define CONNECTOR_BNC   3
47  { 0x10, "bnc"},
48#define CONNECTOR_TX    4
49  { 0x02, "tx"},
50#define CONNECTOR_FX    5
51  { 0x04, "fx"},
52#define CONNECTOR_MII   6
53  { 0x40, "mii"},
54  { 0, "???"}
55};
56
57static void vxgetlink(void);
58static void vxsetlink(void);
59
60#define	udelay(n)	waiton_timer2(((n)*TICKS_PER_MS)/1000)
61
62/**************************************************************************
63ETH_RESET - Reset adapter
64***************************************************************************/
65static void t595_reset(struct nic *nic)
66{
67	int i, j;
68
69	/***********************************************************
70			Reset 3Com 595 card
71	*************************************************************/
72
73	/* stop card */
74	outw(RX_DISABLE, BASE + VX_COMMAND);
75	outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
76	VX_BUSY_WAIT;
77	outw(TX_DISABLE, BASE + VX_COMMAND);
78	outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
79	udelay(8000);
80	outw(RX_RESET, BASE + VX_COMMAND);
81	VX_BUSY_WAIT;
82	outw(TX_RESET, BASE + VX_COMMAND);
83	VX_BUSY_WAIT;
84	outw(C_INTR_LATCH, BASE + VX_COMMAND);
85	outw(SET_RD_0_MASK, BASE + VX_COMMAND);
86	outw(SET_INTR_MASK, BASE + VX_COMMAND);
87	outw(SET_RX_FILTER, BASE + VX_COMMAND);
88
89	/*
90	* initialize card
91	*/
92	VX_BUSY_WAIT;
93
94	GO_WINDOW(0);
95
96	/* Disable the card */
97/*	outw(0, BASE + VX_W0_CONFIG_CTRL); */
98
99	/* Configure IRQ to none */
100/*	outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
101
102	/* Enable the card */
103/*	outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
104
105	GO_WINDOW(2);
106
107	/* Reload the ether_addr. */
108	for (i = 0; i < ETH_ALEN; i++)
109		outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
110
111	outw(RX_RESET, BASE + VX_COMMAND);
112	VX_BUSY_WAIT;
113	outw(TX_RESET, BASE + VX_COMMAND);
114	VX_BUSY_WAIT;
115
116	/* Window 1 is operating window */
117	GO_WINDOW(1);
118	for (i = 0; i < 31; i++)
119		inb(BASE + VX_W1_TX_STATUS);
120
121	outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
122		S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
123	outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
124		S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
125
126/*
127 * Attempt to get rid of any stray interrupts that occured during
128 * configuration.  On the i386 this isn't possible because one may
129 * already be queued.  However, a single stray interrupt is
130 * unimportant.
131 */
132
133	outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
134
135	outw(SET_RX_FILTER | FIL_INDIVIDUAL |
136	    FIL_BRDCST, BASE + VX_COMMAND);
137
138	vxsetlink();
139/*{
140	int i,j;
141	i = CONNECTOR_TX;
142	GO_WINDOW(3);
143	j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
144	outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
145        GO_WINDOW(4);
146        outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
147        GO_WINDOW(1);
148}*/
149
150	/* start tranciever and receiver */
151	outw(RX_ENABLE, BASE + VX_COMMAND);
152	outw(TX_ENABLE, BASE + VX_COMMAND);
153
154}
155
156/**************************************************************************
157ETH_TRANSMIT - Transmit a frame
158***************************************************************************/
159static char padmap[] = {
160	0, 3, 2, 1};
161
162static void t595_transmit(
163struct nic *nic,
164const char *d,			/* Destination */
165unsigned int t,			/* Type */
166unsigned int s,			/* size */
167const char *p)			/* Packet */
168{
169	register int len;
170	int pad;
171	int status;
172
173#ifdef EDEBUG
174	printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
175#endif
176
177	/* swap bytes of type */
178	t= htons(t);
179
180	len=s+ETH_HLEN; /* actual length of packet */
181	pad = padmap[len & 3];
182
183	/*
184	* The 3c595 automatically pads short packets to minimum ethernet length,
185	* but we drop packets that are too large. Perhaps we should truncate
186	* them instead?
187	*/
188	if (len + pad > ETH_FRAME_LEN) {
189		return;
190	}
191
192	/* drop acknowledgements */
193	while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
194		if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
195			outw(TX_RESET, BASE + VX_COMMAND);
196			outw(TX_ENABLE, BASE + VX_COMMAND);
197		}
198
199		outb(0x0, BASE + VX_W1_TX_STATUS);
200	}
201
202	while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
203		/* no room in FIFO */
204	}
205
206	outw(len, BASE + VX_W1_TX_PIO_WR_1);
207	outw(0x0, BASE + VX_W1_TX_PIO_WR_1);	/* Second dword meaningless */
208
209	/* write packet */
210	outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
211	outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
212	outw(t, BASE + VX_W1_TX_PIO_WR_1);
213	outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
214	if (s & 1)
215		outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
216
217	while (pad--)
218		outb(0, BASE + VX_W1_TX_PIO_WR_1);	/* Padding */
219
220        /* wait for Tx complete */
221        while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
222                ;
223}
224
225/**************************************************************************
226ETH_POLL - Wait for a frame
227***************************************************************************/
228static int t595_poll(struct nic *nic)
229{
230	/* common variables */
231	unsigned short type = 0;	/* used by EDEBUG */
232	/* variables for 3C595 */
233	short status, cst;
234	register short rx_fifo;
235
236	cst=inw(BASE + VX_STATUS);
237
238#ifdef EDEBUG
239	if(cst & 0x1FFF)
240		printf("-%hX-",cst);
241#endif
242
243	if( (cst & S_RX_COMPLETE)==0 ) {
244		/* acknowledge  everything */
245		outw(ACK_INTR | cst, BASE + VX_COMMAND);
246		outw(C_INTR_LATCH, BASE + VX_COMMAND);
247
248		return 0;
249	}
250
251	status = inw(BASE + VX_W1_RX_STATUS);
252#ifdef EDEBUG
253	printf("*%hX*",status);
254#endif
255
256	if (status & ERR_RX) {
257		outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
258		return 0;
259	}
260
261	rx_fifo = status & RX_BYTES_MASK;
262	if (rx_fifo==0)
263		return 0;
264
265		/* read packet */
266#ifdef EDEBUG
267	printf("[l=%d",rx_fifo);
268#endif
269	insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
270	if(rx_fifo & 1)
271		nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
272	nic->packetlen=rx_fifo;
273
274	while(1) {
275		status = inw(BASE + VX_W1_RX_STATUS);
276#ifdef EDEBUG
277		printf("*%hX*",status);
278#endif
279		rx_fifo = status & RX_BYTES_MASK;
280
281		if(rx_fifo>0) {
282			insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
283			if(rx_fifo & 1)
284				nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
285			nic->packetlen+=rx_fifo;
286#ifdef EDEBUG
287			printf("+%d",rx_fifo);
288#endif
289		}
290		if(( status & RX_INCOMPLETE )==0) {
291#ifdef EDEBUG
292			printf("=%d",nic->packetlen);
293#endif
294			break;
295		}
296		udelay(1000);
297	}
298
299	/* acknowledge reception of packet */
300	outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
301	while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
302#ifdef EDEBUG
303	type = (nic->packet[12]<<8) | nic->packet[13];
304	if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
305	    nic->packet[5] == 0xFF*ETH_ALEN)
306		printf(",t=%hX,b]",type);
307	else
308		printf(",t=%hX]",type);
309#endif
310	return 1;
311}
312
313
314/*************************************************************************
315	3Com 595 - specific routines
316**************************************************************************/
317
318static int
319eeprom_rdy()
320{
321	int i;
322
323	for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
324		udelay(1000);
325	if (i >= MAX_EEPROMBUSY) {
326	        /* printf("3c595: eeprom failed to come ready.\n"); */
327		printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
328		return (0);
329	}
330	return (1);
331}
332
333/*
334 * get_e: gets a 16 bits word from the EEPROM. we must have set the window
335 * before
336 */
337static int
338get_e(offset)
339int offset;
340{
341	if (!eeprom_rdy())
342		return (0xffff);
343	outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
344	if (!eeprom_rdy())
345		return (0xffff);
346	return (inw(BASE + VX_W0_EEPROM_DATA));
347}
348
349static void
350vxgetlink(void)
351{
352    int n, k;
353
354    GO_WINDOW(3);
355    vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
356    for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
357      if (vx_connectors & conn_tab[k].bit) {
358        if (n > 0) {
359          printf("/");
360	}
361        printf(conn_tab[k].name);
362        n++;
363      }
364    }
365    if (vx_connectors == 0) {
366        printf("no connectors!");
367        return;
368    }
369    GO_WINDOW(3);
370    vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
371                        & INTERNAL_CONNECTOR_MASK)
372                        >> INTERNAL_CONNECTOR_BITS;
373    if (vx_connector & 0x10) {
374        vx_connector &= 0x0f;
375        printf("[*%s*]", conn_tab[vx_connector].name);
376        printf(": disable 'auto select' with DOS util!");
377    } else {
378        printf("[*%s*]", conn_tab[vx_connector].name);
379    }
380}
381
382static void
383vxsetlink(void)
384{
385    int i, j, k;
386    char *reason, *warning;
387    static short prev_flags;
388    static char prev_conn = -1;
389
390    if (prev_conn == -1) {
391        prev_conn = vx_connector;
392    }
393
394    i = vx_connector;       /* default in EEPROM */
395    reason = "default";
396    warning = 0;
397
398    if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
399        warning = "strange connector type in EEPROM.";
400        reason = "forced";
401        i = CONNECTOR_UTP;
402    }
403
404        if (warning != 0) {
405            printf("warning: %s\n", warning);
406        }
407        printf("selected %s. (%s)\n", conn_tab[i].name, reason);
408
409    /* Set the selected connector. */
410    GO_WINDOW(3);
411    j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
412    outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
413
414    /* First, disable all. */
415    outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
416    udelay(8000);
417    GO_WINDOW(4);
418    outw(0, BASE + VX_W4_MEDIA_TYPE);
419
420    /* Second, enable the selected one. */
421    switch(i) {
422      case CONNECTOR_UTP:
423        GO_WINDOW(4);
424        outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
425        break;
426      case CONNECTOR_BNC:
427        outw(START_TRANSCEIVER,BASE + VX_COMMAND);
428        udelay(8000);
429        break;
430      case CONNECTOR_TX:
431      case CONNECTOR_FX:
432        GO_WINDOW(4);
433        outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
434        break;
435      default:  /* AUI and MII fall here */
436        break;
437    }
438    GO_WINDOW(1);
439}
440
441static void t595_disable(struct nic *nic)
442{
443    outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
444    udelay(8000);
445    GO_WINDOW(4);
446    outw(0, BASE + VX_W4_MEDIA_TYPE);
447    GO_WINDOW(1);
448}
449
450/**************************************************************************
451ETH_PROBE - Look for an adapter
452***************************************************************************/
453struct nic *t595_probe(struct nic *nic, unsigned short *probeaddrs, struct pci_device *pci)
454{
455	int i;
456	unsigned short *p;
457
458	if (probeaddrs == 0 || probeaddrs[0] == 0)
459		return 0;
460/*	eth_nic_base = probeaddrs[0] & ~3; */
461	eth_nic_base = pci->ioaddr;
462
463	GO_WINDOW(0);
464	outw(GLOBAL_RESET, BASE + VX_COMMAND);
465	VX_BUSY_WAIT;
466
467	vxgetlink();
468
469/*
470	printf("\nEEPROM:");
471	for (i = 0; i < (EEPROMSIZE/2); i++) {
472	  printf("%hX:", get_e(i));
473	}
474	printf("\n");
475*/
476	/*
477	* Read the station address from the eeprom
478	*/
479	p = (unsigned short *) nic->node_addr;
480	for (i = 0; i < 3; i++) {
481		GO_WINDOW(0);
482		p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
483		GO_WINDOW(2);
484		outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
485	}
486
487	printf("Ethernet address: %!\n", nic->node_addr);
488
489	t595_reset(nic);
490	nic->reset = t595_reset;
491	nic->poll = t595_poll;
492	nic->transmit = t595_transmit;
493	nic->disable = t595_disable;
494	return nic;
495
496}
497
498/*
499 * Local variables:
500 *  c-basic-offset: 8
501 * End:
502 */
503
504