Subject: BVME410 ethernet driver
To: None <port-atari@netbsd.org>
From: maximum entropy <entropy@zippy.bernstein.com>
List: port-atari
Date: 12/02/1998 08:26:09
Here is the driver for the BVME410 lance ethernet card.  Yeah, I know
I promised this about a year ago...but I just found and fixed the last
bug that was driving me nuts.  So here it is.

The BVME410 comes in three different memory sizes, and with or without
a coax connector (for a total of 6 different models).  This code was
developed on the fanciest model (256k, with coax) but with luck it
should work on any model.

The card is manufactured by:
	BVM Limited
	Hobb Lane
	Hedge End
	Southampton
	SO03 OGH
	Tel 0703 270770

I bought mine from:
	Bill West Incorporated
	887D Main Street
	Monroe, CT 06468
	Tel 203-261-6027

BWI has a web page with some specs at:
	http://bwi.com/58data_3u.html

Mine cost me a little over $1000 US, but I expect the simpler models
are somewhat cheaper.  I also bought it about a year ago -- it's
possible that the prices have changed significantly.

I have no affiliation with either BVM or BWI.  However, I would like
to give special thanks to Robert Kulawiec (robert@bwi.com).  When I
was deciding whether or not I should risk purchasing the card, knowing
that I'd be on my own when writing the driver, Robert faxed me the
programming information from the card's manual without even blinking
an eye.  It was a great pleasure to do business with a hardware dealer
who wasn't fazed by the idea that I wouldn't be using his product on a
Windoze box (or even with one of the systems BWI sells that has
drivers for the card).

One more thing...I haven't tested this driver in the presence of
either a PAM or Riebel card (if I had one of them, I wouldn't have
written this driver).  If anyone with those cards has trouble, let me
know.  I can probably tighten up any loose ends in the match/attach
routines if need be.

Enough BS...here's the data.  Merry holidays to all...

--- /sys/dev/ic/lance.c-orig	Sat Aug 15 07:13:29 1998
+++ /sys/dev/ic/lance.c	Wed Dec  2 03:13:11 1998
@@ -268,6 +268,10 @@
 		sc->sc_nrbuf = 64;
 		sc->sc_ntbuf = 16;
 		break;
+	case 262144:
+		sc->sc_nrbuf = 128;
+		sc->sc_ntbuf = 32;
+		break;
 	default:
 		panic("lance_config: weird memory size");
 	}
--- /sys/arch/atari/vme/if_le_vme.c-orig	Wed Jul 22 07:15:35 1998
+++ /sys/arch/atari/vme/if_le_vme.c	Wed Dec  2 03:29:09 1998
@@ -75,25 +75,26 @@
 #include <atari/vme/vmevar.h>
 #include <atari/vme/if_levar.h>
 
+/*
+ * All cards except BVME410 have 64KB RAM. However.... On the Riebl cards the
+ * area between the offsets 0xee70-0xeec0 is used to store config data.
+ */
 struct le_addresses {
 	u_long	reg_addr;
 	u_long	mem_addr;
 	int	irq;
+	int	reg_size;
+	int	mem_size;
 } lestd[] = {
-	{ 0xfe00fff0, 0xfe010000, IRQUNK },	/* Riebl VME	*/
-	{ 0xffcffff0, 0xffcf0000,      5 },	/* PAM VME	*/
-	{ 0xfecffff0, 0xfecf0000,      5 }	/* Rhotron VME	*/
+	{ 0xfe00fff0, 0xfe010000, IRQUNK, 16, 64*1024 }, /* Riebl	*/
+	{ 0xffcffff0, 0xffcf0000,      5, 16, 64*1024 }, /* PAM		*/
+	{ 0xfecffff0, 0xfecf0000,      5, 16, 64*1024 }, /* Rhotron	*/
+	{ 0xfeff4100, 0xfe000000,      4,  8, VMECF_MEMSIZ_DEFAULT } /*BVME410*/
 };
 
 #define	NLESTD	(sizeof(lestd) / sizeof(lestd[0]))
 
 /*
- * All cards have 64KB RAM. However.... On the Riebl cards the area
- * between the offsets 0xee70-0xeec0 is used to store config data.
- */
-#define	MEMSIZE	(64*1024)
-
-/*
  * Default mac for RIEBL cards without a (working) battery. The first 4 bytes
  * are the manufacturer id.
  */
@@ -108,6 +109,10 @@
 static int probe_addresses __P((bus_space_tag_t *, bus_space_tag_t *,
 				bus_space_handle_t *, bus_space_handle_t *));
 static void riebl_skip_reserved_area __P((struct lance_softc *));
+static int nm93c06_read __P((bus_space_tag_t, bus_space_handle_t, int));
+static int bvme410_mem_size __P((bus_space_tag_t, u_long));
+static void bvme410_copytobuf __P((struct lance_softc *, void *, int, int));
+static void bvme410_zerobuf __P((struct lance_softc *, int, int));
 
 struct cfattach le_vme_ca = {
 	sizeof(struct le_softc), le_vme_match, le_vme_attach
@@ -190,24 +195,35 @@
 		if ((le_ap->irq != IRQUNK) && (va->va_irq != le_ap->irq))
 			continue;
 
-		if (bus_space_map(iot, le_ap->reg_addr, 16, 0, &ioh)) {
+		if (bus_space_map(iot, le_ap->reg_addr, le_ap->reg_size, 0, &ioh)) {
 			printf("leprobe: cannot map io-area\n");
 			return (0);
 		}
-		if (bus_space_map(memt, le_ap->mem_addr, MEMSIZE, 0, &memh)) {
-			bus_space_unmap(iot, (caddr_t)le_ap->reg_addr, 16);
+		if (le_ap->mem_size == VMECF_MEMSIZ_DEFAULT) {
+			if (bus_space_peek_2(iot, ioh, BVME410_IVEC)) {
+				bus_space_write_2(iot, ioh, BVME410_BAR, 0x1); /* XXX */
+				le_ap->mem_size = bvme410_mem_size(memt, le_ap->mem_addr);
+			}
+		}
+		if (le_ap->mem_size == VMECF_MEMSIZ_DEFAULT) {
+			bus_space_unmap(iot, (caddr_t)le_ap->reg_addr, le_ap->reg_size);
+			continue;
+		}
+
+		if (bus_space_map(memt, le_ap->mem_addr, le_ap->mem_size, 0, &memh)) {
+			bus_space_unmap(iot, (caddr_t)le_ap->reg_addr, le_ap->reg_size);
 			printf("leprobe: cannot map memory-area\n");
 			return (0);
 		}
 		found = probe_addresses(&iot, &memt, &ioh, &memh);
-		bus_space_unmap(iot, (caddr_t)le_ap->reg_addr, 16);
-		bus_space_unmap(memt, (caddr_t)le_ap->mem_addr, 8*NBPG);
+		bus_space_unmap(iot, (caddr_t)le_ap->reg_addr, le_ap->reg_size);
+		bus_space_unmap(memt, (caddr_t)le_ap->mem_addr, le_ap->mem_size);
 
 		if (found) {
 			va->va_iobase = le_ap->reg_addr;
-			va->va_iosize = 16;
+			va->va_iosize = le_ap->reg_size;
 			va->va_maddr  = le_ap->mem_addr;
-			va->va_msize  = MEMSIZE;
+			va->va_msize  = le_ap->mem_size;
 			if (va->va_irq == IRQUNK)
 				va->va_irq = le_ap->irq;
 			return 1;
@@ -333,6 +349,10 @@
 		lesc->sc_type = LE_PAM;
 		bus_space_read_1(va->va_iot, ioh, LER_MEME);
 	}
+	else if (bus_space_peek_2(va->va_iot, ioh, BVME410_IVEC)) {
+		printf("BVME410");
+		lesc->sc_type = LE_BVME410;
+	}
 	else {
 		printf("Riebl card");
 		if(bus_space_read_4(va->va_memt, memh, RIEBL_MAGIC_ADDR)
@@ -344,11 +364,22 @@
 		}
 	}
 
-	sc->sc_copytodesc   = lance_copytobuf_contig;
-	sc->sc_copyfromdesc = lance_copyfrombuf_contig;
-	sc->sc_copytobuf    = lance_copytobuf_contig;
-	sc->sc_copyfrombuf  = lance_copyfrombuf_contig;
-	sc->sc_zerobuf      = lance_zerobuf_contig;
+	switch (lesc->sc_type) {
+	    case LE_BVME410:
+		sc->sc_copytodesc   = bvme410_copytobuf;
+		sc->sc_copyfromdesc = lance_copyfrombuf_contig;
+		sc->sc_copytobuf    = bvme410_copytobuf;
+		sc->sc_copyfrombuf  = lance_copyfrombuf_contig;
+		sc->sc_zerobuf      = bvme410_zerobuf;
+		break;
+	    default:
+		sc->sc_copytodesc   = lance_copytobuf_contig;
+		sc->sc_copyfromdesc = lance_copyfrombuf_contig;
+		sc->sc_copytobuf    = lance_copytobuf_contig;
+		sc->sc_copyfrombuf  = lance_copyfrombuf_contig;
+		sc->sc_zerobuf      = lance_zerobuf_contig;
+		break;
+	}
 
 	sc->sc_rdcsr   = lerdcsr;
 	sc->sc_wrcsr   = lewrcsr;
@@ -380,6 +411,15 @@
 		}
 		i = bus_space_read_1(va->va_iot, ioh, LER_MEME);
 		break;
+	    case LE_BVME410:
+		for (i = 0; i < (sizeof(sc->sc_enaddr) >> 1); i++) {
+		    u_int16_t tmp;
+
+		    tmp = nm93c06_read(va->va_iot, ioh, i);
+		    sc->sc_enaddr[2 * i] = (tmp >> 8) & 0xff;
+		    sc->sc_enaddr[2 * i + 1] = tmp & 0xff;
+		}
+		bus_space_write_2(va->va_iot, ioh, BVME410_BAR, 0x1); /* XXX */
 	}
 
 	am7990_config(&lesc->sc_am7990);
@@ -408,6 +448,9 @@
 		case LE_PAM:
 			bus_space_write_1(va->va_iot, ioh, LER_IVEC, 64 + 64);
 			break;
+		case LE_BVME410:
+			bus_space_write_2(va->va_iot, ioh, BVME410_IVEC, 64 + 64);
+			break;
 	}
 
 	/*
@@ -445,3 +488,102 @@
 		sc->sc_tbufaddr[i] += offset;
 	}
 }
+
+static int
+nm93c06_read(iot, ioh, nm93c06reg)
+	bus_space_tag_t iot;
+	bus_space_handle_t ioh;
+	int nm93c06reg;
+{
+	int bar;
+	int shift;
+	int bits = 0x180 | (nm93c06reg & 0xf);
+	int data = 0;
+
+	bar = 1<<BVME410_CS_SHIFT;
+	bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+	delay(1); /* tCSS = 1 us */
+	for (shift = 9; shift >= 0; shift--) {
+		if (((bits >> shift) & 1) == 1)
+			bar |= 1<<BVME410_DIN_SHIFT;
+		else
+			bar &= ~(1<<BVME410_DIN_SHIFT);
+		bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+		delay(1); /* tDIS = 0.4 us */
+		bar |= 1<<BVME410_CLK_SHIFT;
+		bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+		delay(2); /* tSKH = 1 us, tSKH + tSKL >= 4 us */
+		bar &= ~(1<<BVME410_CLK_SHIFT);
+		bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+		delay(2); /* tSKL = 1 us, tSKH + tSKL >= 4 us */
+	}
+	bar &= ~(1<<BVME410_DIN_SHIFT);
+	for (shift = 15; shift >= 0; shift--) {
+		delay(1); /* tDIS = 100 ns, BVM manual says 0.4 us */
+		bar |= 1<<BVME410_CLK_SHIFT;
+		bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+		delay(2); /* tSKH = 1 us, tSKH + tSKL >= 4 us */
+		data |= (bus_space_read_2(iot, ioh, BVME410_BAR) & 1) << shift;
+		bar &= ~(1<<BVME410_CLK_SHIFT);
+		bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+		delay(2); /* tSKL = 1 us, tSKH + tSKL >= 4 us */
+	}
+	bar &= ~(1<<BVME410_CS_SHIFT);
+	bus_space_write_2(iot, ioh, BVME410_BAR, bar);
+	delay(1); /* tCS = 1 us */
+	return data;
+}
+
+static int
+bvme410_mem_size(memt, mem_addr)
+	bus_space_tag_t memt;
+	u_long mem_addr;
+{
+	bus_space_handle_t memh;
+	int r;
+
+	if (bus_space_map(memt, mem_addr, 256*1024, 0, &memh))
+		return VMECF_MEMSIZ_DEFAULT;
+	if (!bus_space_peek_1(memt, memh, 0)) {
+		bus_space_unmap(memt, (caddr_t)mem_addr, 256*1024);
+		return VMECF_MEMSIZ_DEFAULT;
+	}
+	bus_space_write_1(memt, memh, 0, 128);
+	bus_space_write_1(memt, memh, 64*1024, 32);
+	bus_space_write_1(memt, memh, 32*1024, 8);
+	r = (int)(bus_space_read_1(memt, memh, 0) * 2048);
+	bus_space_unmap(memt, (caddr_t)mem_addr, 256*1024);
+	return r;
+}
+
+/*
+ * Need to be careful when writing to the bvme410 dual port memory.
+ * Continue writing each byte until it reads back the same.
+ */
+
+static void
+bvme410_copytobuf(sc, from, boff, len)
+	struct lance_softc *sc;
+	void *from;
+	int boff, len;
+{
+	volatile char *buf = (volatile char *) sc->sc_mem;
+	char *f = (char *) from;
+
+	for (buf += boff; len; buf++,f++,len--)
+		while (*buf != *f)
+			*buf = *f;
+}
+
+static void
+bvme410_zerobuf(sc, boff, len)
+	struct lance_softc *sc;
+	int boff, len;
+{
+	volatile char *buf = (volatile char *)sc->sc_mem;
+
+	for (buf += boff; len; buf++,len--)
+		while (*buf != '\0')
+			*buf = '\0';
+}
+
--- /sys/arch/atari/vme/if_levar.h-orig	Thu Oct  9 19:25:38 1997
+++ /sys/arch/atari/vme/if_levar.h	Tue Dec  1 19:48:43 1998
@@ -42,6 +42,8 @@
  */
 #define	LER_RDP		0	/* Data port			*/
 #define	LER_RAP		2	/* Register select port		*/
+#define	BVME410_IVEC	4	/* Interrupt ID Register	*/
+#define	BVME410_BAR	6	/* BVME410 RAM Base Address Reg */
 #define	LER_IVEC	7	/* Interrupt vector		*/
 #define	LER_EEPROM	13	/* PAM's Eeprom enable port	*/
 #define	LER_MEME	15	/* PAM's Mem enable port	*/
@@ -72,6 +74,7 @@
 #define	LE_PAM		0
 #define	LE_OLD_RIEBL	1
 #define	LE_NEW_RIEBL	2
+#define	LE_BVME410	3
 
 /*
  * Determine type of RIEBL card by magic
@@ -94,3 +97,12 @@
  */
 #define	RIEBL_RES_START		0xee70
 #define	RIEBL_RES_END		0xeec0
+
+/*
+ * Bits in the BVME410 RAM Base Address Register, when node address
+ * serial EEPROM is enabled.
+ */
+#define	BVME410_CS_SHIFT	1
+#define	BVME410_CLK_SHIFT	2
+#define	BVME410_DIN_SHIFT	3
+
--- /sys/arch/atari/conf/GENERIC-orig	Wed Dec  2 03:56:31 1998
+++ /sys/arch/atari/conf/GENERIC	Wed Dec  2 03:56:57 1998
@@ -184,6 +184,7 @@
 avmebus0 at mainbus0			# VME bus
 vme0	at avmebus0
 le0	at vme0	irq 5			# Lance ethernet (Riebl/PAM).
+le0	at vme0 irq 4			# Lance ethernet (BVME410).
 et0	at vme0				# Crazy Dots II
 
 isabus0	at mainbus0			# ISA-bus


--
entropy -- it's not just a good idea, it's the second law.