1/* $NetBSD: 3c90xb.c,v 1.13 2007/03/04 05:59:59 christos Exp $ */
2
3/*
4 * Copyright (c) 1999
5 * 	Matthias Drochner.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/types.h>
30#include <machine/pio.h>
31
32struct mbuf; /* XXX */
33typedef int bus_dmamap_t; /* XXX */
34#include <dev/ic/elink3reg.h>
35#include <dev/ic/elinkxlreg.h>
36
37#include <lib/libsa/stand.h>
38
39#include <libi386.h>
40#include <pcivar.h>
41
42#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD)
43#include <lib/libkern/libkern.h>
44#include <bootinfo.h>
45#endif
46
47#include "etherdrv.h"
48
49#define RECVBUF_SIZE 1600 /* struct ex_upd + packet */
50
51#ifdef _STANDALONE
52
53static pcihdl_t mytag;
54static char recvbuf[RECVBUF_SIZE];
55#define RECVBUF_PHYS vtophys(recvbuf)
56#define RECVBUF_VIRT ((void *)recvbuf)
57static struct ex_dpd sndbuf;
58#define SNDBUF_PHYS vtophys(&sndbuf)
59#define SNDBUF_VIRT ((void *)&sndbuf)
60
61#else /* !standalone, userspace testing environment */
62
63#define	PCI_MODE1_ENABLE	0x80000000UL
64#define PCIBUSNO 1
65#define PCIDEVNO 4
66static pcihdl_t mytag = PCI_MODE1_ENABLE | (PCIBUSNO << 16) | (PCIDEVNO << 11);
67
68extern void *mapmem(int, int);
69void *dmamem; /* virtual */
70#define DMABASE 0x3ffd800
71#define DMASIZE 10240
72#define RECVBUF_PHYS DMABASE
73#define RECVBUF_VIRT dmamem
74#define SNDBUF_PHYS (DMABASE + RECVBUF_SIZE)
75#define SNDBUF_VIRT ((void *)(((char *)dmamem) + RECVBUF_SIZE))
76
77#endif /* _STANDALONE */
78
79
80#define CSR_READ_1(reg) inb(iobase + (reg))
81#define CSR_READ_2(reg) inw(iobase + (reg))
82#define CSR_READ_4(reg) inl(iobase + (reg))
83#define CSR_WRITE_1(reg, val) outb(iobase + (reg), val)
84#define CSR_WRITE_2(reg, val) outw(iobase + (reg), val)
85#define CSR_WRITE_4(reg, val) outl(iobase + (reg), val)
86
87#undef GO_WINDOW
88#define GO_WINDOW(x) CSR_WRITE_2(ELINK_COMMAND, WINDOW_SELECT | x)
89
90static int iobase;
91static u_char myethaddr[6];
92unsigned ether_medium;
93
94static struct {
95	int did;
96	int mii;
97} excards[] = {
98	{0x9005, 0}, /* 3c900b Combo */
99	{0x9055, 1}, /* 3c905b TP */
100	{0x9058, 0}, /* 3c905b Combo */
101	{-1}
102}, *excard;
103
104static struct mtabentry {
105	int address_cfg; /* configured connector */
106	int config_bit; /* connector present */
107	char *name;
108} mediatab[] = { /* indexed by media type - etherdrv.h */
109	{ELINKMEDIA_10BASE_2, ELINK_PCI_BNC, "BNC"},
110	{ELINKMEDIA_10BASE_T, ELINK_PCI_10BASE_T, "UTP"},
111	{ELINKMEDIA_AUI, ELINK_PCI_AUI, "AUI"},
112	{ELINKMEDIA_MII, ELINK_PCI_100BASE_MII, "MII"},
113	{ELINKMEDIA_100BASE_TX, ELINK_PCI_100BASE_TX, "100TX"},
114};
115
116#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD)
117static struct btinfo_netif bi_netif;
118#endif
119
120#define ex_waitcmd() \
121	do { \
122		while (CSR_READ_2(ELINK_STATUS) & COMMAND_IN_PROGRESS) \
123			continue; \
124	} while (0)
125
126void ex_reset(void);
127uint16_t ex_read_eeprom(int);
128static int ex_eeprom_busy(void);
129void ex_init(void);
130void ex_set_media(void);
131
132void
133ex_reset(void)
134{
135	CSR_WRITE_2(ELINK_COMMAND, GLOBAL_RESET);
136	delay(100000);
137	ex_waitcmd();
138}
139
140/*
141 * Read EEPROM data.
142 * XXX what to do if EEPROM doesn't unbusy?
143 */
144uint16_t
145ex_read_eeprom(int offset)
146{
147	uint16_t data = 0;
148
149	GO_WINDOW(0);
150	if (ex_eeprom_busy())
151		goto out;
152	CSR_WRITE_1(ELINK_W0_EEPROM_COMMAND, READ_EEPROM | (offset & 0x3f));
153	if (ex_eeprom_busy())
154		goto out;
155	data = CSR_READ_2(ELINK_W0_EEPROM_DATA);
156out:
157	return data;
158}
159
160static int
161ex_eeprom_busy(void)
162{
163	int i = 100;
164
165	while (i--) {
166		if (!(CSR_READ_2(ELINK_W0_EEPROM_COMMAND) & EEPROM_BUSY))
167			return 0;
168		delay(100);
169	}
170	printf("\nex: eeprom stays busy.\n");
171	return 1;
172}
173
174/*
175 * Bring device up.
176 */
177void
178ex_init(void)
179{
180	int i;
181
182	ex_waitcmd();
183	EtherStop();
184
185	/*
186	 * Set the station address and clear the station mask. The latter
187	 * is needed for 90x cards, 0 is the default for 90xB cards.
188	 */
189	GO_WINDOW(2);
190	for (i = 0; i < 6; i++) {
191		CSR_WRITE_1(ELINK_W2_ADDR_0 + i,
192		    myethaddr[i]);
193		CSR_WRITE_1(ELINK_W2_RECVMASK_0 + i, 0);
194	}
195
196	GO_WINDOW(3);
197
198	CSR_WRITE_2(ELINK_COMMAND, RX_RESET);
199	ex_waitcmd();
200	CSR_WRITE_2(ELINK_COMMAND, TX_RESET);
201	ex_waitcmd();
202
203	CSR_WRITE_2(ELINK_COMMAND, SET_INTR_MASK | 0); /* disable */
204	CSR_WRITE_2(ELINK_COMMAND, ACK_INTR | 0xff);
205
206	ex_set_media();
207
208	CSR_WRITE_2(ELINK_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST);
209
210	CSR_WRITE_4(ELINK_DNLISTPTR, 0);
211	CSR_WRITE_2(ELINK_COMMAND, TX_ENABLE);
212
213	CSR_WRITE_4(ELINK_UPLISTPTR, RECVBUF_PHYS);
214	CSR_WRITE_2(ELINK_COMMAND, RX_ENABLE);
215	CSR_WRITE_2(ELINK_COMMAND, ELINK_UPUNSTALL);
216
217	GO_WINDOW(1);
218}
219
220void
221ex_set_media(void)
222{
223	int config0, config1;
224
225	CSR_WRITE_2(ELINK_W3_MAC_CONTROL, 0);
226
227	if (ether_medium == ETHERMEDIUM_MII)
228		goto setcfg;
229
230	GO_WINDOW(4);
231	CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, 0);
232	CSR_WRITE_2(ELINK_COMMAND, STOP_TRANSCEIVER);
233	delay(800);
234
235	switch (ether_medium) {
236	case ETHERMEDIUM_UTP:
237		CSR_WRITE_2(ELINK_W4_MEDIA_TYPE,
238			    JABBER_GUARD_ENABLE | LINKBEAT_ENABLE);
239		break;
240	case ETHERMEDIUM_BNC:
241		CSR_WRITE_2(ELINK_COMMAND, START_TRANSCEIVER);
242		delay(800);
243		break;
244	case ETHERMEDIUM_AUI:
245		CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, SQE_ENABLE);
246		delay(800);
247		break;
248	case ETHERMEDIUM_100TX:
249		CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, LINKBEAT_ENABLE);
250		break;
251	}
252
253setcfg:
254	GO_WINDOW(3);
255
256	config0 = CSR_READ_2(ELINK_W3_INTERNAL_CONFIG);
257	config1 = CSR_READ_2(ELINK_W3_INTERNAL_CONFIG + 2);
258
259	config1 = config1 & ~CONFIG_MEDIAMASK;
260	config1 |= (mediatab[ether_medium].address_cfg
261		    << CONFIG_MEDIAMASK_SHIFT);
262
263	CSR_WRITE_2(ELINK_W3_INTERNAL_CONFIG, config0);
264	CSR_WRITE_2(ELINK_W3_INTERNAL_CONFIG + 2, config1);
265}
266
267static void
268ex_probemedia(void)
269{
270	int i, j;
271	struct mtabentry *m;
272
273	/* test for presence of connectors */
274	GO_WINDOW(3);
275	i = CSR_READ_1(ELINK_W3_RESET_OPTIONS);
276	j = (CSR_READ_2(ELINK_W3_INTERNAL_CONFIG + 2) & CONFIG_MEDIAMASK)
277		>> CONFIG_MEDIAMASK_SHIFT;
278	GO_WINDOW(0);
279
280	for (ether_medium = 0, m = mediatab;
281	     ether_medium < sizeof(mediatab) / sizeof(mediatab[0]);
282	     ether_medium++, m++) {
283		if (j == m->address_cfg) {
284			if (!(i & m->config_bit)) {
285				printf("%s not present\n", m->name);
286				goto bad;
287			}
288			printf("using %s\n", m->name);
289			return;
290		}
291	}
292	printf("unknown connector\n");
293bad:
294	ether_medium = -1;
295}
296
297int
298EtherInit(unsigned char *myadr)
299{
300	uint32_t pcicsr;
301	uint16_t val;
302	volatile struct ex_upd *upd;
303#ifndef _STANDALONE
304	uint32_t id;
305#endif
306
307	if (pcicheck()) {
308		printf("pcicheck failed\n");
309		return 0;
310	}
311#ifndef _STANDALONE
312	pcicfgread(&mytag, 0, &id);
313#endif
314	for (excard = &excards[0]; excard->did != -1; excard++) {
315#ifdef _STANDALONE
316		if (pcifinddev(0x10b7, excard->did, &mytag) == 0)
317			goto found;
318#else
319		if (id == (0x10b7 | (excard->did << 16)))
320			goto found;
321#endif
322	}
323	printf("no ex\n");
324	return 0;
325
326found:
327	pcicfgread(&mytag, 0x10, &iobase);
328	iobase &= ~3;
329
330#ifndef _STANDALONE
331	dmamem = mapmem(DMABASE, DMASIZE);
332	if (!dmamem)
333		return 0;
334#endif
335
336	/* enable bus mastering in PCI command register */
337	if (pcicfgread(&mytag, 0x04, (int *)&pcicsr)
338	    || pcicfgwrite(&mytag, 0x04, pcicsr | 4)) {
339		printf("cannot enable DMA\n");
340		return 0;
341	}
342
343	ex_reset();
344
345	if (excard->mii)
346		ether_medium = ETHERMEDIUM_MII;
347	else {
348		ex_probemedia();
349		if (ether_medium < 0)
350			return 0;
351	}
352
353	val = ex_read_eeprom(EEPROM_OEM_ADDR0);
354	myethaddr[0] = val >> 8;
355	myethaddr[1] = val & 0xff;
356	val = ex_read_eeprom(EEPROM_OEM_ADDR1);
357	myethaddr[2] = val >> 8;
358	myethaddr[3] = val & 0xff;
359	val = ex_read_eeprom(EEPROM_OEM_ADDR2);
360	myethaddr[4] = val >> 8;
361	myethaddr[5] = val & 0xff;
362	memcpy(myadr, myethaddr, 6);
363
364	upd = RECVBUF_VIRT;
365	upd->upd_nextptr = RECVBUF_PHYS;
366	upd->upd_pktstatus = 1500;
367	upd->upd_frags[0].fr_addr = RECVBUF_PHYS + 100;
368	upd->upd_frags[0].fr_len = 1500 | EX_FR_LAST;
369
370	ex_init();
371
372#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD)
373	strncpy(bi_netif.ifname, "ex", sizeof(bi_netif.ifname));
374	bi_netif.bus = BI_BUS_PCI;
375	bi_netif.addr.tag = mytag;
376
377	BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif));
378#endif
379
380	return 1;
381}
382
383void
384EtherStop(void)
385{
386	/*
387	 * Issue software reset
388	 */
389	CSR_WRITE_2(ELINK_COMMAND, RX_DISABLE);
390	CSR_WRITE_2(ELINK_COMMAND, TX_DISABLE);
391        CSR_WRITE_2(ELINK_COMMAND, STOP_TRANSCEIVER);
392	CSR_WRITE_2(ELINK_COMMAND, INTR_LATCH);
393}
394
395int
396EtherSend(char *pkt, int len)
397{
398	volatile struct ex_dpd *dpd;
399	int i;
400
401	dpd = SNDBUF_VIRT;
402
403	dpd->dpd_nextptr = 0;
404	dpd->dpd_fsh = len;
405#ifdef _STANDALONE
406	dpd->dpd_frags[0].fr_addr = vtophys(pkt);
407#else
408	memcpy(SNDBUF_VIRT + 100, pkt, len);
409	dpd->dpd_frags[0].fr_addr = SNDBUF_PHYS + 100;
410#endif
411	dpd->dpd_frags[0].fr_len = len | EX_FR_LAST;
412
413	CSR_WRITE_4(ELINK_DNLISTPTR, SNDBUF_PHYS);
414	CSR_WRITE_2(ELINK_COMMAND, ELINK_DNUNSTALL);
415
416	i = 10000;
417	while (!(dpd->dpd_fsh & 0x00010000)) {
418		if (--i < 0) {
419			printf("3c90xb: send timeout\n");
420			return -1;
421		}
422		delay(1);
423	}
424
425	return len;
426}
427
428int
429EtherReceive(char *pkt, int maxlen)
430{
431	volatile struct ex_upd *upd;
432	int len;
433
434	upd = RECVBUF_VIRT;
435
436	if (!(upd->upd_pktstatus & ~EX_UPD_PKTLENMASK))
437		return 0;
438
439	len = upd->upd_pktstatus & EX_UPD_PKTLENMASK;
440	if (len > maxlen)
441		len = 0;
442	else
443		memcpy(pkt, RECVBUF_VIRT + 100, len);
444
445	upd->upd_pktstatus = 1500;
446	CSR_WRITE_2(ELINK_COMMAND, ELINK_UPUNSTALL);
447
448	return len;
449}
450