1 /* $NetBSD: am7990.c,v 1.7 2008/12/14 18:46:33 christos Exp $ */
3 /* mostly from netbsd:sys/arch/i386/netboot/ne2100.c
4 memory allocation now 1 chunk, added deallocation
5 receive function changed - don't use irq
9 * source in this file came from
10 * the Mach ethernet boot written by Leendert van Doorn.
12 * A very simple network driver for NE2100 boards that polls.
14 * Copyright (c) 1992 by Leendert van Doorn
17 #include <sys/types.h>
18 #include <machine/pio.h>
19 #include <lib/libkern/libkern.h>
20 #include <lib/libsa/stand.h>
27 extern u_char eth_myaddr
[6];
29 extern int lance_rap
, lance_rdp
;
33 #define LA(adr) vtophys(adr)
35 /* Lance register offsets */
36 #define LA_CSR lance_rdp
37 #define LA_CSR1 lance_rdp
38 #define LA_CSR2 lance_rdp
39 #define LA_CSR3 lance_rdp
40 #define LA_RAP lance_rap
43 * Some driver specific constants.
44 * Take care when tuning, this program only has 32 Kb
46 #define LANCEBUFSIZE 1518 /* plus 4 CRC bytes */
47 #define MAXLOOP 1000000L /* arbitrary retry limit */
48 #define LOG2NRCVRING 2 /* log2(NRCVRING) */
49 #define NRCVRING (1 << LOG2NRCVRING)
51 static int next_rmd
; /* next receive element */
52 static initblock_t
*initblock
; /* initialization block */
53 static tmde_t
*tmd
; /* transmit ring */
54 static rmde_t
*rmd
; /* receive ring */
55 static char rbuffer
[NRCVRING
][LANCEBUFSIZE
]; /* receive buffers */
65 /* stop chip and disable DMA access */
66 outw(LA_RAP
, RDP_CSR0
);
67 outw(LA_CSR
, CSR_STOP
);
68 for (l
= 0; (inw(LA_CSR
) & CSR_STOP
) == 0; l
++) {
70 printf("Lance failed to stop\n");
77 * Reset ethernet board
86 /* initblock, tmd, and rmd should be 8 byte aligned;
87 sizes of initblock_t and tmde_t are multiples of 8 */
88 dmamem
= alloc(sizeof(initblock_t
) +
89 sizeof(tmde_t
) + NRCVRING
* sizeof(rmde_t
) + 4);
90 /* +4 is ok because alloc()'s result is 4-byte aligned! */
92 initblock
= (initblock_t
*)(((unsigned long)dmamem
+ 4) & -8);
93 tmd
= (tmde_t
*)(initblock
+ 1);
94 rmd
= (rmde_t
*)(tmd
+ 1);
96 /* stop the chip, and make sure it did */
99 /* fill lance initialization block */
100 memset(initblock
, 0, sizeof(initblock_t
));
102 /* set my ethernet address */
103 for (i
= 0; i
< 6; i
++)
104 initblock
->ib_padr
[i
] = eth_myaddr
[i
];
106 /* receive ring pointer */
108 initblock
->ib_rdralow
= (u_short
)addr
;
109 initblock
->ib_rdrahigh
= (u_char
)(addr
>> 16);
110 initblock
->ib_rlen
= LOG2NRCVRING
<< 5;
112 /* transmit ring with one element */
114 initblock
->ib_tdralow
= (u_short
)addr
;
115 initblock
->ib_tdrahigh
= (u_char
)(addr
>> 16);
116 initblock
->ib_tlen
= 0 << 5;
118 /* setup the receive ring entries */
119 for (next_rmd
= 0, i
= 0; i
< NRCVRING
; i
++) {
120 addr
= LA(&rbuffer
[i
]);
121 rmd
[i
].rmd_ladr
= (u_short
)addr
;
122 rmd
[i
].rmd_hadr
= (u_char
)(addr
>> 16);
124 rmd
[i
].rmd_bcnt
= -LANCEBUFSIZE
;
125 rmd
[i
].rmd_flags
= RMD_OWN
;
128 /* zero transmit ring */
129 memset(tmd
, 0, sizeof(tmde_t
));
131 /* give lance the init block */
132 addr
= LA(initblock
);
133 outw(LA_RAP
, RDP_CSR1
);
134 outw(LA_CSR1
, (u_short
)addr
);
135 outw(LA_RAP
, RDP_CSR2
);
136 outw(LA_CSR2
, (char)(addr
>> 16));
137 outw(LA_RAP
, RDP_CSR3
);
140 /* and initialize it */
141 outw(LA_RAP
, RDP_CSR0
);
142 outw(LA_CSR
, CSR_INIT
|CSR_STRT
);
144 /* wait for the lance to complete initialization and fire it up */
145 for (l
= 0; (inw(LA_CSR
) & CSR_IDON
) == 0; l
++) {
147 printf("Lance failed to initialize\n");
151 for (l
= 0; (inw(LA_CSR
)&(CSR_TXON
|CSR_RXON
)) != (CSR_TXON
|CSR_RXON
); l
++) {
153 printf("Lance not started\n");
160 * Stop ethernet board and free ressources
167 dealloc(dmamem
, sizeof(initblock_t
) +
168 sizeof(tmde_t
) + NRCVRING
* sizeof(rmde_t
) + 4);
172 * Send an ethernet packet
175 EtherSend(char *pkt
, int len
)
184 if (len
> LANCEBUFSIZE
) {
185 printf("packet too long\n");
189 /* set up transmit ring element */
190 if (tmd
->tmd_flags
& TMD_OWN
) {
191 printf("lesend: td busy, status=%x\n", tmd
->tmd_flags
);
196 printf("unaligned data\n");
199 tmd
->tmd_ladr
= (u_short
)addr
;
200 tmd
->tmd_hadr
= (u_char
)(addr
>> 16);
201 tmd
->tmd_bcnt
= -len
;
203 tmd
->tmd_flags
= TMD_OWN
|TMD_STP
|TMD_ENP
;
205 /* start transmission */
206 outw(LA_CSR
, CSR_TDMD
);
208 /* wait for interrupt and acknowledge it */
209 for (l
= 0; l
< MAXLOOP
; l
++) {
210 if ((csr
= inw(LA_CSR
)) & CSR_TINT
) {
211 outw(LA_CSR
, CSR_TINT
);
213 if (tmd
->tmd_flags
& (TMD_ONE
|TMD_MORE
|TMD_ERR
|TMD_DEF
))
214 printf("lesend: status=%x\n", tmd
->tmd_flags
);
218 delay(10); /* don't poll too much on PCI, seems
219 to disturb DMA on poor hardware */
225 * Poll the LANCE just see if there's an Ethernet packet
226 * available. If there is, its contents is returned.
229 EtherReceive(char *pkt
, int maxlen
)
236 outw(LA_CSR
, csr
& (CSR_BABL
| CSR_MISS
| CSR_MERR
| CSR_RINT
));
238 if ((next_rmd
< 0) || (next_rmd
>= NRCVRING
)) {
239 printf("next_rmd bad\n");
244 if (rp
->rmd_flags
& RMD_OWN
)
247 if (csr
& (CSR_BABL
| CSR_CERR
| CSR_MISS
| CSR_MERR
))
248 printf("le: csr %x\n", csr
);
250 if (rp
->rmd_flags
& (RMD_FRAM
| RMD_OFLO
| RMD_CRC
| RMD_BUFF
)) {
251 printf("le: rmd_flags %x\n", rp
->rmd_flags
);
255 if (rp
->rmd_flags
!= (RMD_STP
|RMD_ENP
)) {
256 printf("le: rmd_flags %x\n", rp
->rmd_flags
);
260 len
= rp
->rmd_mcnt
- 4;
262 if ((len
< 0) || (len
>= LANCEBUFSIZE
)) {
263 printf("bad pkt len\n");
268 memcpy(pkt
, rbuffer
[next_rmd
], len
);
273 /* give packet back to the lance */
274 rp
->rmd_bcnt
= -LANCEBUFSIZE
;
276 rp
->rmd_flags
= RMD_OWN
;
277 next_rmd
= (next_rmd
+ 1) & (NRCVRING
- 1);