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