patch-2.0.21-2.1.0 linux/arch/m68k/boot/atari/ethlance.c
Next file: linux/arch/m68k/boot/atari/ethlance.h
Previous file: linux/arch/m68k/boot/atari/bootstrap.c
Back to the patch index
Back to the overall index
- Lines: 436
- Date:
Wed Sep 25 10:47:39 1996
- Orig file:
lx2.0/v2.0.21/linux/arch/m68k/boot/atari/ethlance.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file lx2.0/v2.0.21/linux/arch/m68k/boot/atari/ethlance.c linux/arch/m68k/boot/atari/ethlance.c
@@ -0,0 +1,435 @@
+
+#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 );
+}
+
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov