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,
+ ðer_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