1/*	$NetBSD: elink3.c,v 1.3 1998/02/16 11:26:36 drochner Exp $	*/
2
3/* stripped down from freebsd:sys/i386/netboot/3c509.c */
4
5/**************************************************************************
6NETBOOT -  BOOTP/TFTP Bootstrap Program
7
8Author: 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
233c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
24
253c509.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
34#include <libi386.h>
35
36#include "etherdrv.h"
37#include "3c509.h"
38
39extern unsigned short eth_base;
40
41extern u_char eth_myaddr[6];
42
43void
44epstop(void)
45{
46
47	/* stop card */
48	outw(BASE + EP_COMMAND, RX_DISABLE);
49	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
50	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
51
52	outw(BASE + EP_COMMAND, TX_DISABLE);
53	outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
54
55	outw(BASE + EP_COMMAND, RX_RESET);
56	outw(BASE + EP_COMMAND, TX_RESET);
57
58	outw(BASE + EP_COMMAND, C_INTR_LATCH);
59	outw(BASE + EP_COMMAND, SET_RD_0_MASK);
60	outw(BASE + EP_COMMAND, SET_INTR_MASK);
61	outw(BASE + EP_COMMAND, SET_RX_FILTER);
62}
63
64void
65EtherStop(void)
66{
67
68	epstop();
69	outw(BASE + EP_COMMAND, GLOBAL_RESET);
70	delay(100000);
71}
72
73/**************************************************************************
74ETH_RESET - Reset adapter
75***************************************************************************/
76void
77epreset(void)
78{
79	int i;
80
81	/***********************************************************
82			Reset 3Com 509 card
83	*************************************************************/
84
85	epstop();
86
87	/*
88	 * initialize card
89	*/
90	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
91		continue;
92
93	GO_WINDOW(0);
94
95	/* Disable the card */
96	outw(BASE + EP_W0_CONFIG_CTRL, 0);
97
98	/* Configure IRQ to none */
99	outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(0));
100
101	/* Enable the card */
102	outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
103
104	GO_WINDOW(2);
105
106	/* Reload the ether_addr. */
107	for (i = 0; i < 6; i++)
108		outb(BASE + EP_W2_ADDR_0 + i, eth_myaddr[i]);
109
110	outw(BASE + EP_COMMAND, RX_RESET);
111	outw(BASE + EP_COMMAND, TX_RESET);
112
113	/* Window 1 is operating window */
114	GO_WINDOW(1);
115	for (i = 0; i < 31; i++)
116		inb(BASE + EP_W1_TX_STATUS);
117
118	/* get rid of stray intr's */
119	outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
120
121	outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
122
123	outw(BASE + EP_COMMAND, SET_INTR_MASK);
124
125	outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
126	    FIL_BRDCST);
127
128	/* configure BNC */
129	if (ether_medium == ETHERMEDIUM_BNC) {
130		outw(BASE + EP_COMMAND, START_TRANSCEIVER);
131		delay(1000);
132	}
133	/* configure UTP */
134	if (ether_medium == ETHERMEDIUM_UTP) {
135		GO_WINDOW(4);
136		outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
137		GO_WINDOW(1);
138	}
139
140	/* start tranciever and receiver */
141	outw(BASE + EP_COMMAND, RX_ENABLE);
142	outw(BASE + EP_COMMAND, TX_ENABLE);
143
144	/* set early threshold for minimal packet length */
145	outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 64);
146
147	outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16);
148}
149
150/**************************************************************************
151ETH_TRANSMIT - Transmit a frame
152***************************************************************************/
153static const char padmap[] = {
154	0, 3, 2, 1};
155
156int
157EtherSend(char *pkt, int len)
158{
159	int pad;
160	int status;
161
162#ifdef EDEBUG
163	printf("{l=%d}", len);
164#endif
165
166	pad = padmap[len & 3];
167
168	/*
169	* The 3c509 automatically pads short packets to minimum ethernet length,
170	* but we drop packets that are too large. Perhaps we should truncate
171	* them instead?
172	*/
173	if (len + pad > ETHER_MAX_LEN) {
174		return -1;
175	}
176
177	/* drop acknowledgements */
178	while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
179		if (status & (TXS_UNDERRUN | TXS_MAX_COLLISION |
180			TXS_STATUS_OVERFLOW)) {
181			outw(BASE + EP_COMMAND, TX_RESET);
182			outw(BASE + EP_COMMAND, TX_ENABLE);
183		}
184
185		outb(BASE + EP_W1_TX_STATUS, 0x0);
186	}
187
188	while (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
189		/* no room in FIFO */
190		continue;
191	}
192
193	outw(BASE + EP_W1_TX_PIO_WR_1, len);
194	outw(BASE + EP_W1_TX_PIO_WR_1, 0x0);	/* Second dword meaningless */
195
196	/* write packet */
197	outsw(BASE + EP_W1_TX_PIO_WR_1, pkt, len / 2);
198	if (len & 1)
199		outb(BASE + EP_W1_TX_PIO_WR_1, *(pkt + len - 1));
200
201	while (pad--)
202		outb(BASE + EP_W1_TX_PIO_WR_1, 0);	/* Padding */
203
204	/* timeout after sending */
205	delay(1000);
206	return len;
207}
208
209/**************************************************************************
210ETH_POLL - Wait for a frame
211***************************************************************************/
212int
213EtherReceive(char *pkt, int maxlen)
214{
215	/* common variables */
216	int len;
217	/* variables for 3C509 */
218	short status, cst;
219	register short rx_fifo;
220
221	cst = inw(BASE + EP_STATUS);
222
223#ifdef EDEBUG
224	if (cst & 0x1FFF)
225		printf("-%x-",cst);
226#endif
227
228	if ((cst & (S_RX_COMPLETE|S_RX_EARLY)) == 0) {
229		/* acknowledge  everything */
230		outw(BASE + EP_COMMAND, ACK_INTR| (cst & S_5_INTS));
231		outw(BASE + EP_COMMAND, C_INTR_LATCH);
232
233		return 0;
234	}
235
236	status = inw(BASE + EP_W1_RX_STATUS);
237#ifdef EDEBUG
238	printf("*%x*",status);
239#endif
240
241	if (status & ERR_RX) {
242		outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
243		return 0;
244	}
245
246	rx_fifo = status & RX_BYTES_MASK;
247	if (rx_fifo == 0)
248		return 0;
249
250	if (rx_fifo > maxlen)
251		goto zulang;
252
253	/* read packet */
254#ifdef EDEBUG
255	printf("[l=%d",rx_fifo);
256#endif
257	insw(BASE + EP_W1_RX_PIO_RD_1, pkt, rx_fifo / 2);
258	if (rx_fifo & 1)
259		pkt[rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1);
260	len = rx_fifo;
261
262	for (;;) {
263		status = inw(BASE + EP_W1_RX_STATUS);
264#ifdef EDEBUG
265		printf("*%x*",status);
266#endif
267		rx_fifo = status & RX_BYTES_MASK;
268
269		if (rx_fifo > 0) {
270			if ((len + rx_fifo) > maxlen)
271				goto zulang;
272
273			insw(BASE + EP_W1_RX_PIO_RD_1, pkt + len, rx_fifo / 2);
274			if (rx_fifo & 1)
275				pkt[len + rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1);
276			len += rx_fifo;
277#ifdef EDEBUG
278			printf("+%d",rx_fifo);
279#endif
280		}
281
282		if ((status & RX_INCOMPLETE) == 0) {
283#ifdef EDEBUG
284			printf("=%d",len);
285#endif
286			break;
287		}
288
289		delay(1000);
290	}
291
292	/* acknowledge reception of packet */
293	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
294	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
295		continue;
296
297	return len;
298
299 zulang:
300	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
301	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
302		continue;
303	return 0;
304}
305
306/*************************************************************************
307	3Com 509 - specific routines
308**************************************************************************/
309
310static int
311eeprom_rdy(void)
312{
313	int i;
314
315	for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
316	if (i >= MAX_EEPROMBUSY) {
317		printf("3c509: eeprom failed to come ready.\r\n");
318		return 0;
319	}
320	return 1;
321}
322
323/*
324 * get_e: gets a 16 bits word from the EEPROM. we must have set the window
325 * before
326 */
327int
328ep_get_e(int offset)
329{
330	if (!eeprom_rdy())
331		return 0xffff;
332	outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset);
333	if (!eeprom_rdy())
334		return 0xffff;
335	return inw(IS_BASE + EP_W0_EEPROM_DATA);
336}
337