1/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
2/*
3  Permission is granted to distribute the enclosed cs89x0.[ch] driver
4  only in conjunction with the Etherboot package.  The code is
5  ordinarily distributed under the GPL.
6
7  Russ Nelson, January 2000
8
9  ChangeLog:
10
11  Thu Dec 6 22:40:00 1996  Markus Gutschke  <gutschk@math.uni-muenster.de>
12
13  * disabled all "advanced" features; this should make the code more reliable
14
15  * reorganized the reset function
16
17  * always reset the address port, so that autoprobing will continue working
18
19  * some cosmetic changes
20
21  * 2.5
22
23  Thu Dec 5 21:00:00 1996  Markus Gutschke  <gutschk@math.uni-muenster.de>
24
25  * tested the code against a CS8900 card
26
27  * lots of minor bug fixes and adjustments
28
29  * this is the first release, that actually works! it still requires some
30    changes in order to be more tolerant to different environments
31
32  * 4
33
34  Fri Nov 22 23:00:00 1996  Markus Gutschke  <gutschk@math.uni-muenster.de>
35
36  * read the manuals for the CS89x0 chipsets and took note of all the
37    changes that will be neccessary in order to adapt Russel Nelson's code
38    to the requirements of a BOOT-Prom
39
40  * 6
41
42  Thu Nov 19 22:00:00 1996  Markus Gutschke  <gutschk@math.uni-muenster.de>
43
44  * Synched with Russel Nelson's current code (v1.00)
45
46  * 2
47
48  Thu Nov 12 18:00:00 1996  Markus Gutschke  <gutschk@math.uni-muenster.de>
49
50  * Cleaned up some of the code and tried to optimize the code size.
51
52  * 1.5
53
54  Sun Nov 10 16:30:00 1996  Markus Gutschke  <gutschk@math.uni-muenster.de>
55
56  * First experimental release. This code compiles fine, but I
57  have no way of testing whether it actually works.
58
59  * I did not (yet) bother to make the code 16bit aware, so for
60  the time being, it will only work for Etherboot/32.
61
62  * 12
63
64  */
65
66#include "etherboot.h"
67#include "nic.h"
68#include "cards.h"
69#include "cs89x0.h"
70
71static unsigned short	eth_nic_base;
72static unsigned long    eth_mem_start;
73static unsigned short   eth_irq;
74static unsigned short   eth_cs_type;	/* one of: CS8900, CS8920, CS8920M  */
75static unsigned short   eth_auto_neg_cnf;
76static unsigned short   eth_adapter_cnf;
77static unsigned short	eth_linectl;
78
79/*************************************************************************
80	CS89x0 - specific routines
81**************************************************************************/
82
83static inline int readreg(int portno)
84{
85	outw(portno, eth_nic_base + ADD_PORT);
86	return inw(eth_nic_base + DATA_PORT);
87}
88
89static inline void writereg(int portno, int value)
90{
91	outw(portno, eth_nic_base + ADD_PORT);
92	outw(value, eth_nic_base + DATA_PORT);
93	return;
94}
95
96/*************************************************************************
97EEPROM access
98**************************************************************************/
99
100static int wait_eeprom_ready(void)
101{
102	unsigned long tmo = currticks() + 4*TICKS_PER_SEC;
103
104	/* check to see if the EEPROM is ready, a timeout is used -
105	   just in case EEPROM is ready when SI_BUSY in the
106	   PP_SelfST is clear */
107	while(readreg(PP_SelfST) & SI_BUSY) {
108		if (currticks() >= tmo)
109			return -1; }
110	return 0;
111}
112
113static int get_eeprom_data(int off, int len, unsigned short *buffer)
114{
115	int i;
116
117#ifdef	EDEBUG
118	printf("\ncs: EEPROM data from %hX for %hX:",off,len);
119#endif
120	for (i = 0; i < len; i++) {
121		if (wait_eeprom_ready() < 0)
122			return -1;
123		/* Now send the EEPROM read command and EEPROM location
124		   to read */
125		writereg(PP_EECMD, (off + i) | EEPROM_READ_CMD);
126		if (wait_eeprom_ready() < 0)
127			return -1;
128		buffer[i] = readreg(PP_EEData);
129#ifdef	EDEBUG
130		if (!(i%10))
131			printf("\ncs: ");
132		printf("%hX ", buffer[i]);
133#endif
134	}
135#ifdef	EDEBUG
136	putchar('\n');
137#endif
138
139	return(0);
140}
141
142static int get_eeprom_chksum(int off, int len, unsigned short *buffer)
143{
144	int  i, cksum;
145
146	cksum = 0;
147	for (i = 0; i < len; i++)
148		cksum += buffer[i];
149	cksum &= 0xffff;
150	if (cksum == 0)
151		return 0;
152	return -1;
153}
154
155/*************************************************************************
156Activate all of the available media and probe for network
157**************************************************************************/
158
159static void clrline(void)
160{
161	int i;
162
163	putchar('\r');
164	for (i = 79; i--; ) putchar(' ');
165	printf("\rcs: ");
166	return;
167}
168
169static void control_dc_dc(int on_not_off)
170{
171	unsigned int selfcontrol;
172	unsigned long tmo = currticks() + TICKS_PER_SEC;
173
174	/* control the DC to DC convertor in the SelfControl register.  */
175	selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
176	if (((eth_adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
177		selfcontrol |= HCB1;
178	else
179		selfcontrol &= ~HCB1;
180	writereg(PP_SelfCTL, selfcontrol);
181
182	/* Wait for the DC/DC converter to power up - 1000ms */
183	while (currticks() < tmo);
184
185	return;
186}
187
188static int detect_tp(void)
189{
190	unsigned long tmo;
191
192	/* Turn on the chip auto detection of 10BT/ AUI */
193
194	clrline(); printf("attempting %s:","TP");
195
196        /* If connected to another full duplex capable 10-Base-T card
197	   the link pulses seem to be lost when the auto detect bit in
198	   the LineCTL is set.  To overcome this the auto detect bit
199	   will be cleared whilst testing the 10-Base-T interface.
200	   This would not be necessary for the sparrow chip but is
201	   simpler to do it anyway. */
202	writereg(PP_LineCTL, eth_linectl &~ AUI_ONLY);
203	control_dc_dc(0);
204
205        /* Delay for the hardware to work out if the TP cable is
206	   present - 150ms */
207	for (tmo = currticks() + 4; currticks() < tmo; );
208
209	if ((readreg(PP_LineST) & LINK_OK) == 0)
210		return 0;
211
212	if (eth_cs_type != CS8900) {
213
214		writereg(PP_AutoNegCTL, eth_auto_neg_cnf & AUTO_NEG_MASK);
215
216		if ((eth_auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
217			printf(" negotiating duplex... ");
218			while (readreg(PP_AutoNegST) & AUTO_NEG_BUSY) {
219				if (currticks() - tmo > 40*TICKS_PER_SEC) {
220					printf("time out ");
221					break;
222				}
223			}
224		}
225		if (readreg(PP_AutoNegST) & FDX_ACTIVE)
226			printf("using full duplex");
227		else
228			printf("using half duplex");
229	}
230
231	return A_CNF_MEDIA_10B_T;
232}
233
234/* send a test packet - return true if carrier bits are ok */
235static int send_test_pkt(struct nic *nic)
236{
237	static unsigned char testpacket[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
238				     0, 46, /*A 46 in network order       */
239				     0, 0,  /*DSAP=0 & SSAP=0 fields      */
240				     0xf3,0 /*Control (Test Req+P bit set)*/ };
241	unsigned long tmo;
242
243	writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_TX_ON);
244
245	memcpy(testpacket, nic->node_addr, ETH_ALEN);
246	memcpy(testpacket+ETH_ALEN, nic->node_addr, ETH_ALEN);
247
248	outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
249	outw(ETH_ZLEN, eth_nic_base + TX_LEN_PORT);
250
251	/* Test to see if the chip has allocated memory for the packet */
252	for (tmo = currticks() + 2;
253	     (readreg(PP_BusST) & READY_FOR_TX_NOW) == 0; )
254		if (currticks() >= tmo)
255			return(0);
256
257	/* Write the contents of the packet */
258	outsw(eth_nic_base + TX_FRAME_PORT, testpacket,
259	      (ETH_ZLEN+1)>>1);
260
261	printf(" sending test packet ");
262	/* wait a couple of timer ticks for packet to be received */
263	for (tmo = currticks() + 2; currticks() < tmo; );
264
265	if ((readreg(PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
266			printf("succeeded");
267			return 1;
268	}
269	printf("failed");
270	return 0;
271}
272
273
274static int detect_aui(struct nic *nic)
275{
276	clrline(); printf("attempting %s:","AUI");
277	control_dc_dc(0);
278
279	writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
280
281	if (send_test_pkt(nic)) {
282		return A_CNF_MEDIA_AUI; }
283	else
284		return 0;
285}
286
287static int detect_bnc(struct nic *nic)
288{
289	clrline(); printf("attempting %s:","BNC");
290	control_dc_dc(1);
291
292	writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
293
294	if (send_test_pkt(nic)) {
295		return A_CNF_MEDIA_10B_2; }
296	else
297		return 0;
298}
299
300/**************************************************************************
301ETH_RESET - Reset adapter
302***************************************************************************/
303
304static void cs89x0_reset(struct nic *nic)
305{
306	int  i;
307	unsigned long reset_tmo;
308
309	writereg(PP_SelfCTL, readreg(PP_SelfCTL) | POWER_ON_RESET);
310
311	/* wait for two ticks; that is 2*55ms */
312	for (reset_tmo = currticks() + 2; currticks() < reset_tmo; );
313
314	if (eth_cs_type != CS8900) {
315		/* Hardware problem requires PNP registers to be reconfigured
316		   after a reset */
317		if (eth_irq != 0xFFFF) {
318			outw(PP_CS8920_ISAINT, eth_nic_base + ADD_PORT);
319			outb(eth_irq, eth_nic_base + DATA_PORT);
320			outb(0, eth_nic_base + DATA_PORT + 1); }
321
322		if (eth_mem_start) {
323			outw(PP_CS8920_ISAMemB, eth_nic_base + ADD_PORT);
324			outb((eth_mem_start >> 8) & 0xff, eth_nic_base + DATA_PORT);
325			outb((eth_mem_start >> 24) & 0xff, eth_nic_base + DATA_PORT + 1); } }
326
327	/* Wait until the chip is reset */
328	for (reset_tmo = currticks() + 2;
329	     (readreg(PP_SelfST) & INIT_DONE) == 0 &&
330		     currticks() < reset_tmo; );
331
332	/* disable interrupts and memory accesses */
333	writereg(PP_BusCTL, 0);
334
335	/* set the ethernet address */
336	for (i=0; i < ETH_ALEN/2; i++)
337		writereg(PP_IA+i*2,
338			 nic->node_addr[i*2] |
339			 (nic->node_addr[i*2+1] << 8));
340
341	/* receive only error free packets addressed to this card */
342	writereg(PP_RxCTL, DEF_RX_ACCEPT);
343
344	/* do not generate any interrupts on receive operations */
345	writereg(PP_RxCFG, 0);
346
347	/* do not generate any interrupts on transmit operations */
348	writereg(PP_TxCFG, 0);
349
350	/* do not generate any interrupts on buffer operations */
351	writereg(PP_BufCFG, 0);
352
353	/* reset address port, so that autoprobing will keep working */
354	outw(PP_ChipID, eth_nic_base + ADD_PORT);
355
356	return;
357}
358
359/**************************************************************************
360ETH_TRANSMIT - Transmit a frame
361***************************************************************************/
362
363static void cs89x0_transmit(
364	struct nic *nic,
365	const char *d,			/* Destination */
366	unsigned int t,			/* Type */
367	unsigned int s,			/* size */
368	const char *p)			/* Packet */
369{
370	unsigned long tmo;
371	int           sr;
372
373	/* does this size have to be rounded??? please,
374	   somebody have a look in the specs */
375	if ((sr = ((s + ETH_HLEN + 1)&~1)) < ETH_ZLEN)
376		sr = ETH_ZLEN;
377
378retry:
379	/* initiate a transmit sequence */
380	outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
381	outw(sr, eth_nic_base + TX_LEN_PORT);
382
383	/* Test to see if the chip has allocated memory for the packet */
384	if ((readreg(PP_BusST) & READY_FOR_TX_NOW) == 0) {
385		/* Oops... this should not happen! */
386		printf("cs: unable to send packet; retrying...\n");
387		for (tmo = currticks() + 5*TICKS_PER_SEC; currticks() < tmo; );
388		cs89x0_reset(nic);
389		goto retry; }
390
391	/* Write the contents of the packet */
392	outsw(eth_nic_base + TX_FRAME_PORT, d, ETH_ALEN/2);
393	outsw(eth_nic_base + TX_FRAME_PORT, nic->node_addr,
394	      ETH_ALEN/2);
395	outw(((t >> 8)&0xFF)|(t << 8), eth_nic_base + TX_FRAME_PORT);
396	outsw(eth_nic_base + TX_FRAME_PORT, p, (s+1)/2);
397	for (sr = sr/2 - (s+1)/2 - ETH_ALEN - 1; sr-- > 0;
398	     outw(0, eth_nic_base + TX_FRAME_PORT));
399
400	/* wait for transfer to succeed */
401	for (tmo = currticks()+5*TICKS_PER_SEC;
402	     (s = readreg(PP_TxEvent)&~0x1F) == 0 && currticks() < tmo;)
403		/* nothing */ ;
404	if ((s & TX_SEND_OK_BITS) != TX_OK) {
405		printf("\ntransmission error %#hX\n", s);
406	}
407
408	return;
409}
410
411/**************************************************************************
412ETH_POLL - Wait for a frame
413***************************************************************************/
414
415static int cs89x0_poll(struct nic *nic)
416{
417	int status;
418
419	status = readreg(PP_RxEvent);
420
421	if ((status & RX_OK) == 0)
422		return(0);
423
424	status = inw(eth_nic_base + RX_FRAME_PORT);
425	nic->packetlen = inw(eth_nic_base + RX_FRAME_PORT);
426	insw(eth_nic_base + RX_FRAME_PORT, nic->packet, nic->packetlen >> 1);
427	if (nic->packetlen & 1)
428		nic->packet[nic->packetlen-1] = inw(eth_nic_base + RX_FRAME_PORT);
429	return 1;
430}
431
432static void cs89x0_disable(struct nic *nic)
433{
434	cs89x0_reset(nic);
435}
436
437/**************************************************************************
438ETH_PROBE - Look for an adapter
439***************************************************************************/
440
441struct nic *cs89x0_probe(struct nic *nic, unsigned short *probe_addrs)
442{
443	static const unsigned int netcard_portlist[] = {
444#ifdef	CS_SCAN
445		CS_SCAN,
446#else	/* use "conservative" default values for autoprobing */
447		0x300,0x320,0x340,0x200,0x220,0x240,
448		0x260,0x280,0x2a0,0x2c0,0x2e0,
449	/* if that did not work, then be more aggressive */
450		0x301,0x321,0x341,0x201,0x221,0x241,
451		0x261,0x281,0x2a1,0x2c1,0x2e1,
452#endif
453		0};
454
455	int      i, result = -1;
456	unsigned rev_type = 0, ioaddr, ioidx, isa_cnf, cs_revision;
457	unsigned short eeprom_buff[CHKSUM_LEN];
458
459
460	for (ioidx = 0; (ioaddr=netcard_portlist[ioidx++]) != 0; ) {
461		/* if they give us an odd I/O address, then do ONE write to
462		   the address port, to get it back to address zero, where we
463		   expect to find the EISA signature word. */
464		if (ioaddr & 1) {
465			ioaddr &= ~1;
466			if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
467				continue;
468			outw(PP_ChipID, ioaddr + ADD_PORT);
469		}
470
471		if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
472			continue;
473		eth_nic_base = ioaddr;
474
475		/* get the chip type */
476		rev_type = readreg(PRODUCT_ID_ADD);
477		eth_cs_type = rev_type &~ REVISON_BITS;
478		cs_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
479
480		printf("\ncs: cs89%c0%s rev %c, base %#hX",
481		       eth_cs_type==CS8900?'0':'2',
482		       eth_cs_type==CS8920M?"M":"",
483		       cs_revision,
484		       eth_nic_base);
485
486		/* First check to see if an EEPROM is attached*/
487		if ((readreg(PP_SelfST) & EEPROM_PRESENT) == 0) {
488			printf("\ncs: no EEPROM...\n");
489			outw(PP_ChipID, eth_nic_base + ADD_PORT);
490			continue; }
491		else if (get_eeprom_data(START_EEPROM_DATA,CHKSUM_LEN,
492					 eeprom_buff) < 0) {
493			printf("\ncs: EEPROM read failed...\n");
494			outw(PP_ChipID, eth_nic_base + ADD_PORT);
495			continue; }
496		else if (get_eeprom_chksum(START_EEPROM_DATA,CHKSUM_LEN,
497					   eeprom_buff) < 0) {
498			printf("\ncs: EEPROM checksum bad...\n");
499			outw(PP_ChipID, eth_nic_base + ADD_PORT);
500			continue; }
501
502		/* get transmission control word but keep the
503		   autonegotiation bits */
504		eth_auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
505		/* Store adapter configuration */
506		eth_adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
507		/* Store ISA configuration */
508		isa_cnf = eeprom_buff[ISA_CNF_OFFSET/2];
509
510		/* store the initial memory base address */
511		eth_mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
512
513		printf("%s%s%s, addr ",
514		       (eth_adapter_cnf & A_CNF_10B_T)?", RJ-45":"",
515		       (eth_adapter_cnf & A_CNF_AUI)?", AUI":"",
516		       (eth_adapter_cnf & A_CNF_10B_2)?", BNC":"");
517
518		/* If this is a CS8900 then no pnp soft */
519		if (eth_cs_type != CS8900 &&
520		    /* Check if the ISA IRQ has been set  */
521		    (i = readreg(PP_CS8920_ISAINT) & 0xff,
522		     (i != 0 && i < CS8920_NO_INTS)))
523			eth_irq = i;
524		else {
525			i = isa_cnf & INT_NO_MASK;
526			if (eth_cs_type == CS8900) {
527				/* the table that follows is dependent
528				   upon how you wired up your cs8900
529				   in your system.  The table is the
530				   same as the cs8900 engineering demo
531				   board.  irq_map also depends on the
532				   contents of the table.  Also see
533				   write_irq, which is the reverse
534				   mapping of the table below. */
535				if (i < 4) i = "\012\013\014\005"[i];
536				else printf("\ncs: BUG: isa_config is %d\n", i); }
537			eth_irq = i; }
538
539		/* Retrieve and print the ethernet address. */
540		for (i=0; i<ETH_ALEN; i++) {
541			nic->node_addr[i] = ((unsigned char *)eeprom_buff)[i];
542		}
543		printf("%!\n", nic->node_addr);
544
545		/* Set the LineCTL quintuplet based on adapter
546		   configuration read from EEPROM */
547		if ((eth_adapter_cnf & A_CNF_EXTND_10B_2) &&
548		    (eth_adapter_cnf & A_CNF_LOW_RX_SQUELCH))
549			eth_linectl = LOW_RX_SQUELCH;
550		else
551			eth_linectl = 0;
552
553		/* check to make sure that they have the "right"
554		   hardware available */
555		switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
556		case A_CNF_MEDIA_10B_T: result = eth_adapter_cnf & A_CNF_10B_T;
557			break;
558		case A_CNF_MEDIA_AUI:   result = eth_adapter_cnf & A_CNF_AUI;
559			break;
560		case A_CNF_MEDIA_10B_2: result = eth_adapter_cnf & A_CNF_10B_2;
561			break;
562		default: result = eth_adapter_cnf & (A_CNF_10B_T | A_CNF_AUI |
563						     A_CNF_10B_2);
564		}
565		if (!result) {
566			printf("cs: EEPROM is configured for unavailable media\n");
567		error:
568			writereg(PP_LineCTL, readreg(PP_LineCTL) &
569				 ~(SERIAL_TX_ON | SERIAL_RX_ON));
570			outw(PP_ChipID, eth_nic_base + ADD_PORT);
571			continue;
572		}
573
574		/* Initialize the card for probing of the attached media */
575		cs89x0_reset(nic);
576
577		/* set the hardware to the configured choice */
578		switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
579		case A_CNF_MEDIA_10B_T:
580			result = detect_tp();
581			if (!result) {
582				clrline();
583				printf("10Base-T (RJ-45%s",
584				       ") has no cable\n"); }
585			/* check "ignore missing media" bit */
586			if (eth_auto_neg_cnf & IMM_BIT)
587				/* Yes! I don't care if I see a link pulse */
588				result = A_CNF_MEDIA_10B_T;
589			break;
590		case A_CNF_MEDIA_AUI:
591			result = detect_aui(nic);
592			if (!result) {
593				clrline();
594				printf("10Base-5 (AUI%s",
595				       ") has no cable\n"); }
596			/* check "ignore missing media" bit */
597			if (eth_auto_neg_cnf & IMM_BIT)
598				/* Yes! I don't care if I see a carrrier */
599				result = A_CNF_MEDIA_AUI;
600			break;
601		case A_CNF_MEDIA_10B_2:
602			result = detect_bnc(nic);
603			if (!result) {
604				clrline();
605				printf("10Base-2 (BNC%s",
606				       ") has no cable\n"); }
607			/* check "ignore missing media" bit */
608			if (eth_auto_neg_cnf & IMM_BIT)
609				/* Yes! I don't care if I can xmit a packet */
610				result = A_CNF_MEDIA_10B_2;
611			break;
612		case A_CNF_MEDIA_AUTO:
613			writereg(PP_LineCTL, eth_linectl | AUTO_AUI_10BASET);
614			if (eth_adapter_cnf & A_CNF_10B_T)
615				if ((result = detect_tp()) != 0)
616					break;
617			if (eth_adapter_cnf & A_CNF_AUI)
618				if ((result = detect_aui(nic)) != 0)
619					break;
620			if (eth_adapter_cnf & A_CNF_10B_2)
621				if ((result = detect_bnc(nic)) != 0)
622					break;
623			clrline(); printf("no media detected\n");
624			goto error;
625		}
626		clrline();
627		switch(result) {
628		case 0:                 printf("no network cable attached to configured media\n");
629			goto error;
630		case A_CNF_MEDIA_10B_T: printf("using 10Base-T (RJ-45)\n");
631			break;
632		case A_CNF_MEDIA_AUI:   printf("using 10Base-5 (AUI)\n");
633			break;
634		case A_CNF_MEDIA_10B_2: printf("using 10Base-2 (BNC)\n");
635			break;
636		}
637
638		/* Turn on both receive and transmit operations */
639		writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_RX_ON |
640			 SERIAL_TX_ON);
641
642		break;
643	}
644
645	if (ioaddr == 0)
646		return (0);
647	nic->reset = cs89x0_reset;
648	nic->poll = cs89x0_poll;
649	nic->transmit = cs89x0_transmit;
650	nic->disable = cs89x0_disable;
651	return (nic);
652}
653
654/*
655 * Local variables:
656 *  c-basic-offset: 8
657 * End:
658 */
659
660