1*58a2b000SEvgeniy Ivanov /* $NetBSD: am7990.c,v 1.7 2008/12/14 18:46:33 christos Exp $ */
2*58a2b000SEvgeniy Ivanov
3*58a2b000SEvgeniy Ivanov /* mostly from netbsd:sys/arch/i386/netboot/ne2100.c
4*58a2b000SEvgeniy Ivanov memory allocation now 1 chunk, added deallocation
5*58a2b000SEvgeniy Ivanov receive function changed - don't use irq
6*58a2b000SEvgeniy Ivanov */
7*58a2b000SEvgeniy Ivanov
8*58a2b000SEvgeniy Ivanov /*
9*58a2b000SEvgeniy Ivanov * source in this file came from
10*58a2b000SEvgeniy Ivanov * the Mach ethernet boot written by Leendert van Doorn.
11*58a2b000SEvgeniy Ivanov *
12*58a2b000SEvgeniy Ivanov * A very simple network driver for NE2100 boards that polls.
13*58a2b000SEvgeniy Ivanov *
14*58a2b000SEvgeniy Ivanov * Copyright (c) 1992 by Leendert van Doorn
15*58a2b000SEvgeniy Ivanov */
16*58a2b000SEvgeniy Ivanov
17*58a2b000SEvgeniy Ivanov #include <sys/types.h>
18*58a2b000SEvgeniy Ivanov #include <machine/pio.h>
19*58a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h>
20*58a2b000SEvgeniy Ivanov #include <lib/libsa/stand.h>
21*58a2b000SEvgeniy Ivanov
22*58a2b000SEvgeniy Ivanov #include <libi386.h>
23*58a2b000SEvgeniy Ivanov
24*58a2b000SEvgeniy Ivanov #include "etherdrv.h"
25*58a2b000SEvgeniy Ivanov #include "lance.h"
26*58a2b000SEvgeniy Ivanov
27*58a2b000SEvgeniy Ivanov extern u_char eth_myaddr[6];
28*58a2b000SEvgeniy Ivanov
29*58a2b000SEvgeniy Ivanov extern int lance_rap, lance_rdp;
30*58a2b000SEvgeniy Ivanov
31*58a2b000SEvgeniy Ivanov static void *dmamem;
32*58a2b000SEvgeniy Ivanov
33*58a2b000SEvgeniy Ivanov #define LA(adr) vtophys(adr)
34*58a2b000SEvgeniy Ivanov
35*58a2b000SEvgeniy Ivanov /* Lance register offsets */
36*58a2b000SEvgeniy Ivanov #define LA_CSR lance_rdp
37*58a2b000SEvgeniy Ivanov #define LA_CSR1 lance_rdp
38*58a2b000SEvgeniy Ivanov #define LA_CSR2 lance_rdp
39*58a2b000SEvgeniy Ivanov #define LA_CSR3 lance_rdp
40*58a2b000SEvgeniy Ivanov #define LA_RAP lance_rap
41*58a2b000SEvgeniy Ivanov
42*58a2b000SEvgeniy Ivanov /*
43*58a2b000SEvgeniy Ivanov * Some driver specific constants.
44*58a2b000SEvgeniy Ivanov * Take care when tuning, this program only has 32 Kb
45*58a2b000SEvgeniy Ivanov */
46*58a2b000SEvgeniy Ivanov #define LANCEBUFSIZE 1518 /* plus 4 CRC bytes */
47*58a2b000SEvgeniy Ivanov #define MAXLOOP 1000000L /* arbitrary retry limit */
48*58a2b000SEvgeniy Ivanov #define LOG2NRCVRING 2 /* log2(NRCVRING) */
49*58a2b000SEvgeniy Ivanov #define NRCVRING (1 << LOG2NRCVRING)
50*58a2b000SEvgeniy Ivanov
51*58a2b000SEvgeniy Ivanov static int next_rmd; /* next receive element */
52*58a2b000SEvgeniy Ivanov static initblock_t *initblock; /* initialization block */
53*58a2b000SEvgeniy Ivanov static tmde_t *tmd; /* transmit ring */
54*58a2b000SEvgeniy Ivanov static rmde_t *rmd; /* receive ring */
55*58a2b000SEvgeniy Ivanov static char rbuffer[NRCVRING][LANCEBUFSIZE]; /* receive buffers */
56*58a2b000SEvgeniy Ivanov
57*58a2b000SEvgeniy Ivanov /*
58*58a2b000SEvgeniy Ivanov * Stop ethernet board
59*58a2b000SEvgeniy Ivanov */
60*58a2b000SEvgeniy Ivanov void
am7990_stop(void)61*58a2b000SEvgeniy Ivanov am7990_stop(void)
62*58a2b000SEvgeniy Ivanov {
63*58a2b000SEvgeniy Ivanov long l;
64*58a2b000SEvgeniy Ivanov
65*58a2b000SEvgeniy Ivanov /* stop chip and disable DMA access */
66*58a2b000SEvgeniy Ivanov outw(LA_RAP, RDP_CSR0);
67*58a2b000SEvgeniy Ivanov outw(LA_CSR, CSR_STOP);
68*58a2b000SEvgeniy Ivanov for (l = 0; (inw(LA_CSR) & CSR_STOP) == 0; l++) {
69*58a2b000SEvgeniy Ivanov if (l >= MAXLOOP) {
70*58a2b000SEvgeniy Ivanov printf("Lance failed to stop\n");
71*58a2b000SEvgeniy Ivanov return;
72*58a2b000SEvgeniy Ivanov }
73*58a2b000SEvgeniy Ivanov }
74*58a2b000SEvgeniy Ivanov }
75*58a2b000SEvgeniy Ivanov
76*58a2b000SEvgeniy Ivanov /*
77*58a2b000SEvgeniy Ivanov * Reset ethernet board
78*58a2b000SEvgeniy Ivanov */
79*58a2b000SEvgeniy Ivanov void
am7990_init(void)80*58a2b000SEvgeniy Ivanov am7990_init(void)
81*58a2b000SEvgeniy Ivanov {
82*58a2b000SEvgeniy Ivanov long l;
83*58a2b000SEvgeniy Ivanov u_long addr;
84*58a2b000SEvgeniy Ivanov int i;
85*58a2b000SEvgeniy Ivanov
86*58a2b000SEvgeniy Ivanov /* initblock, tmd, and rmd should be 8 byte aligned;
87*58a2b000SEvgeniy Ivanov sizes of initblock_t and tmde_t are multiples of 8 */
88*58a2b000SEvgeniy Ivanov dmamem = alloc(sizeof(initblock_t) +
89*58a2b000SEvgeniy Ivanov sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
90*58a2b000SEvgeniy Ivanov /* +4 is ok because alloc()'s result is 4-byte aligned! */
91*58a2b000SEvgeniy Ivanov
92*58a2b000SEvgeniy Ivanov initblock = (initblock_t *)(((unsigned long)dmamem + 4) & -8);
93*58a2b000SEvgeniy Ivanov tmd = (tmde_t *)(initblock + 1);
94*58a2b000SEvgeniy Ivanov rmd = (rmde_t *)(tmd + 1);
95*58a2b000SEvgeniy Ivanov
96*58a2b000SEvgeniy Ivanov /* stop the chip, and make sure it did */
97*58a2b000SEvgeniy Ivanov am7990_stop();
98*58a2b000SEvgeniy Ivanov
99*58a2b000SEvgeniy Ivanov /* fill lance initialization block */
100*58a2b000SEvgeniy Ivanov memset(initblock, 0, sizeof(initblock_t));
101*58a2b000SEvgeniy Ivanov
102*58a2b000SEvgeniy Ivanov /* set my ethernet address */
103*58a2b000SEvgeniy Ivanov for (i = 0; i < 6; i++)
104*58a2b000SEvgeniy Ivanov initblock->ib_padr[i] = eth_myaddr[i];
105*58a2b000SEvgeniy Ivanov
106*58a2b000SEvgeniy Ivanov /* receive ring pointer */
107*58a2b000SEvgeniy Ivanov addr = LA(rmd);
108*58a2b000SEvgeniy Ivanov initblock->ib_rdralow = (u_short)addr;
109*58a2b000SEvgeniy Ivanov initblock->ib_rdrahigh = (u_char)(addr >> 16);
110*58a2b000SEvgeniy Ivanov initblock->ib_rlen = LOG2NRCVRING << 5;
111*58a2b000SEvgeniy Ivanov
112*58a2b000SEvgeniy Ivanov /* transmit ring with one element */
113*58a2b000SEvgeniy Ivanov addr = LA(tmd);
114*58a2b000SEvgeniy Ivanov initblock->ib_tdralow = (u_short)addr;
115*58a2b000SEvgeniy Ivanov initblock->ib_tdrahigh = (u_char)(addr >> 16);
116*58a2b000SEvgeniy Ivanov initblock->ib_tlen = 0 << 5;
117*58a2b000SEvgeniy Ivanov
118*58a2b000SEvgeniy Ivanov /* setup the receive ring entries */
119*58a2b000SEvgeniy Ivanov for (next_rmd = 0, i = 0; i < NRCVRING; i++) {
120*58a2b000SEvgeniy Ivanov addr = LA(&rbuffer[i]);
121*58a2b000SEvgeniy Ivanov rmd[i].rmd_ladr = (u_short)addr;
122*58a2b000SEvgeniy Ivanov rmd[i].rmd_hadr = (u_char)(addr >> 16);
123*58a2b000SEvgeniy Ivanov rmd[i].rmd_mcnt = 0;
124*58a2b000SEvgeniy Ivanov rmd[i].rmd_bcnt = -LANCEBUFSIZE;
125*58a2b000SEvgeniy Ivanov rmd[i].rmd_flags = RMD_OWN;
126*58a2b000SEvgeniy Ivanov }
127*58a2b000SEvgeniy Ivanov
128*58a2b000SEvgeniy Ivanov /* zero transmit ring */
129*58a2b000SEvgeniy Ivanov memset(tmd, 0, sizeof(tmde_t));
130*58a2b000SEvgeniy Ivanov
131*58a2b000SEvgeniy Ivanov /* give lance the init block */
132*58a2b000SEvgeniy Ivanov addr = LA(initblock);
133*58a2b000SEvgeniy Ivanov outw(LA_RAP, RDP_CSR1);
134*58a2b000SEvgeniy Ivanov outw(LA_CSR1, (u_short)addr);
135*58a2b000SEvgeniy Ivanov outw(LA_RAP, RDP_CSR2);
136*58a2b000SEvgeniy Ivanov outw(LA_CSR2, (char)(addr >> 16));
137*58a2b000SEvgeniy Ivanov outw(LA_RAP, RDP_CSR3);
138*58a2b000SEvgeniy Ivanov outw(LA_CSR3, 0);
139*58a2b000SEvgeniy Ivanov
140*58a2b000SEvgeniy Ivanov /* and initialize it */
141*58a2b000SEvgeniy Ivanov outw(LA_RAP, RDP_CSR0);
142*58a2b000SEvgeniy Ivanov outw(LA_CSR, CSR_INIT|CSR_STRT);
143*58a2b000SEvgeniy Ivanov
144*58a2b000SEvgeniy Ivanov /* wait for the lance to complete initialization and fire it up */
145*58a2b000SEvgeniy Ivanov for (l = 0; (inw(LA_CSR) & CSR_IDON) == 0; l++) {
146*58a2b000SEvgeniy Ivanov if (l >= MAXLOOP) {
147*58a2b000SEvgeniy Ivanov printf("Lance failed to initialize\n");
148*58a2b000SEvgeniy Ivanov break;
149*58a2b000SEvgeniy Ivanov }
150*58a2b000SEvgeniy Ivanov }
151*58a2b000SEvgeniy Ivanov for (l = 0; (inw(LA_CSR)&(CSR_TXON|CSR_RXON)) != (CSR_TXON|CSR_RXON); l++) {
152*58a2b000SEvgeniy Ivanov if (l >= MAXLOOP) {
153*58a2b000SEvgeniy Ivanov printf("Lance not started\n");
154*58a2b000SEvgeniy Ivanov break;
155*58a2b000SEvgeniy Ivanov }
156*58a2b000SEvgeniy Ivanov }
157*58a2b000SEvgeniy Ivanov }
158*58a2b000SEvgeniy Ivanov
159*58a2b000SEvgeniy Ivanov /*
160*58a2b000SEvgeniy Ivanov * Stop ethernet board and free ressources
161*58a2b000SEvgeniy Ivanov */
162*58a2b000SEvgeniy Ivanov void
EtherStop(void)163*58a2b000SEvgeniy Ivanov EtherStop(void)
164*58a2b000SEvgeniy Ivanov {
165*58a2b000SEvgeniy Ivanov am7990_stop();
166*58a2b000SEvgeniy Ivanov
167*58a2b000SEvgeniy Ivanov dealloc(dmamem, sizeof(initblock_t) +
168*58a2b000SEvgeniy Ivanov sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
169*58a2b000SEvgeniy Ivanov }
170*58a2b000SEvgeniy Ivanov
171*58a2b000SEvgeniy Ivanov /*
172*58a2b000SEvgeniy Ivanov * Send an ethernet packet
173*58a2b000SEvgeniy Ivanov */
174*58a2b000SEvgeniy Ivanov int
EtherSend(char * pkt,int len)175*58a2b000SEvgeniy Ivanov EtherSend(char *pkt, int len)
176*58a2b000SEvgeniy Ivanov {
177*58a2b000SEvgeniy Ivanov long l;
178*58a2b000SEvgeniy Ivanov u_long addr;
179*58a2b000SEvgeniy Ivanov u_short csr;
180*58a2b000SEvgeniy Ivanov int savlen = len;
181*58a2b000SEvgeniy Ivanov
182*58a2b000SEvgeniy Ivanov if (len < 60)
183*58a2b000SEvgeniy Ivanov len = 60;
184*58a2b000SEvgeniy Ivanov if (len > LANCEBUFSIZE) {
185*58a2b000SEvgeniy Ivanov printf("packet too long\n");
186*58a2b000SEvgeniy Ivanov return -1;
187*58a2b000SEvgeniy Ivanov }
188*58a2b000SEvgeniy Ivanov
189*58a2b000SEvgeniy Ivanov /* set up transmit ring element */
190*58a2b000SEvgeniy Ivanov if (tmd->tmd_flags & TMD_OWN) {
191*58a2b000SEvgeniy Ivanov printf("lesend: td busy, status=%x\n", tmd->tmd_flags);
192*58a2b000SEvgeniy Ivanov return -1;
193*58a2b000SEvgeniy Ivanov }
194*58a2b000SEvgeniy Ivanov addr = LA(pkt);
195*58a2b000SEvgeniy Ivanov if (addr & 1) {
196*58a2b000SEvgeniy Ivanov printf("unaligned data\n");
197*58a2b000SEvgeniy Ivanov return -1;
198*58a2b000SEvgeniy Ivanov }
199*58a2b000SEvgeniy Ivanov tmd->tmd_ladr = (u_short)addr;
200*58a2b000SEvgeniy Ivanov tmd->tmd_hadr = (u_char)(addr >> 16);
201*58a2b000SEvgeniy Ivanov tmd->tmd_bcnt = -len;
202*58a2b000SEvgeniy Ivanov tmd->tmd_err = 0;
203*58a2b000SEvgeniy Ivanov tmd->tmd_flags = TMD_OWN|TMD_STP|TMD_ENP;
204*58a2b000SEvgeniy Ivanov
205*58a2b000SEvgeniy Ivanov /* start transmission */
206*58a2b000SEvgeniy Ivanov outw(LA_CSR, CSR_TDMD);
207*58a2b000SEvgeniy Ivanov
208*58a2b000SEvgeniy Ivanov /* wait for interrupt and acknowledge it */
209*58a2b000SEvgeniy Ivanov for (l = 0; l < MAXLOOP; l++) {
210*58a2b000SEvgeniy Ivanov if ((csr = inw(LA_CSR)) & CSR_TINT) {
211*58a2b000SEvgeniy Ivanov outw(LA_CSR, CSR_TINT);
212*58a2b000SEvgeniy Ivanov #ifdef LEDEBUG
213*58a2b000SEvgeniy Ivanov if (tmd->tmd_flags & (TMD_ONE|TMD_MORE|TMD_ERR|TMD_DEF))
214*58a2b000SEvgeniy Ivanov printf("lesend: status=%x\n", tmd->tmd_flags);
215*58a2b000SEvgeniy Ivanov #endif
216*58a2b000SEvgeniy Ivanov break;
217*58a2b000SEvgeniy Ivanov }
218*58a2b000SEvgeniy Ivanov delay(10); /* don't poll too much on PCI, seems
219*58a2b000SEvgeniy Ivanov to disturb DMA on poor hardware */
220*58a2b000SEvgeniy Ivanov }
221*58a2b000SEvgeniy Ivanov return savlen;
222*58a2b000SEvgeniy Ivanov }
223*58a2b000SEvgeniy Ivanov
224*58a2b000SEvgeniy Ivanov /*
225*58a2b000SEvgeniy Ivanov * Poll the LANCE just see if there's an Ethernet packet
226*58a2b000SEvgeniy Ivanov * available. If there is, its contents is returned.
227*58a2b000SEvgeniy Ivanov */
228*58a2b000SEvgeniy Ivanov int
EtherReceive(char * pkt,int maxlen)229*58a2b000SEvgeniy Ivanov EtherReceive(char *pkt, int maxlen)
230*58a2b000SEvgeniy Ivanov {
231*58a2b000SEvgeniy Ivanov rmde_t *rp;
232*58a2b000SEvgeniy Ivanov u_short csr;
233*58a2b000SEvgeniy Ivanov int len = 0;
234*58a2b000SEvgeniy Ivanov
235*58a2b000SEvgeniy Ivanov csr = inw(LA_CSR);
236*58a2b000SEvgeniy Ivanov outw(LA_CSR, csr & (CSR_BABL | CSR_MISS | CSR_MERR | CSR_RINT));
237*58a2b000SEvgeniy Ivanov
238*58a2b000SEvgeniy Ivanov if ((next_rmd < 0) || (next_rmd >= NRCVRING)) {
239*58a2b000SEvgeniy Ivanov printf("next_rmd bad\n");
240*58a2b000SEvgeniy Ivanov return 0;
241*58a2b000SEvgeniy Ivanov }
242*58a2b000SEvgeniy Ivanov rp = &rmd[next_rmd];
243*58a2b000SEvgeniy Ivanov
244*58a2b000SEvgeniy Ivanov if (rp->rmd_flags & RMD_OWN)
245*58a2b000SEvgeniy Ivanov return 0;
246*58a2b000SEvgeniy Ivanov
247*58a2b000SEvgeniy Ivanov if (csr & (CSR_BABL | CSR_CERR | CSR_MISS | CSR_MERR))
248*58a2b000SEvgeniy Ivanov printf("le: csr %x\n", csr);
249*58a2b000SEvgeniy Ivanov
250*58a2b000SEvgeniy Ivanov if (rp->rmd_flags & (RMD_FRAM | RMD_OFLO | RMD_CRC | RMD_BUFF)) {
251*58a2b000SEvgeniy Ivanov printf("le: rmd_flags %x\n", rp->rmd_flags);
252*58a2b000SEvgeniy Ivanov goto cleanup;
253*58a2b000SEvgeniy Ivanov }
254*58a2b000SEvgeniy Ivanov
255*58a2b000SEvgeniy Ivanov if (rp->rmd_flags != (RMD_STP|RMD_ENP)) {
256*58a2b000SEvgeniy Ivanov printf("le: rmd_flags %x\n", rp->rmd_flags);
257*58a2b000SEvgeniy Ivanov return -1;
258*58a2b000SEvgeniy Ivanov }
259*58a2b000SEvgeniy Ivanov
260*58a2b000SEvgeniy Ivanov len = rp->rmd_mcnt - 4;
261*58a2b000SEvgeniy Ivanov
262*58a2b000SEvgeniy Ivanov if ((len < 0) || (len >= LANCEBUFSIZE)) {
263*58a2b000SEvgeniy Ivanov printf("bad pkt len\n");
264*58a2b000SEvgeniy Ivanov return -1;
265*58a2b000SEvgeniy Ivanov }
266*58a2b000SEvgeniy Ivanov
267*58a2b000SEvgeniy Ivanov if (len <= maxlen)
268*58a2b000SEvgeniy Ivanov memcpy(pkt, rbuffer[next_rmd], len);
269*58a2b000SEvgeniy Ivanov else
270*58a2b000SEvgeniy Ivanov len = 0;
271*58a2b000SEvgeniy Ivanov
272*58a2b000SEvgeniy Ivanov cleanup:
273*58a2b000SEvgeniy Ivanov /* give packet back to the lance */
274*58a2b000SEvgeniy Ivanov rp->rmd_bcnt = -LANCEBUFSIZE;
275*58a2b000SEvgeniy Ivanov rp->rmd_mcnt = 0;
276*58a2b000SEvgeniy Ivanov rp->rmd_flags = RMD_OWN;
277*58a2b000SEvgeniy Ivanov next_rmd = (next_rmd + 1) & (NRCVRING - 1);
278*58a2b000SEvgeniy Ivanov
279*58a2b000SEvgeniy Ivanov return len;
280*58a2b000SEvgeniy Ivanov }
281