Subject: Re: netboot via native bootloader
To: None <port-cobalt@NetBSD.org>
From: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
List: port-cobalt
Date: 10/28/2007 00:55:13
I wrote:

> Today I've been trying to add netboot support to the cobalt
> native bootloader, using a standalone tulip (2114x Ethernet)
> driver for sandpoint which has been committed by Tohru Nishimura:
 :
> Current my working sources are too ugly so I'll post
> a patch after some more cleanup in a few days.

A patch to be committed is attached.

I've also put a bootloader binary here:
http://www.ceres.dti.ne.jp/~tsutsui/netbsd/boot-cobalt-20071028.gz

To test netboot, refer this FAQ entry
http://www.netbsd.org/ports/cobalt/faq.html#netboot
and specify bootloader's name (boot.gz or so) instead of kernel. 

Note if bootloader is loaded from wd disk it can't load a kernel
from tlp0, so some more initialization might be required.
---
Izumi Tsutsui


Index: stand/boot/Makefile
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/Makefile,v
retrieving revision 1.11
diff -u -r1.11 Makefile
--- stand/boot/Makefile	12 Aug 2006 11:38:11 -0000	1.11
+++ stand/boot/Makefile	27 Oct 2007 15:50:03 -0000
@@ -8,8 +8,10 @@
 S=	${.CURDIR}/../../../..
 MIPS=	${S}/arch/mips
 COBALT=	${S}/arch/cobalt
+LIBSADIR=	${S}/lib/libsa
 
 # .PATH:	${.CURDIR}/../common
+.PATH:		${LIBSADIR}
 
 BINMODE?= 444
 
@@ -55,6 +57,9 @@
 CPPFLAGS+=	-I${.OBJDIR} -I${S} -I${S}/lib/libsa
 CPPFLAGS+=	-DCONS_SERIAL -DCOMBASE=${COMBASE} -DCOMPORT=${COMPORT}
 CPPFLAGS+=	-DCOMSPEED=${COMSPEED} -DCOMPROBE=${COMPROBE}
+CPPFLAGS+=	-DSUPPORT_DHCP -DSUPPORT_BOOTP
+#CPPFLAGS+=	-DBOOTP_DEBUG -DNETIF_DEBUG -DETHER_DEBUG -DNFS_DEBUG
+#CPPFLAGS+=	-DRPC_DEBUG -DRARP_DEBUG -DNET_DEBUG -DDEBUG -DPARANOID
 
 # compiler flags for smallest code size
 CFLAGS=		-Os -mmemcpy -ffreestanding -mno-abicalls -msoft-float -G 128
@@ -68,6 +73,10 @@
 # common sources
 SRCS+=		start.S boot.c devopen.c conf.c clock.c bootinfo.c
 SRCS+=		prf.c com.c cons.c ns16550.c pciide.c tgets.c wdc.c wd.c
+SRCS+=		cache.c if_tlp.c tlp.c
+
+# ${S}/lib/libsa
+SRCS+=		dev_net.c
 
 SRCS+=		vers.c
 CLEANFILES+=	vers.c
Index: stand/boot/boot.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/boot.c,v
retrieving revision 1.9
diff -u -r1.9 boot.c
--- stand/boot/boot.c	17 Oct 2007 19:54:09 -0000	1.9
+++ stand/boot/boot.c	27 Oct 2007 15:50:03 -0000
@@ -75,6 +75,7 @@
 
 #include <lib/libsa/stand.h>
 #include <lib/libsa/loadfile.h>
+#include <lib/libsa/dev_net.h>
 #include <lib/libkern/libkern.h>
 
 #include <sys/param.h>
@@ -171,10 +172,27 @@
 	DPRINTF(("patch_bootstring: [%s]\n", bootstring));
 
 #define DEVNAMESIZE	(MAXDEVNAME + sizeof(" root=/dev/hd") + sizeof("0a"))
-	/* bsd notation -> linux notation (wd0a -> hda1) */
-	if (strlen(bootstring) <= (511 - DEVNAMESIZE)) {
+	if (strcmp(devsw[dev].dv_name, "wd") == 0 &&
+	    strlen(bootstring) <= (511 - DEVNAMESIZE)) {
 		int len;
 
+		/* omit "nfsroot=" arg on wd boot */
+		if ((sp = strstr(bootstring, "nfsroot=")) != NULL) {
+			const char *end;
+
+			end = strchr(sp, ' ');
+
+			/* strip off leading spaces */
+			for (--sp; (sp > bootstring) && (*sp == ' '); --sp)
+				;
+
+			if (end != NULL)
+				strcpy(++sp, end);
+			else
+				*++sp = '\0';
+		}
+
+		/* bsd notation -> linux notation (wd0a -> hda1) */
 		strcat(bootstring, " root=/dev/hd");
 
 		len = strlen(bootstring);
@@ -200,12 +218,14 @@
 	char *ptr, *spec;
 	char c, namebuf[PATH_MAX];
 	static char bootdev[] = "wd0a";
+	static const char nfsbootdev[] = "nfs";
 
 	bootstr_dev = prompt_dev = NULL;
 	bootstr_kname = prompt_kname = NULL;
 
 	/* first, get root device specified by the firmware */
 	spec = bootstring;
+
 	/* assume the last one is valid */
 	while ((spec = strstr(spec, "root=")) != NULL) {
 		spec += 5;	/* skip 'root=' */
@@ -239,6 +259,11 @@
 		}
 	}
 
+	/* third, check if netboot */
+	if (strstr(bootstring, "nfsroot=") != NULL) {
+		bootstr_dev = nfsbootdev;
+	}
+
 	DPRINTF(("bootstr_dev = %s, bootstr_kname = %s\n",
 	    bootstr_dev ? bootstr_dev : "<NULL>",
 	    bootstr_kname ? bootstr_kname : "<NULL>"));
@@ -365,9 +390,10 @@
 	struct btinfo_symtab bi_syms;
 	struct btinfo_bootpath bi_bpath;
 	struct btinfo_howto bi_howto;
-
 	int addr, speed, howto;
 
+	try_bootp = 1;
+
 	/* Initialize boot info early */
 	howto = 0x0;
 	bi_flags.bi_flags = 0x0;
Index: stand/boot/boot.h
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/boot.h,v
retrieving revision 1.5
diff -u -r1.5 boot.h
--- stand/boot/boot.h	17 Oct 2007 19:54:09 -0000	1.5
+++ stand/boot/boot.h	27 Oct 2007 15:50:03 -0000
@@ -71,8 +71,27 @@
 int wdclose(struct open_file *);
 
 /*
+ * tlp
+ */
+void *tlp_init(void *); 
+int tlp_send(void *, char *, u_int);
+int tlp_recv(void *, char *, u_int, u_int);
+
+extern struct netif_driver ether_tlp_driver;
+
+/*
  * devopen
  */
 int devparse(const char *, int *, uint8_t *, uint8_t *, const char **);
 
+/*
+ * tgetc
+ */
 int tgets(char *);
+
+/*
+ * cache
+ */
+void pdcache_wb(uint32_t, u_int);
+void pdcache_inv(uint32_t, u_int);
+void pdcache_wbinv(uint32_t, u_int);
Index: stand/boot/clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/clock.c,v
retrieving revision 1.1
diff -u -r1.1 clock.c
--- stand/boot/clock.c	25 Jun 2003 17:24:22 -0000	1.1
+++ stand/boot/clock.c	27 Oct 2007 15:50:03 -0000
@@ -39,10 +39,20 @@
 #include <sys/types.h>
 #include <lib/libsa/stand.h>
 
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <lib/libsa/net.h>
+
+#include <dev/ic/mc146818reg.h>
+
 #include "boot.h"
 
 #define DELAY_CALIBRATE	1000
 
+#define MCCLOCK_BASE	0xa0000070
+#define MCCLOCK_REG	0
+#define MCCLOCK_DATA	1
+
 void
 delay(int ms)
 {
@@ -51,5 +61,20 @@
 	 */
 	volatile register int N = ms * DELAY_CALIBRATE;
 	for (; --N;)
-		;
+		__insn_barrier();
+}
+
+time_t
+getsecs(void)
+{
+	u_int sec;
+
+	*(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_REG) = MC_SEC;
+	sec  = *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_DATA);
+	*(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_REG) = MC_MIN;
+	sec += *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_DATA) * 60;
+	*(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_REG) = MC_HOUR;
+	sec += *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_DATA) * 60 * 60;
+
+	return (time_t)sec;
 }
Index: stand/boot/conf.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/conf.c,v
retrieving revision 1.4
diff -u -r1.4 conf.c
--- stand/boot/conf.c	11 Dec 2005 12:17:06 -0000	1.4
+++ stand/boot/conf.c	27 Oct 2007 15:50:03 -0000
@@ -52,6 +52,7 @@
  */
 struct devsw devsw[] = {
 	{ "wd",	wdstrategy, wdopen, wdclose, noioctl },
+	{ "nfs", net_strategy, net_open, net_close, net_ioctl }
 };
 
 int	ndevs = (sizeof(devsw)/sizeof(devsw[0]));
@@ -61,19 +62,15 @@
  */
 struct fs_ops file_system[] = {
 	FS_OPS(ufs),
-#if 0
 	FS_OPS(nfs),
-#endif
 };
 
 int nfsys = sizeof(file_system) / sizeof(file_system[0]);
 
-#if 0
 extern struct netif_driver en_driver;
 
 struct netif_driver *netif_drivers[] = {
-	&en_driver,
+	&ether_tlp_driver,
 };
 
 int n_netif_drivers = sizeof(netif_drivers) / sizeof(netif_drivers[0]);
-#endif
Index: stand/boot/devopen.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/devopen.c,v
retrieving revision 1.3
diff -u -r1.3 devopen.c
--- stand/boot/devopen.c	17 Oct 2007 19:54:09 -0000	1.3
+++ stand/boot/devopen.c	27 Oct 2007 15:50:03 -0000
@@ -77,24 +77,31 @@
 		/* extract device name */
 		for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
 			devname[i] = fname[i];
-		devname[i] = 0;
+		devname[i] = '\0';
 
-		if (!isnum(fname[i]))
-			return EUNIT;
+		if (strcmp(devname, "nfs") == 0) {
+			/* no unit number or partition suffix on netboot */
+			u = 0;
+			p = 0;
+		} else {
+			/* parse [disk][unit][part] (ex. wd0a) strings */	
+			if (!isnum(fname[i]))
+				return EUNIT;
+
+			/* device number */
+			for (u = 0; isnum(fname[i]) && (i < devlen); i++)
+				u = u * 10 + (fname[i] - '0');
+
+			if (!isalpha(fname[i]))
+				return EPART;
+
+			/* partition number */
+			if (i < devlen)
+				p = fname[i++] - 'a';
 
-		/* device number */
-		for (u = 0; isnum(fname[i]) && (i < devlen); i++)
-			u = u * 10 + (fname[i] - '0');
-
-		if (!isalpha(fname[i]))
-			return EPART;
-
-		/* partition number */
-		if (i < devlen)
-			p = fname[i++] - 'a';
-
-		if (i != devlen)
-			return ENXIO;
+			if (i != devlen)
+				return ENXIO;
+		}
 
 		/* check device name */
 		for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
@@ -132,7 +139,7 @@
 
 	dp = &devsw[dev];
 	if ((void *)dp->dv_open == (void *)nodev)
-	return ENXIO;
+		return ENXIO;
 
 	f->f_dev = dp;
     
Index: stand/boot/start.S
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/start.S,v
retrieving revision 1.5
diff -u -r1.5 start.S
--- stand/boot/start.S	17 Oct 2007 19:54:09 -0000	1.5
+++ stand/boot/start.S	27 Oct 2007 15:50:03 -0000
@@ -64,7 +64,7 @@
 	jal	_C_LABEL(main)			# main(unsigned int)
 	move	a1, s1				# restore argv
 
-XLEAF(_xtt)
+XLEAF(_rtt)
 	jal	_C_LABEL(cpu_reboot)		# failed, reboot
 	nop
 END(start)
Index: stand/boot/version
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/version,v
retrieving revision 1.5
diff -u -r1.5 version
--- stand/boot/version	17 Oct 2007 19:54:09 -0000	1.5
+++ stand/boot/version	27 Oct 2007 15:50:03 -0000
@@ -9,3 +9,4 @@
 0.3:	Parse boot_flags and pass boothowto value via bootinfo
 0.4:	parse "root=/dev/hdXN" args passed from the firmware and
 	set default boot device accordingly
+0.5:	Add support for netboot via tlp0
--- /dev/null	2007-10-28 00:31:10.000000000 +0900
+++ stand/boot/cache.c	2007-10-28 00:49:18.000000000 +0900
@@ -0,0 +1,92 @@
+/*	$NetBSD$	*/
+
+/*
+ * Copyright 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <lib/libsa/stand.h>
+#include <lib/libkern/libkern.h>
+
+#include <mips/cpuregs.h>
+#include <mips/cache_r4k.h>
+
+#include "boot.h"
+
+#define	CACHELINESIZE	16
+#define	round_line(x)	(((x) + (CACHELINESIZE - 1)) & ~(CACHELINESIZE - 1))
+#define	trunc_line(x)	((x) & ~(CACHELINESIZE - 1))
+
+__asm(".set mips3");
+
+void
+pdcache_inv(uint32_t va, u_int size)
+{
+	uint32_t eva;
+
+	va  = trunc_line(va);
+	eva = round_line(va + size);
+
+	while (va < eva) {
+		cache_op_r4k_line(va, CACHE_R4K_D|CACHEOP_R4K_HIT_INV);
+		va += CACHELINESIZE;
+	}
+}
+
+void
+pdcache_wb(uint32_t va, u_int size)
+{
+	uint32_t eva;
+
+	va  = trunc_line(va);
+	eva = round_line(va + size);
+
+	while (va < eva) {
+		cache_op_r4k_line(va, CACHE_R4K_D|CACHEOP_R4K_HIT_WB);
+		va += CACHELINESIZE;
+	}
+}
+
+void
+pdcache_wbinv(uint32_t va, u_int size)
+{
+	uint32_t eva;
+
+	va  = trunc_line(va);
+	eva = round_line(va + size);
+
+	while (va < eva) {
+		cache_op_r4k_line(va, CACHE_R4K_D|CACHEOP_R4K_HIT_WB_INV);
+		va += CACHELINESIZE;
+	}
+}
--- /dev/null	2007-10-28 00:31:10.000000000 +0900
+++ stand/boot/if_tlp.c	2007-10-27 15:25:18.000000000 +0900
@@ -0,0 +1,153 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by UCHIYAMA Yashusi.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <lib/libsa/stand.h>
+#include <lib/libkern/libkern.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <lib/libsa/net.h>
+#include <lib/libsa/netif.h>
+#include <lib/libsa/dev_net.h>
+
+#include "boot.h"
+
+static int tlp_match(struct netif *, void *);
+static int tlp_probe(struct netif *, void *);
+static void tlp_attach(struct iodesc *, void *);
+static int tlp_get(struct iodesc *, void *, size_t, time_t);
+static int tlp_put(struct iodesc *, void *, size_t);
+static void tlp_end(struct netif *);
+
+#define	MIN_LEN		60	/* ETHER_MIN_LEN - ETHER_CRC_LEN */
+
+static struct netif_stats tlp_stats[1];
+
+static struct netif_dif tlp_ifs[] = {
+	{ 0, 1, &tlp_stats[0], NULL, 0 },
+};
+
+struct netif_driver ether_tlp_driver = {
+	"tlp",
+	tlp_match,
+	tlp_probe,
+	tlp_attach,
+	tlp_get,
+	tlp_put,
+	tlp_end,
+	tlp_ifs,
+	1,
+};
+
+#ifdef DEBUG
+int debug = 1;		/* referred in various libsa net sources */
+#endif
+
+int
+tlp_match(struct netif *netif, void *hint)
+{
+
+	/* always match for onboard tlp */
+	return 1;
+}
+
+int
+tlp_probe(struct netif *netif, void *hint)
+{
+
+	/* XXX */
+	return 0;
+}
+
+void
+tlp_attach(struct iodesc *desc, void *hint)
+{
+	struct netif *nif = desc->io_netif;
+	struct netif_dif *dif = &nif->nif_driver->netif_ifs[nif->nif_unit];
+
+	dif->dif_private = tlp_init(&desc->myea);
+}
+
+int
+tlp_get(struct iodesc *desc, void *pkt, size_t maxlen, time_t timeout)
+{
+	int len;
+	struct netif *nif = desc->io_netif;
+	struct netif_dif *dif = &nif->nif_driver->netif_ifs[nif->nif_unit];
+	struct local *l = dif->dif_private;
+
+	len = tlp_recv(l, pkt, maxlen, timeout);
+	if (len == -1) {
+		printf("tlp: receive timeout\n");
+		/* XXX */
+	}
+
+	if (len < MIN_LEN)
+		len = -1;
+
+	return len;
+}
+
+int
+tlp_put(struct iodesc *desc, void *pkt, size_t len)
+{
+	struct netif *nif = desc->io_netif;
+	struct netif_dif *dif = &nif->nif_driver->netif_ifs[nif->nif_unit];
+	struct local *l = dif->dif_private;
+	int rv;
+	size_t sendlen;
+
+	sendlen = len;
+	if (sendlen < MIN_LEN)
+		sendlen = MIN_LEN;	/* XXX */
+
+	rv = tlp_send(l, pkt, sendlen);
+
+	return rv;
+}
+
+void
+tlp_end(struct netif *netif)
+{
+}
--- /dev/null	2007-10-28 00:31:10.000000000 +0900
+++ stand/boot/tlp.c	2007-10-27 23:04:34.000000000 +0900
@@ -0,0 +1,463 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Tohru Nishimura.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <lib/libsa/stand.h>
+#include <lib/libsa/net.h>
+
+#include <mips/cpuregs.h>
+
+#include "boot.h"
+
+/*
+ * - little endian access for CSR register.
+ * - assume KSEG0 for vtophys() translation. 
+ * - PIPT writeback cache aware.
+ */
+#define CSR_WRITE(l, r, v)					 	\
+do {									\
+	*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1((l)->csr + (r)) = (v);	\
+} while (0)
+#define CSR_READ(l, r)							\
+	*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1((l)->csr + (r))
+#define VTOPHYS(va) 		MIPS_KSEG0_TO_PHYS(va)
+#define wb(adr, siz)		pdcache_wb((uint32_t)(adr), (uint32_t)(siz))
+#define wbinv(adr, siz)		pdcache_wbinv((uint32_t)(adr), (uint32_t)(siz))
+#define inv(adr, siz)		pdcache_inv((uint32_t)(adr), (uint32_t)(siz))
+#define DELAY(n)		delay(n)
+#define ALLOC(T, A)	(T *)((uint32_t)alloc(sizeof(T) + (A)) & ~((A) - 1))
+
+#define T0_OWN		(1U<<31)	/* desc is ready to tx */
+#define T0_ES		(1U<<15)	/* Tx error summary */
+#define T1_LS		(1U<<30)	/* last segment */
+#define T1_FS		(1U<<29)	/* first segment */
+#define T1_SET		(1U<<27)	/* "setup packet" */
+#define T1_TER		(1U<<25)	/* end of ring mark */
+#define T1_TBS_MASK	0x7ff		/* segment size 10:0 */
+#define R0_OWN		(1U<<31)	/* desc is empty */
+#define R0_FS		(1U<<30)	/* first desc of frame */
+#define R0_LS		(1U<<8)		/* last desc of frame */
+#define R0_ES		(1U<<15)	/* Rx error summary */
+#define R1_RER		(1U<<25)	/* end of ring mark */
+#define R0_FL_MASK	0x3fff0000	/* frame length 29:16 */
+#define R1_RBS_MASK	0x7ff		/* segment size 10:0 */
+
+struct desc {
+	volatile uint32_t xd0, xd1, xd2, xd3;
+};
+
+
+#define TLP_BMR		0x000		/* 0: bus mode */
+#define  BMR_RST	(1U<< 0)	/* software reset */
+#define TLP_TPD		0x008		/* 1: instruct Tx to start */
+#define  TPD_POLL	(1U<< 0)	/* transmit poll demand */
+#define TLP_RPD		0x010		/* 2: instruct Rx to start */
+#define  RPD_POLL	(1U<< 0)	/* receive poll demand */
+#define TLP_RRBA	0x018		/* 3: Rx descriptor base */
+#define TLP_TRBA	0x020		/* 4: Tx descriptor base */
+#define TLP_STS		0x028		/* 5: status */
+#define  STS_TS		0x00700000	/* Tx status */
+#define  STS_RS		0x000e0000	/* Rx status */
+#define TLP_OMR		0x030		/* 6: operation mode */
+#define  OMR_SDP	(1U<<25)	/* always ON */
+#define  OMR_PS		(1U<<18)	/* port select */
+#define  OMR_PM		(1U<< 6)	/* promicuous */
+#define  OMR_TEN	(1U<<13)	/* instruct start/stop Tx */
+#define  OMR_REN	(1U<< 1)	/* instruct start/stop Rx */
+#define  OMR_FD		(1U<< 9)	/* FDX */
+#define TLP_IEN		0x38		/* 7: interrupt enable mask */
+#define TLP_APROM	0x048		/* 9: SEEPROM and MII management */
+#define  SROM_RD	(1U <<14)	/* read operation */
+#define  SROM_WR	(1U <<13)	/* write openration */
+#define  SROM_SR	(1U <<11)	/* SEEPROM select */
+#define TLP_CSR12	0x60		/* SIA status */
+
+#define TLP_CSR15	0x78		/* SIA general register */
+#define  SIAGEN_MD0	(1U<<16)
+#define  SIAGEN_CWE	(1U<<28)
+
+
+#define FRAMESIZE	1536
+#define BUFSIZE		2048
+#define NRXBUF		2
+#define NEXT_RXBUF(x)	((x + 1) & (NRXBUF - 1))
+
+struct local {
+	struct desc TxD;
+	struct desc RxD[NRXBUF];
+	uint8_t txstore[BUFSIZE];
+	uint8_t rxstore[NRXBUF][BUFSIZE];
+	uint32_t csr, omr, rx;
+	u_int sromsft;
+	u_int phy, bmsr, anlpar;
+};
+
+#define COBALT_TLP0_BASE	0x10100000
+#define EEP_MAC_OFFSET		0
+
+static void size_srom(struct local *);
+static int read_srom(struct local *, int);
+#if 0
+static u_int tlp_mii_read(struct local *, int, int);
+static void tlp_mii_write(struct local *, int, int, int);
+static void mii_initphy(struct local *);
+#endif
+
+void *
+tlp_init(void *cookie)
+{
+	uint32_t tag, val;
+	struct local *l;
+	struct desc *TxD, *RxD;
+	uint8_t *en;
+	int i;
+
+	l = ALLOC(struct local, sizeof(struct desc));
+	memset(l, 0, sizeof(struct local));
+
+#if 0
+	/* XXX assume tlp0 at pci0 dev 7 function 0 */
+	tag = (0 << 16) | ( 7 << 11) | (0 << 8);
+	/* memory map is not initialized by the firmware on cobalt */
+	l->csr = pcicfgread(tag, 0x10);
+	l->csr &= 0xfffffffc;
+	DPRINTF(("%s: CSR = 0x%x\n", __func__, l->csr));
+#else
+
+	l->csr = COBALT_TLP0_BASE;
+#endif
+
+	val = CSR_READ(l, TLP_BMR);
+	CSR_WRITE(l, TLP_BMR, val | BMR_RST);
+	DELAY(1000);
+	CSR_WRITE(l, TLP_BMR, val);
+	DELAY(1000);
+	(void)CSR_READ(l, TLP_BMR);
+
+	l->omr = OMR_PS | OMR_SDP;
+	CSR_WRITE(l, TLP_OMR, l->omr);
+	CSR_WRITE(l, TLP_STS, ~0);
+	CSR_WRITE(l, TLP_IEN, 0);
+
+#if 0
+	mii_initphy(l);
+#endif
+	size_srom(l);
+
+	en = cookie;
+	/* MAC address is stored at offset 0 in SROM on cobalt */
+	val = read_srom(l, EEP_MAC_OFFSET / 2 + 0);
+	en[0] = val;
+	en[1] = val >> 8;
+	val = read_srom(l, EEP_MAC_OFFSET / 2 + 1);
+	en[2] = val;
+	en[3] = val >> 8;
+	val = read_srom(l, EEP_MAC_OFFSET / 2 + 2);
+	en[4] = val;
+	en[5] = val >> 8;
+
+	DPRINTF(("tlp: MAC address %x:%x:%x:%x:%x:%x\n",
+	    en[0], en[1], en[2], en[3], en[4], en[5]));
+
+	RxD = &l->RxD[0];
+	for (i = 0; i < NRXBUF; i++) {
+		RxD[i].xd0 = htole32(R0_OWN);
+		RxD[i].xd1 = htole32(FRAMESIZE);
+		RxD[i].xd2 = htole32(VTOPHYS(l->rxstore[i]));
+		RxD[i].xd3 = htole32(VTOPHYS(&RxD[NEXT_RXBUF(i)]));
+	}
+	RxD[NRXBUF - 1].xd1 |= htole32(R1_RER);
+	CSR_WRITE(l, TLP_RRBA, VTOPHYS(RxD));
+
+	/* "setup packet" to have own station address */
+	TxD = &l->TxD;
+	TxD->xd3 = htole32(VTOPHYS(TxD));
+	TxD->xd2 = htole32(VTOPHYS(l->txstore));
+	TxD->xd1 = htole32(T1_SET | T1_TER);
+	TxD->xd0 = htole32(0);
+	CSR_WRITE(l, TLP_TRBA, VTOPHYS(TxD));
+
+	memset(l->txstore, 0, FRAMESIZE);
+
+	/* make sure the entire descriptors transfered to memory */
+	wbinv(l, sizeof(struct local));
+
+	l->rx = 0;
+	l->omr |= OMR_FD | OMR_TEN | OMR_REN;
+
+#if 1
+	/* reset PHY (cobalt quirk from if_tlp_pci.c) */
+	CSR_WRITE(l, TLP_CSR15, SIAGEN_CWE | SIAGEN_MD0);
+	delay(10);
+	CSR_WRITE(l, TLP_CSR15, SIAGEN_CWE);
+	delay(10);
+#endif
+	
+	/* start Tx/Rx */
+	CSR_WRITE(l, TLP_OMR, l->omr);
+	CSR_WRITE(l, TLP_TPD, TPD_POLL);
+	CSR_WRITE(l, TLP_RPD, RPD_POLL);
+
+	return l;
+}
+
+int
+tlp_send(void *dev, char *buf, u_int len)
+{
+	struct local *l = dev;
+	struct desc *TxD;
+	u_int loop;
+
+#if 1
+	wbinv(buf, len);
+	TxD = &l->TxD;
+	TxD->xd3 = htole32(VTOPHYS(TxD));
+	TxD->xd2 = htole32(VTOPHYS(buf));
+	TxD->xd1 = htole32(T1_FS | T1_LS | T1_TER | (len & T1_TBS_MASK));
+#else
+	memcpy(l->txstore, buf, len);
+	wb(l->txstore, len);
+	TxD = &l->TxD;
+	TxD->xd3 = htole32(VTOPHYS(TxD));
+	TxD->xd2 = htole32(VTOPHYS(l->txstore));
+	TxD->xd1 = htole32(T1_FS | T1_LS | T1_TER | (len & T1_TBS_MASK));
+#endif
+	TxD->xd0 = htole32(T0_OWN);
+	wbinv(TxD, sizeof(struct desc));
+	CSR_WRITE(l, TLP_TPD, TPD_POLL);
+	loop = 100;
+	do {
+		if ((le32toh(TxD->xd0) & T0_OWN) == 0)
+			goto done;
+		DELAY(10);
+		inv(TxD, sizeof(struct desc));
+	} while (--loop > 0);
+	printf("xmit failed\n");
+	return -1;
+  done:
+	return len;
+}
+
+int
+tlp_recv(void *dev, char *buf, u_int maxlen, u_int timo)
+{
+	struct local *l = dev;
+	struct desc *RxD;
+	u_int bound, rxstat, len;
+	uint8_t *ptr;
+
+	bound = 1000 * timo;
+#if 0
+printf("recving with %u sec. timeout\n", timo);
+#endif
+
+  again:
+	RxD = &l->RxD[l->rx];
+	do {
+		inv(RxD, sizeof(struct desc));
+		rxstat = le32toh(RxD->xd0);
+		if ((rxstat & R0_OWN) == 0)
+			goto gotone;
+		DELAY(1000); /* 1 milli second */
+	} while (--bound > 0);
+	errno = 0;
+	CSR_WRITE(l, TLP_RPD, RPD_POLL);
+	return -1;
+  gotone:
+	if (rxstat & R0_ES) {
+		RxD->xd0 = htole32(R0_OWN);
+		wbinv(RxD, sizeof(struct desc));
+		l->rx = NEXT_RXBUF(l->rx);
+		CSR_WRITE(l, TLP_RPD, RPD_POLL);
+		goto again;
+	}
+	/* good frame */
+	len = ((rxstat & R0_FL_MASK) >> 16) - 4; /* HASFCS */
+        if (len > maxlen)
+                len = maxlen;
+	ptr = l->rxstore[l->rx];
+	memcpy(buf, ptr, len);
+	inv(ptr, len);
+	RxD->xd0 = htole32(R0_OWN);
+	wbinv(RxD, sizeof(struct desc));
+	l->rx = NEXT_RXBUF(l->rx);
+	CSR_WRITE(l, TLP_OMR, l->omr); /* necessary? */
+	return len;
+}
+
+static void
+size_srom(struct local *l)
+{
+	/* determine 8/6 bit addressing SEEPROM */
+	l->sromsft = 8;
+	l->sromsft = (read_srom(l, 255) & 0x40000) ? 8 : 6;
+}
+
+/*
+ * bare SEEPROM access with bitbang'ing
+ */
+#define R110	6		/* SEEPROM read op */
+#define CS  	(1U << 0)	/* hold chip select */
+#define CLK	(1U << 1)	/* clk bit */
+#define D1	(1U << 2)	/* bit existence */
+#define D0	0		/* bit absence */
+#define VV 	(1U << 3)	/* taken 0/1 from SEEPROM */
+
+static int
+read_srom(struct local *l, int off)
+{
+	u_int idx, val, x1, x0, cnt, bit, ret;
+
+	idx = off & 0xff;		/* A7-A0 */
+	idx |= R110 << l->sromsft;	/* 110 for READ */
+
+	val = SROM_RD | SROM_SR;
+	CSR_WRITE(l, TLP_APROM, val);
+	val |= CS;			/* hold CS */
+	CSR_WRITE(l, TLP_APROM, val);
+
+	x1 = val | D1;			/* 1 */
+	x0 = val | D0;			/* 0 */
+	/* instruct R110 op. at off in MSB first order */
+	for (cnt = (1 << (l->sromsft + 2)); cnt > 0; cnt >>= 1) {
+		bit = (idx & cnt) ? x1 : x0;
+		CSR_WRITE(l, TLP_APROM, bit);
+		DELAY(10);
+		CSR_WRITE(l, TLP_APROM, bit | CLK);
+		DELAY(10);
+	}
+	/* read 16bit quantity in MSB first order */
+	ret = 0;
+	for (cnt = 16; cnt > 0; cnt--) {
+		CSR_WRITE(l, TLP_APROM, val);
+		DELAY(10);
+		CSR_WRITE(l, TLP_APROM, val | CLK);
+		DELAY(10);
+		ret = (ret << 1) | !!(CSR_READ(l, TLP_APROM) & VV);
+	}
+	val &= ~CS; /* turn off chip select */
+	CSR_WRITE(l, TLP_APROM, val);
+
+	return ret;
+}
+
+#if 0
+
+static u_int
+tlp_mii_read(struct local *l, int phy, int reg)
+{
+	/* later ... */
+	return 0;
+}
+
+static void
+tlp_mii_write(struct local *l, int phy, int reg, int val)
+{
+	/* later ... */
+}
+
+#define MII_BMCR	0x00 	/* Basic mode control register (rw) */
+#define  BMCR_RESET	0x8000	/* reset */
+#define  BMCR_AUTOEN	0x1000	/* autonegotiation enable */
+#define  BMCR_ISO	0x0400	/* isolate */
+#define  BMCR_STARTNEG	0x0200	/* restart autonegotiation */
+#define MII_BMSR	0x01	/* Basic mode status register (ro) */
+
+static void
+mii_initphy(struct local *l)
+{
+	int phy, ctl, sts, bound;
+
+	for (phy = 0; phy < 32; phy++) {
+		ctl = tlp_mii_read(l, phy, MII_BMCR);
+		sts = tlp_mii_read(l, phy, MII_BMSR);
+		if (ctl != 0xffff && sts != 0xffff)
+			goto found;
+	}
+	printf("MII: no PHY found\n");
+	return;
+  found:
+	ctl = tlp_mii_read(l, phy, MII_BMCR);
+	tlp_mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
+	bound = 100;
+	do {
+		DELAY(10);
+		ctl = tlp_mii_read(l, phy, MII_BMCR);
+		if (ctl == 0xffff) {
+			printf("MII: PHY %d has died after reset\n", phy);
+			return;
+		}
+	} while (bound-- > 0 && (ctl & BMCR_RESET));
+	if (bound == 0) {
+		printf("PHY %d reset failed\n", phy);
+	}
+	ctl &= ~BMCR_ISO;
+	tlp_mii_write(l, phy, MII_BMCR, ctl);
+	sts = tlp_mii_read(l, phy, MII_BMSR) |
+	    tlp_mii_read(l, phy, MII_BMSR); /* read twice */
+	l->phy = phy;
+	l->bmsr = sts;
+}
+
+static void
+mii_dealan(struct local *, u_int timo)
+{
+	u_int anar, bound;
+
+	anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
+	tlp_mii_write(l, l->phy, MII_ANAR, anar);
+	tlp_mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
+	l->anlpar = 0;
+	bound = getsecs() + timo;
+	do {
+		l->bmsr = tlp_mii_read(l, l->phy, MII_BMSR) |
+		   tlp_mii_read(l, l->phy, MII_BMSR); /* read twice */
+		if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
+			l->anlpar = tlp_mii_read(l, l->phy, MII_ANLPAR);
+			break;
+		}
+		DELAY(10 * 1000);
+	} while (getsecs() < bound);
+	return;
+}
+#endif