1/* $NetBSD: am7990.c,v 1.6 2007/03/04 05:59:59 christos 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 27extern u_char eth_myaddr[6]; 28 29extern int lance_rap, lance_rdp; 30 31static void *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 51static int next_rmd; /* next receive element */ 52static initblock_t *initblock; /* initialization block */ 53static tmde_t *tmd; /* transmit ring */ 54static rmde_t *rmd; /* receive ring */ 55static char rbuffer[NRCVRING][LANCEBUFSIZE]; /* receive buffers */ 56 57/* 58 * Stop ethernet board 59 */ 60void 61am7990_stop(void) 62{ 63 long l; 64 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++) { 69 if (l >= MAXLOOP) { 70 printf("Lance failed to stop\n"); 71 return; 72 } 73 } 74} 75 76/* 77 * Reset ethernet board 78 */ 79void 80am7990_init(void) 81{ 82 long l; 83 u_long addr; 84 int i; 85 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! */ 91 92 initblock = (initblock_t *)(((unsigned long)dmamem + 4) & -8); 93 tmd = (tmde_t *)(initblock + 1); 94 rmd = (rmde_t *)(tmd + 1); 95 96 /* stop the chip, and make sure it did */ 97 am7990_stop(); 98 99 /* fill lance initialization block */ 100 memset(initblock, 0, sizeof(initblock_t)); 101 102 /* set my ethernet address */ 103 for (i = 0; i < 6; i++) 104 initblock->ib_padr[i] = eth_myaddr[i]; 105 106 /* receive ring pointer */ 107 addr = LA(rmd); 108 initblock->ib_rdralow = (u_short)addr; 109 initblock->ib_rdrahigh = (u_char)(addr >> 16); 110 initblock->ib_rlen = LOG2NRCVRING << 5; 111 112 /* transmit ring with one element */ 113 addr = LA(tmd); 114 initblock->ib_tdralow = (u_short)addr; 115 initblock->ib_tdrahigh = (u_char)(addr >> 16); 116 initblock->ib_tlen = 0 << 5; 117 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); 123 rmd[i].rmd_mcnt = 0; 124 rmd[i].rmd_bcnt = -LANCEBUFSIZE; 125 rmd[i].rmd_flags = RMD_OWN; 126 } 127 128 /* zero transmit ring */ 129 memset(tmd, 0, sizeof(tmde_t)); 130 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); 138 outw(LA_CSR3, 0); 139 140 /* and initialize it */ 141 outw(LA_RAP, RDP_CSR0); 142 outw(LA_CSR, CSR_INIT|CSR_STRT); 143 144 /* wait for the lance to complete initialization and fire it up */ 145 for (l = 0; (inw(LA_CSR) & CSR_IDON) == 0; l++) { 146 if (l >= MAXLOOP) { 147 printf("Lance failed to initialize\n"); 148 break; 149 } 150 } 151 for (l = 0; (inw(LA_CSR)&(CSR_TXON|CSR_RXON)) != (CSR_TXON|CSR_RXON); l++) { 152 if (l >= MAXLOOP) { 153 printf("Lance not started\n"); 154 break; 155 } 156 } 157} 158 159/* 160 * Stop ethernet board and free ressources 161 */ 162void 163EtherStop(void) 164{ 165 am7990_stop(); 166 167 dealloc(dmamem, sizeof(initblock_t) + 168 sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4); 169} 170 171/* 172 * Send an ethernet packet 173 */ 174int 175EtherSend(char *pkt, int len) 176{ 177 long l; 178 u_long addr; 179 u_short csr; 180 int savlen = len; 181 182 if (len < 60) 183 len = 60; 184 if (len > LANCEBUFSIZE) { 185 printf("packet too long\n"); 186 return -1; 187 } 188 189 /* set up transmit ring element */ 190 if (tmd->tmd_flags & TMD_OWN) { 191 printf("lesend: td busy, status=%x\n", tmd->tmd_flags); 192 return -1; 193 } 194 addr = LA(pkt); 195 if (addr & 1) { 196 printf("unaligned data\n"); 197 return -1; 198 } 199 tmd->tmd_ladr = (u_short)addr; 200 tmd->tmd_hadr = (u_char)(addr >> 16); 201 tmd->tmd_bcnt = -len; 202 tmd->tmd_err = 0; 203 tmd->tmd_flags = TMD_OWN|TMD_STP|TMD_ENP; 204 205 /* start transmission */ 206 outw(LA_CSR, CSR_TDMD); 207 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); 212#ifdef LEDEBUG 213 if (tmd->tmd_flags & (TMD_ONE|TMD_MORE|TMD_ERR|TMD_DEF)) 214 printf("lesend: status=%x\n", tmd->tmd_flags); 215#endif 216 break; 217 } 218 delay(10); /* don't poll too much on PCI, seems 219 to disturb DMA on poor hardware */ 220 } 221 return savlen; 222} 223 224/* 225 * Poll the LANCE just see if there's an Ethernet packet 226 * available. If there is, its contents is returned. 227 */ 228int 229EtherReceive(char *pkt, int maxlen) 230{ 231 rmde_t *rp; 232 u_short csr; 233 int len = 0; 234 235 csr = inw(LA_CSR); 236 outw(LA_CSR, csr & (CSR_BABL | CSR_MISS | CSR_MERR | CSR_RINT)); 237 238 if ((next_rmd < 0) || (next_rmd >= NRCVRING)) { 239 printf("next_rmd bad\n"); 240 return 0; 241 } 242 rp = &rmd[next_rmd]; 243 244 if (rp->rmd_flags & RMD_OWN) 245 return 0; 246 247 if (csr & (CSR_BABL | CSR_CERR | CSR_MISS | CSR_MERR)) 248 printf("le: csr %x\n", csr); 249 250 if (rp->rmd_flags & (RMD_FRAM | RMD_OFLO | RMD_CRC | RMD_BUFF)) { 251 printf("le: rmd_flags %x\n", rp->rmd_flags); 252 goto cleanup; 253 } 254 255 if (rp->rmd_flags != (RMD_STP|RMD_ENP)) { 256 printf("le: rmd_flags %x\n", rp->rmd_flags); 257 return -1; 258 } 259 260 len = rp->rmd_mcnt - 4; 261 262 if ((len < 0) || (len >= LANCEBUFSIZE)) { 263 printf("bad pkt len\n"); 264 return -1; 265 } 266 267 if (len <= maxlen) 268 memcpy(pkt, rbuffer[next_rmd], len); 269 else 270 len = 0; 271 272 cleanup: 273 /* give packet back to the lance */ 274 rp->rmd_bcnt = -LANCEBUFSIZE; 275 rp->rmd_mcnt = 0; 276 rp->rmd_flags = RMD_OWN; 277 next_rmd = (next_rmd + 1) & (NRCVRING - 1); 278 279 return len; 280} 281