1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
|
/*
* ethlance.c -- Low-level Ethernet driver for Lance-based chips
*
* Copyright 1996-97 by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <stdio.h>
#include <string.h>
#include "bootp.h"
#include "ethlance.h"
struct {
volatile unsigned short *memaddr;
volatile unsigned short *ioaddr;
} lance_addr_list[] = {
{ (void *)0xfe010000, (void *)0xfe00fff0 }, /* RieblCard VME in TT */
{ (void *)0xfec10000, (void *)0xfec0fff0 }, /* RieblCard VME in MegaSTE
(highest byte stripped) */
{ (void *)0xfee00000, (void *)0xfeff7000 }, /* RieblCard in ST
(highest byte stripped) */
{ (void *)0xfecf0000, (void *)0xfecffff0 }, /* PAMCard VME in TT and MSTE
(highest byte stripped) */
};
#define N_LANCE_ADDR (sizeof(lance_addr_list)/sizeof(*lance_addr_list))
#define TX_RING_SIZE 1
#define TX_RING_LEN_BITS 0
#define RX_RING_SIZE 16
#define RX_RING_LEN_BITS (4 << 5)
#define offsetof(type,elt) ((unsigned long)(&(((type *)0)->elt)))
/* The LANCE Rx and Tx ring descriptors. */
struct lance_rx_head {
unsigned short base; /* Low word of base addr */
volatile unsigned char flag;
unsigned char base_hi; /* High word of base addr (unused) */
short buf_length; /* This length is 2s complement! */
short msg_length; /* This length is "normal". */
};
struct lance_tx_head {
unsigned short base; /* Low word of base addr */
volatile unsigned char flag;
unsigned char base_hi; /* High word of base addr (unused) */
short length; /* Length is 2s complement! */
volatile short misc;
};
struct ringdesc {
unsigned short adr_lo; /* Low 16 bits of address */
unsigned char len; /* Length bits */
unsigned char adr_hi; /* High 8 bits of address (unused) */
};
struct lance_packet {
volatile unsigned char data[PKTLEN];
};
/* The LANCE initialization block, described in databook. */
struct lance_init_block {
unsigned short mode; /* Pre-set mode */
unsigned char hwaddr[6]; /* Physical ethernet address */
unsigned filter[2]; /* Multicast filter (unused). */
/* Receive and transmit ring base, along with length bits. */
struct ringdesc rx_ring;
struct ringdesc tx_ring;
};
/* The whole layout of the Lance shared memory */
struct lance_memory {
struct lance_init_block init;
struct lance_tx_head tx_head[TX_RING_SIZE];
struct lance_rx_head rx_head[RX_RING_SIZE];
struct lance_packet tx_packet[TX_RING_SIZE];
struct lance_packet rx_packet[TX_RING_SIZE];
};
#define RIEBL_MAGIC 0x09051990
#define RIEBL_MAGIC_ADDR ((unsigned long *)(((char *)MEM) + 0xee8a))
#define RIEBL_HWADDR_ADDR ((unsigned char *)(((char *)MEM) + 0xee8e))
#define RIEBL_IVEC_ADDR ((unsigned short *)(((char *)MEM) + 0xfffe))
struct lance_ioreg {
/* base+0x0 */ volatile unsigned short data;
/* base+0x2 */ volatile unsigned short addr;
unsigned char _dummy1[3];
/* base+0x7 */ volatile unsigned char ivec;
unsigned char _dummy2[5];
/* base+0xd */ volatile unsigned char eeprom;
unsigned char _dummy3;
/* base+0xf */ volatile unsigned char mem;
};
enum lance_type {
OLD_RIEBL, /* old Riebl card without battery */
NEW_RIEBL, /* new Riebl card with battery */
PAM_CARD /* PAM card with EEPROM */
} CardType;
HWADDR dev_addr;
/* This is a default address for the old RieblCards without a battery
* that have no ethernet address at boot time. 00:00:36:04 is the
* prefix for Riebl cards, the 00:00 at the end is arbitrary.
*/
HWADDR OldRieblDefHwaddr = {
0x00, 0x00, 0x36, 0x04, 0x00, 0x00
};
struct lance_ioreg *IO;
struct lance_memory *MEM;
#define DREG IO->data
#define AREG IO->addr
#define REGA(a) ( AREG = (a), DREG )
int CurRx;
/* Definitions for the Lance */
/* tx_head flags */
#define TMD1_ENP 0x01
#define TMD1_STP 0x02
#define TMD1_DEF 0x04
#define TMD1_ONE 0x08
#define TMD1_MORE 0x10
#define TMD1_ERR 0x40
#define TMD1_OWN 0x80
#define TMD1_OWN_CHIP TMD1_OWN
#define TMD1_OWN_HOST 0
/* tx_head misc field */
#define TMD3_TDR 0x03FF
#define TMD3_RTRY 0x0400
#define TMD3_LCAR 0x0800
#define TMD3_LCOL 0x1000
#define TMD3_UFLO 0x4000
#define TMD3_BUFF3 0x8000
/* rx_head flags */
#define RMD1_ENP 0x01
#define RMD1_STP 0x02
#define RMD1_BUFF 0x04
#define RMD1_CRC 0x08
#define RMD1_OFLO 0x10
#define RMD1_FRAM 0x20
#define RMD1_ERR 0x40
#define RMD1_OWN 0x80
#define RMD1_OWN_CHIP RMD1_OWN
#define RMD1_OWN_HOST 0
/* register names */
#define CSR0 0
#define CSR1 1
#define CSR2 2
#define CSR3 3
/* CSR0 */
#define CSR0_INIT 0x0001 /* initialize */
#define CSR0_STRT 0x0002 /* start */
#define CSR0_STOP 0x0004 /* stop */
#define CSR0_TDMD 0x0008 /* transmit demand */
#define CSR0_TXON 0x0010 /* transmitter on */
#define CSR0_RXON 0x0020 /* receiver on */
#define CSR0_INEA 0x0040 /* interrupt enable */
#define CSR0_INTR 0x0080 /* interrupt active */
#define CSR0_IDON 0x0100 /* initialization done */
#define CSR0_TINT 0x0200 /* transmitter interrupt */
#define CSR0_RINT 0x0400 /* receiver interrupt */
#define CSR0_MERR 0x0800 /* memory error */
#define CSR0_MISS 0x1000 /* missed frame */
#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) */
#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits */
#define CSR0_ERR 0x8000 /* error */
/* CSR3 */
#define CSR3_BCON 0x0001
#define CSR3_ACON 0x0002
#define CSR3_BSWP 0x0004
#define HZ 200
#define _hz_200 (*(volatile unsigned long *)0x4ba)
/***************************** Prototypes *****************************/
static int lance_probe( void );
static int addr_readable( volatile void *regp, int wordflag );
static int lance_init( void );
static void lance_get_hwaddr( HWADDR *addr );
static int lance_snd( Packet *pkt, int len );
static int lance_rcv( Packet *pkt, int *len );
/************************* End of Prototypes **************************/
ETHIF_SWITCH LanceSwitch = {
lance_probe, lance_init, lance_get_hwaddr,
lance_snd, lance_rcv
};
static int lance_probe( void )
{ int i;
for( i = 0; i < N_LANCE_ADDR; ++i ) {
if (addr_readable( lance_addr_list[i].memaddr, 1 ) &&
(lance_addr_list[i].memaddr[0] = 1,
lance_addr_list[i].memaddr[0] == 1) &&
(lance_addr_list[i].memaddr[0] = 0,
lance_addr_list[i].memaddr[0] == 0) &&
addr_readable( lance_addr_list[i].ioaddr, 1 )) {
break;
}
}
if (i == N_LANCE_ADDR) return( -1 );
IO = (struct lance_ioreg *)lance_addr_list[i].ioaddr;
MEM = (struct lance_memory *)lance_addr_list[i].memaddr;
REGA( CSR0 ) = CSR0_STOP;
return( 0 );
}
static int addr_readable( volatile void *regp, int wordflag )
{ int ret;
long *vbr, save_berr;
__asm__ __volatile__ ( "movec %/vbr,%0" : "=r" (vbr) : );
save_berr = vbr[2];
__asm__ __volatile__
( "movel %/sp,%/d1\n\t"
"movel #Lberr,%2@\n\t"
"moveq #0,%0\n\t"
"tstl %3\n\t"
"bne 1f\n\t"
"tstb %1@\n\t"
"bra 2f\n"
"1: tstw %1@\n"
"2: moveq #1,%0\n"
"Lberr: movel %/d1,%/sp"
: "=&d" (ret)
: "a" (regp), "a" (&vbr[2]), "rm" (wordflag)
: "d1", "memory"
);
vbr[2] = save_berr;
return( ret );
}
static int lance_init( void )
{ int i;
/* Now test for type: If the eeprom I/O port is readable, it is a
* PAM card */
if (addr_readable( &(IO->eeprom), 0 )) {
/* Switch back to Ram */
i = IO->mem;
CardType = PAM_CARD;
}
else if (*RIEBL_MAGIC_ADDR == RIEBL_MAGIC) {
CardType = NEW_RIEBL;
}
else
CardType = OLD_RIEBL;
/* Get the ethernet address */
switch( CardType ) {
case OLD_RIEBL:
/* No ethernet address! (Set some default address) */
memcpy( dev_addr, OldRieblDefHwaddr, ETHADDRLEN );
break;
case NEW_RIEBL:
memcpy( dev_addr, RIEBL_HWADDR_ADDR, ETHADDRLEN );
break;
case PAM_CARD:
i = IO->eeprom;
for( i = 0; i < ETHADDRLEN; ++i )
dev_addr[i] =
((((unsigned short *)MEM)[i*2] & 0x0f) << 4) |
((((unsigned short *)MEM)[i*2+1] & 0x0f));
i = IO->mem;
break;
}
MEM->init.mode = 0x0000; /* Disable Rx and Tx. */
for( i = 0; i < ETHADDRLEN; i++ )
MEM->init.hwaddr[i] = dev_addr[i^1]; /* <- 16 bit swap! */
MEM->init.filter[0] = 0x00000000;
MEM->init.filter[1] = 0x00000000;
MEM->init.rx_ring.adr_lo = offsetof( struct lance_memory, rx_head );
MEM->init.rx_ring.adr_hi = 0;
MEM->init.rx_ring.len = RX_RING_LEN_BITS;
MEM->init.tx_ring.adr_lo = offsetof( struct lance_memory, tx_head );
MEM->init.tx_ring.adr_hi = 0;
MEM->init.tx_ring.len = TX_RING_LEN_BITS;
REGA( CSR3 ) = CSR3_BSWP | (CardType == PAM_CARD ? CSR3_ACON : 0);
REGA( CSR2 ) = 0;
REGA( CSR1 ) = 0;
REGA( CSR0 ) = CSR0_INIT | CSR0_STRT;
i = 1000000;
while( i-- > 0 )
if (DREG & CSR0_IDON)
break;
if (i < 0 || (DREG & CSR0_ERR)) {
DREG = CSR0_STOP;
return( -1 );
}
DREG = CSR0_IDON;
for (i = 0; i < TX_RING_SIZE; i++) {
MEM->tx_head[i].base = offsetof( struct lance_memory, tx_packet[i] );
MEM->tx_head[i].flag = TMD1_OWN_HOST;
MEM->tx_head[i].base_hi = 0;
MEM->tx_head[i].length = 0;
MEM->tx_head[i].misc = 0;
}
for (i = 0; i < RX_RING_SIZE; i++) {
MEM->rx_head[i].base = offsetof( struct lance_memory, rx_packet[i] );
MEM->rx_head[i].flag = TMD1_OWN_CHIP;
MEM->rx_head[i].base_hi = 0;
MEM->rx_head[i].buf_length = -PKTLEN;
MEM->rx_head[i].msg_length = 0;
}
CurRx = 0;
return( 0 );
}
static void lance_get_hwaddr( HWADDR *addr )
{
memcpy( addr, dev_addr, ETHADDRLEN );
}
static int lance_snd( Packet *pkt, int len )
{ unsigned long timeout;
/* The old LANCE chips doesn't automatically pad buffers to min. size. */
len = (len < 60) ? 60 : len;
/* PAM-Card has a bug: Can only send packets with even number of bytes! */
if (CardType == PAM_CARD && (len & 1))
++len;
MEM->tx_head[0].length = -len;
MEM->tx_head[0].misc = 0;
memcpy( (void *)&MEM->tx_packet[0].data, pkt, len );
MEM->tx_head[0].base = offsetof(struct lance_memory, tx_packet[0]);
MEM->tx_head[0].base_hi = 0;
MEM->tx_head[0].flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
/* Trigger an immediate send poll. */
REGA( CSR0 ) = CSR0_TDMD;
/* Wait for packet being sent */
timeout = _hz_200 + 3*HZ;
while( (MEM->tx_head[0].flag & TMD1_OWN_CHIP) &&
!MEM->tx_head[0].misc &&
_hz_200 < timeout )
;
if ((MEM->tx_head[0].flag & TMD1_OWN) == TMD1_OWN_HOST &&
!(MEM->tx_head[0].misc & TMD1_ERR))
/* sent ok */
return( 0 );
/* failure */
if (_hz_200 >= timeout)
return( ETIMEO );
if (MEM->tx_head[0].misc & TMD3_UFLO) {
/* On FIFO errors, must re-turn on TX! */
DREG = CSR0_STRT;
}
return( ESEND );
}
static int lance_rcv( Packet *pkt, int *len )
{ unsigned long timeout;
int stat;
/* Wait for a packet */
timeout = _hz_200 + 4*HZ;
while( (MEM->rx_head[CurRx].flag & TMD1_OWN_CHIP) &&
_hz_200 < timeout )
;
/* Not ours -> was a timeout */
if (((stat = MEM->rx_head[CurRx].flag) & TMD1_OWN) == TMD1_OWN_CHIP)
return( ETIMEO );
/* Check for errors */
if (stat != (RMD1_ENP|RMD1_STP)) {
MEM->rx_head[CurRx].flag &= (RMD1_ENP|RMD1_STP);
if (stat & RMD1_FRAM) return( EFRAM );
if (stat & RMD1_OFLO) return( EOVERFL );
if (stat & RMD1_CRC) return( ECRC );
return( ERCV );
}
/* Get the packet */
*len = MEM->rx_head[CurRx].msg_length & 0xfff;
memcpy( pkt, (void *)&MEM->rx_packet[CurRx].data, *len );
/* Give the buffer back to the chip */
MEM->rx_head[CurRx].buf_length = -PKTLEN;
MEM->rx_head[CurRx].flag |= RMD1_OWN_CHIP;
CurRx = (CurRx + 1) % RX_RING_SIZE;
return( 0 );
}
/* Local Variables: */
/* tab-width: 4 */
/* End: */
|