Sun Jan 23 01:05:32 2011 UTC ()
change bootloader name to "altboot". bump version and add README.
(nisimura)
diff -r1.1 -r1.2 src/sys/arch/sandpoint/stand/Makefile
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/Makefile
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/README.altboot
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/brdsetup.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/dev_net.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/devopen.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/dsk.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/entry.S
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/fxp.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/globals.h
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/kse.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/main.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/nif.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/nvt.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/pci.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/pciide.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/pcn.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/printf.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/rge.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/siisata.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/sip.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/skg.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/sme.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/tlp.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/vge.c
diff -r0 -r1.1 src/sys/arch/sandpoint/stand/altboot/wm.c
diff -r1.20 -r0 src/sys/arch/sandpoint/stand/netboot/Makefile
diff -r1.20 -r0 src/sys/arch/sandpoint/stand/netboot/nvt.c
diff -r1.22 -r0 src/sys/arch/sandpoint/stand/netboot/brdsetup.c
diff -r1.9 -r0 src/sys/arch/sandpoint/stand/netboot/dev_net.c
diff -r1.9 -r0 src/sys/arch/sandpoint/stand/netboot/pciide.c
diff -r1.12 -r0 src/sys/arch/sandpoint/stand/netboot/devopen.c
diff -r1.8 -r0 src/sys/arch/sandpoint/stand/netboot/dsk.c
diff -r1.7 -r0 src/sys/arch/sandpoint/stand/netboot/entry.S
diff -r1.7 -r0 src/sys/arch/sandpoint/stand/netboot/printf.c
diff -r1.11 -r0 src/sys/arch/sandpoint/stand/netboot/fxp.c
diff -r1.19 -r0 src/sys/arch/sandpoint/stand/netboot/globals.h
diff -r1.19 -r0 src/sys/arch/sandpoint/stand/netboot/pcn.c
diff -r1.19 -r0 src/sys/arch/sandpoint/stand/netboot/sip.c
diff -r1.6 -r0 src/sys/arch/sandpoint/stand/netboot/kse.c
diff -r1.6 -r0 src/sys/arch/sandpoint/stand/netboot/version
diff -r1.41 -r0 src/sys/arch/sandpoint/stand/netboot/main.c
diff -r1.3 -r0 src/sys/arch/sandpoint/stand/netboot/newvers.sh
diff -r1.3 -r0 src/sys/arch/sandpoint/stand/netboot/skg.c
diff -r1.13 -r0 src/sys/arch/sandpoint/stand/netboot/nif.c
diff -r1.13 -r0 src/sys/arch/sandpoint/stand/netboot/wm.c
diff -r1.14 -r0 src/sys/arch/sandpoint/stand/netboot/pci.c
diff -r1.17 -r0 src/sys/arch/sandpoint/stand/netboot/rge.c
diff -r1.15 -r0 src/sys/arch/sandpoint/stand/netboot/siisata.c
diff -r1.5 -r0 src/sys/arch/sandpoint/stand/netboot/sme.c
diff -r1.25 -r0 src/sys/arch/sandpoint/stand/netboot/tlp.c
diff -r1.18 -r0 src/sys/arch/sandpoint/stand/netboot/vge.c
--- src/sys/arch/sandpoint/stand/Makefile 2009/07/20 11:43:08 1.1
+++ src/sys/arch/sandpoint/stand/Makefile 2011/01/23 01:05:29 1.2
| @@ -1,5 +1,5 @@ | | | @@ -1,5 +1,5 @@ |
1 | # $NetBSD: Makefile,v 1.1 2009/07/20 11:43:08 nisimura Exp $ | | 1 | # $NetBSD: Makefile,v 1.2 2011/01/23 01:05:29 nisimura Exp $ |
2 | | | 2 | |
3 | SUBDIR= netboot | | 3 | SUBDIR= altboot |
4 | | | 4 | |
5 | .include <bsd.subdir.mk> | | 5 | .include <bsd.subdir.mk> |
# $NetBSD: Makefile,v 1.1 2011/01/23 01:05:30 nisimura Exp $
S= ${.CURDIR}/../../../..
PROG= altboot
SRCS= entry.S main.c brdsetup.c pci.c devopen.c dev_net.c nif.c \
fxp.c tlp.c rge.c skg.c dsk.c pciide.c siisata.c printf.c
CLEANFILES+= vers.c vers.o ${PROG} ${PROG}.bin
CFLAGS+= -Wall -Wno-main -ffreestanding -msoft-float -mmultiple
CFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith
CPPFLAGS+= -D_STANDALONE -DSUPPORT_DHCP
#CPPFLAGS+= -DCONSNAME=\"com\" -DCONSPORT=0x3f8 -DCONSSPEED=115200
#CPPFLAGS+= -DCONSNAME=\"eumb\" -DCONSPORT=0x4600 -DCONSSPEED=57600
CPPFLAGS+= -nostdinc -I. -I${.OBJDIR} -I${S}
DBG= -Os
# XXX SHOULD NOT NEED TO DEFINE THESE!
LIBCRT0=
LIBC=
LIBCRTBEGIN=
LIBCRTEND=
NOMAN= # defined
STRIPFLAG=
BINMODE= 444
RELOC= 1000000
ENTRY= _start
.if !make(obj) && !make(clean) && !make(cleandir)
.BEGIN:
@[ -h machine ] || ln -s ${S}/arch/${MACHINE}/include machine
@[ -h powerpc ] || ln -s ${S}/arch/powerpc/include powerpc
.NOPATH: machine powerpc
.endif
CLEANFILES+= machine powerpc
### find out what to use for libkern
KERN_AS= library
.include "${S}/lib/libkern/Makefile.inc"
LIBKERN= ${KERNLIB}
### find out what to use for libz
Z_AS= library
.include "${S}/lib/libz/Makefile.inc"
LIBZ= ${ZLIB}
### find out what to use for libsa
SA_AS= library
SAMISCMAKEFLAGS= SA_USE_CREAD=yes SA_USE_LOADFILE=yes
.include "${S}/lib/libsa/Makefile.inc"
LIBSA= ${SALIB}
${PROG}: ${OBJS} ${LIBSA} ${LIBZ} ${LIBKERN}
${HOST_SH} ${.CURDIR}/newvers.sh ${.CURDIR}/version
${CC} -c vers.c
${LD} -N -Ttext ${RELOC} -Bstatic -e ${ENTRY} -o ${PROG} \
${OBJS} vers.o ${LIBSA} ${LIBZ} ${LIBKERN}
${OBJCOPY} -S -O binary ${.TARGET} ${.TARGET}.bin
.include <bsd.prog.mk>
cleandir distclean: .WAIT cleanlibdir
cleanlibdir:
-rm -rf lib
/// notes about altboot ///
$NetBSD: README.altboot,v 1.1 2011/01/23 01:05:30 nisimura Exp $
Altboot is a functional bridge to fill the gap between a NAS product
custom bootloader and the NetBSD kernel startup environment. Altboot
irons out and rectifies erroneously configured HW by product
bootloaders and prepares a sane runtime better suited for booting
NetBSD kernels.
- provides the foundation of a fast NetBSD porting cycle with functionalities
product bootloaders don't have.
- facilitates a flexible and clean NetBSD implementation tailoured
to target HW in detail, minimizing bumpy adjustments and hacks in
locore asm and machdeps in very early kernel startup stage.
- levels out differences among similar-but-not-the-same porting
targets to make it possible having common NetBSD kernels for them.
- builds and hands a bootinfo list to the NetBSD kernel.
Altboot is known working on two models.
- KuroBox with a popular U-Boot as the replacement of vendor proprietary
U-Boot 1.1.4 LiSt 2.1.0 (Sep 21 2006 - 00:22:56) LinkStation / KuroBox
- Synology 101g+ with vendor custom PPCboot
PPCBoot 2.0.0 (Mar 1 2005 - 15:31:41)
The standard use of altboot is to invoke it with a short script from
U-Boot/PPCboot, where the altboot image is stored in an unoccupied 128KB
section of the target's HW NOR flash. Combined with standard
U-Boot/PPCboot functions, it is possible to boot a NetBSD kernel off
it right after power-on, without the help of manual intervention. Note
that the original U-Boot/PPCboot still remains useful and altboot works
as a functional extension for them.
Altboot hands the following bootinfo records to the NetBSD/sandpoint
kernel.
- processor clock tick value driving MPC8241/8245.
- serial console selection.
- booted kernel filename and which device it was fetched from.
- Ethernet MAC address, if target HW lacks SEEPROM to store a unit unique
value.
- product family indication.
- preloaded kernel module names (under development).
### ### ###
/* $NetBSD: brdsetup.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2008 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.
*
* 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 <powerpc/oea/spr.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <lib/libkern/libkern.h>
#include <machine/bootinfo.h>
#include "globals.h"
#define BRD_DECL(xxx) \
void xxx ## setup(struct brdprop *); \
void xxx ## brdfix(struct brdprop *); \
void xxx ## pcifix(struct brdprop *); \
void xxx ## reset(void)
BRD_DECL(mot);
BRD_DECL(enc);
BRD_DECL(kuro);
BRD_DECL(syno);
BRD_DECL(qnap);
static struct brdprop brdlist[] = {
{
"sandpoint",
"Sandpoint X3",
BRD_SANDPOINTX3,
0,
"com", 0x3f8, 115200,
motsetup, motbrdfix, motpcifix },
{
"encpp1",
"EnCore PP1",
BRD_ENCOREPP1,
0,
"com", 0x3f8, 115200,
encsetup, encbrdfix, encpcifix },
{
"kurobox",
"KuroBox",
BRD_KUROBOX,
32768000,
"eumb", 0x4600, 57600,
kurosetup, kurobrdfix, kuropcifix },
{
"synology",
"Synology DS",
BRD_SYNOLOGY,
33164691, /* from Synology/Linux source */
/* 33168000, XXX better precision? */
"eumb", 0x4500, 115200,
synosetup, synobrdfix, synopcifix, synoreset },
{
"qnap",
"QNAP TS-101",
BRD_QNAPTS101,
0,
"eumb", 0x4500, 115200,
NULL, NULL, qnappcifix },
{
"iomega",
"IOMEGA Storcenter",
BRD_STORCENTER,
0,
"eumb", 0x4500, 115200,
NULL, NULL, NULL },
{
"unknown",
"Unknown board",
BRD_UNKNOWN,
0,
"eumb", 0x4500, 115200,
NULL, NULL, NULL }, /* must be the last */
};
static struct brdprop *brdprop;
static uint32_t ticks_per_sec, ns_per_tick;
static void brdfixup(void);
static void setup(void);
static inline uint32_t cputype(void);
static inline u_quad_t mftb(void);
static void init_uart(unsigned, unsigned, uint8_t);
static void send_sat(char *);
const unsigned dcache_line_size = 32; /* 32B linesize */
const unsigned dcache_range_size = 4 * 1024; /* 16KB / 4-way */
unsigned uart1base; /* console */
unsigned uart2base; /* optional satellite processor */
#define THR 0
#define DLB 0
#define DMB 1
#define IER 1
#define FCR 2
#define LCR 3
#define LCR_DLAB 0x80
#define LCR_PEVEN 0x18
#define LCR_PNONE 0x00
#define LCR_8BITS 0x03
#define MCR 4
#define MCR_RTS 0x02
#define MCR_DTR 0x01
#define LSR 5
#define LSR_THRE 0x20
#define DCR 0x11
#define UART_READ(base, r) *(volatile char *)(base + (r))
#define UART_WRITE(base, r, v) *(volatile char *)(base + (r)) = (v)
void brdsetup(void); /* called by entry.S */
void
brdsetup(void)
{
static uint8_t pci_to_memclk[] = {
30, 30, 10, 10, 20, 10, 10, 10,
10, 20, 20, 15, 20, 15, 20, 30,
30, 40, 15, 40, 20, 25, 20, 40,
25, 20, 10, 20, 15, 15, 20, 00
};
static uint8_t mem_to_cpuclk[] = {
25, 30, 45, 20, 20, 00, 10, 30,
30, 20, 45, 30, 25, 35, 30, 35,
20, 25, 20, 30, 35, 40, 40, 20,
30, 25, 40, 30, 30, 25, 35, 00
};
char *consname;
int consport;
uint32_t extclk;
unsigned pchb, pcib, val;
extern struct btinfo_memory bi_mem;
extern struct btinfo_console bi_cons;
extern struct btinfo_clock bi_clk;
extern struct btinfo_prodfamily bi_fam;
/*
* CHRP specification "Map-B" BAT012 layout
* BAT0 0000-0000 (256MB) SDRAM
* BAT1 8000-0000 (256MB) PCI mem space
* BAT2 fc00-0000 (64MB) EUMB, PCI I/O space, misc devs, flash
*
* EUMBBAR is at fc00-0000.
*/
pchb = pcimaketag(0, 0, 0);
pcicfgwrite(pchb, 0x78, 0xfc000000);
brdtype = BRD_UNKNOWN;
extclk = EXT_CLK_FREQ; /* usually 33MHz */
busclock = 0;
if (pcifinddev(0x10ad, 0x0565, &pcib) == 0) {
brdtype = BRD_SANDPOINTX3;
}
else if (pcifinddev(0x1106, 0x0686, &pcib) == 0) {
brdtype = BRD_ENCOREPP1;
}
else if ((pcicfgread(pcimaketag(0, 11, 0), PCI_CLASS_REG) >> 16) ==
PCI_CLASS_ETH) {
/* tlp (ADMtek AN985) or re (RealTek 8169S) at dev 11 */
brdtype = BRD_KUROBOX;
}
else if (PCI_VENDOR(pcicfgread(pcimaketag(0, 15, 0), PCI_ID_REG)) ==
0x11ab) { /* PCI_VENDOR_MARVELL */
brdtype = BRD_SYNOLOGY;
}
else if (PCI_VENDOR(pcicfgread(pcimaketag(0, 15, 0), PCI_ID_REG)) ==
0x8086) { /* PCI_VENDOR_INTEL */
brdtype = BRD_QNAPTS101;
}
else if (PCI_VENDOR(pcicfgread(pcimaketag(0, 13, 0), PCI_ID_REG)) ==
0x1106) { /* PCI_VENDOR_VIA */
brdtype = BRD_STORCENTER;
}
brdprop = brd_lookup(brdtype);
/* brd dependent adjustments */
setup();
/* determine clock frequencies */
if (brdprop->extclk != 0)
extclk = brdprop->extclk;
if (busclock == 0) {
if (cputype() == MPC8245) {
/* PLL_CFG from PCI host bridge register 0xe2 */
val = pcicfgread(pchb, 0xe0);
busclock = (extclk *
pci_to_memclk[(val >> 19) & 0x1f] + 10) / 10;
/* PLLRATIO from HID1 */
__asm ("mfspr %0,1009" : "=r"(val));
cpuclock = ((uint64_t)busclock *
mem_to_cpuclk[val >> 27] + 10) / 10;
} else
busclock = 100000000; /* 100MHz bus clock default */
}
ticks_per_sec = busclock >> 2;
ns_per_tick = 1000000000 / ticks_per_sec;
/* now prepare serial console */
consname = brdprop->consname;
consport = brdprop->consport;
if (strcmp(consname, "eumb") == 0) {
uart1base = 0xfc000000 + consport; /* 0x4500, 0x4600 */
UART_WRITE(uart1base, DCR, 0x01); /* enable DUART mode */
uart2base = uart1base ^ 0x0300;
} else
uart1base = 0xfe000000 + consport; /* 0x3f8, 0x2f8 */
/* more brd adjustments */
brdfixup();
bi_mem.memsize = mpc107memsize();
snprintf(bi_cons.devname, sizeof(bi_cons.devname), consname);
bi_cons.addr = consport;
bi_cons.speed = brdprop->consspeed;
bi_clk.ticks_per_sec = ticks_per_sec;
snprintf(bi_fam.name, sizeof(bi_fam.name), brdprop->family);
}
struct brdprop *
brd_lookup(int brd)
{
u_int i;
for (i = 0; i < sizeof(brdlist)/sizeof(brdlist[0]); i++) {
if (brdlist[i].brdtype == brd)
return &brdlist[i];
}
return &brdlist[i - 1];
}
static void
setup()
{
if (brdprop->setup == NULL)
return;
(*brdprop->setup)(brdprop);
}
static void
brdfixup()
{
if (brdprop->brdfix == NULL)
return;
(*brdprop->brdfix)(brdprop);
}
void
pcifixup()
{
if (brdprop->pcifix == NULL)
return;
(*brdprop->pcifix)(brdprop);
}
void
encsetup(struct brdprop *brd)
{
#ifdef COSNAME
brd->consname = CONSNAME;
#endif
#ifdef CONSPORT
brd->consport = CONSPORT;
#endif
#ifdef CONSSPEED
brd->consspeed = CONSSPEED;
#endif
}
void
encbrdfix(struct brdprop *brd)
{
unsigned ac97, ide, pcib, pmgt, usb12, umot4, val;
/*
* VIA82C686B Southbridge
* 0.22.0 1106.0686 PCI-ISA bridge
* 0.22.1 1106.0571 IDE (viaide)
* 0.22.2 1106.3038 USB 0/1 (uhci)
* 0.22.3 1106.3038 USB 2/3 (uhci)
* 0.22.4 1106.3057 power management
* 0.22.5 1106.3058 AC97 (auvia)
*/
pcib = pcimaketag(0, 22, 0);
ide = pcimaketag(0, 22, 1);
usb12 = pcimaketag(0, 22, 2);
umot4 = pcimaketag(0, 22, 3);
pmgt = pcimaketag(0, 22, 4);
ac97 = pcimaketag(0, 22, 5);
#define CFG(i,v) do { \
*(volatile unsigned char *)(0xfe000000 + 0x3f0) = (i); \
*(volatile unsigned char *)(0xfe000000 + 0x3f1) = (v); \
} while (0)
val = pcicfgread(pcib, 0x84);
val |= (02 << 8);
pcicfgwrite(pcib, 0x84, val);
CFG(0xe2, 0x0f); /* use COM1/2, don't use FDC/LPT */
val = pcicfgread(pcib, 0x84);
val &= ~(02 << 8);
pcicfgwrite(pcib, 0x84, val);
/* route pin C to i8259 IRQ 5, pin D to 11 */
val = pcicfgread(pcib, 0x54);
val = (val & 0xff) | 0xb0500000; /* Dx CB Ax xS */
pcicfgwrite(pcib, 0x54, val);
/* enable EISA ELCR1 (0x4d0) and ELCR2 (0x4d1) */
val = pcicfgread(pcib, 0x44);
val = val | 0x20000000;
pcicfgwrite(pcib, 0x44, val);
/* select level trigger for IRQ 5/11 at ELCR1/2 */
*(volatile uint8_t *)0xfe0004d0 = 0x20; /* bit 5 */
*(volatile uint8_t *)0xfe0004d1 = 0x08; /* bit 11 */
/* USB and AC97 are hardwired with pin D and C */
val = pcicfgread(usb12, 0x3c) &~ 0xff;
val |= 11;
pcicfgwrite(usb12, 0x3c, val);
val = pcicfgread(umot4, 0x3c) &~ 0xff;
val |= 11;
pcicfgwrite(umot4, 0x3c, val);
val = pcicfgread(ac97, 0x3c) &~ 0xff;
val |= 5;
pcicfgwrite(ac97, 0x3c, val);
}
void
motsetup(struct brdprop *brd)
{
#ifdef COSNAME
brd->consname = CONSNAME;
#endif
#ifdef CONSPORT
brd->consport = CONSPORT;
#endif
#ifdef CONSSPEED
brd->consspeed = CONSSPEED;
#endif
}
void
motbrdfix(struct brdprop *brd)
{
/*
* WinBond/Symphony Lab 83C553 with PC87308 "SuperIO"
*
* 0.11.0 10ad.0565 PCI-ISA bridge
* 0.11.1 10ad.0105 IDE (slide)
*/
}
void
motpcifix(struct brdprop *brd)
{
unsigned ide, nic, pcib, steer, val;
int line;
pcib = pcimaketag(0, 11, 0);
ide = pcimaketag(0, 11, 1);
nic = pcimaketag(0, 15, 0);
/*
* //// WinBond PIRQ ////
* 0x40 - bit 5 (0x20) indicates PIRQ presense
* 0x60 - PIRQ interrupt routing steer
*/
if (pcicfgread(pcib, 0x40) & 0x20) {
steer = pcicfgread(pcib, 0x60);
if ((steer & 0x80808080) == 0x80808080)
printf("PIRQ[0-3] disabled\n");
else {
unsigned i, v = steer;
for (i = 0; i < 4; i++, v >>= 8) {
if ((v & 0x80) != 0 || (v & 0xf) == 0)
continue;
printf("PIRQ[%d]=%d\n", i, v & 0xf);
}
}
}
#if 1
/*
* //// IDE fixup -- case A ////
* - "native PCI mode" (ide 0x09)
* - don't use ISA IRQ14/15 (pcib 0x43)
* - native IDE for both channels (ide 0x40)
* - LEGIRQ bit 11 steers interrupt to pin C (ide 0x40)
* - sign as PCI pin C line 11 (ide 0x3d/3c)
*/
/* ide: 0x09 - programming interface; 1000'SsPp */
val = pcicfgread(ide, 0x08);
val &= 0xffff00ff;
pcicfgwrite(ide, 0x08, val | (0x8f << 8));
/* pcib: 0x43 - IDE interrupt routing */
val = pcicfgread(pcib, 0x40) & 0x00ffffff;
pcicfgwrite(pcib, 0x40, val);
/* pcib: 0x45/44 - PCI interrupt routing */
val = pcicfgread(pcib, 0x44) & 0xffff0000;
pcicfgwrite(pcib, 0x44, val);
/* ide: 0x41/40 - IDE channel */
val = pcicfgread(ide, 0x40) & 0xffff0000;
val |= (1 << 11) | 0x33; /* LEGIRQ turns on PCI interrupt */
pcicfgwrite(ide, 0x40, val);
/* ide: 0x3d/3c - use PCI pin C/line 11 */
val = pcicfgread(ide, 0x3c) & 0xffffff00;
val |= 11; /* pin designation is hardwired to pin A */
pcicfgwrite(ide, 0x3c, val);
#else
/*
* //// IDE fixup -- case B ////
* - "compatiblity mode" (ide 0x09)
* - IDE primary/secondary interrupt routing (pcib 0x43)
* - PCI interrupt routing (pcib 0x45/44)
* - no PCI pin/line assignment (ide 0x3d/3c)
*/
/* ide: 0x09 - programming interface; 1000'SsPp */
val = pcicfgread(ide, 0x08);
val &= 0xffff00ff;
pcicfgwrite(ide, 0x08, val | (0x8a << 8));
/* pcib: 0x43 - IDE interrupt routing */
val = pcicfgread(pcib, 0x40) & 0x00ffffff;
pcicfgwrite(pcib, 0x40, val | (0xee << 24));
/* ide: 0x45/44 - PCI interrupt routing */
val = pcicfgread(ide, 0x44) & 0xffff0000;
pcicfgwrite(ide, 0x44, val);
/* ide: 0x3d/3c - turn off PCI pin/line */
val = pcicfgread(ide, 0x3c) & 0xffff0000;
pcicfgwrite(ide, 0x3c, val);
#endif
/*
* //// fxp fixup ////
* - use PCI pin A line 15 (fxp 0x3d/3c)
*/
val = pcicfgread(nic, 0x3c) & 0xffff0000;
pcidecomposetag(nic, NULL, &line, NULL);
val |= (('A' - '@') << 8) | line;
pcicfgwrite(nic, 0x3c, val);
}
void
encpcifix(struct brdprop *brd)
{
unsigned ide, irq, nic, pcib, steer, val;
#define STEER(v, b) (((v) & (b)) ? "edge" : "level")
pcib = pcimaketag(0, 22, 0);
ide = pcimaketag(0, 22, 1);
nic = pcimaketag(0, 25, 0);
/*
* //// VIA PIRQ ////
* 0x57/56/55/54 - Dx CB Ax xS
*/
val = pcicfgread(pcib, 0x54); /* Dx CB Ax xs */
steer = val & 0xf;
irq = (val >> 12) & 0xf; /* 15:12 */
if (irq) {
printf("pin A -> irq %d, %s\n",
irq, STEER(steer, 0x1));
}
irq = (val >> 16) & 0xf; /* 19:16 */
if (irq) {
printf("pin B -> irq %d, %s\n",
irq, STEER(steer, 0x2));
}
irq = (val >> 20) & 0xf; /* 23:20 */
if (irq) {
printf("pin C -> irq %d, %s\n",
irq, STEER(steer, 0x4));
}
irq = (val >> 28); /* 31:28 */
if (irq) {
printf("pin D -> irq %d, %s\n",
irq, STEER(steer, 0x8));
}
#if 0
/*
* //// IDE fixup ////
* - "native mode" (ide 0x09)
* - use primary only (ide 0x40)
*/
/* ide: 0x09 - programming interface; 1000'SsPp */
val = pcicfgread(ide, 0x08) & 0xffff00ff;
pcicfgwrite(ide, 0x08, val | (0x8f << 8));
/* ide: 0x10-20 - leave them PCI memory space assigned */
/* ide: 0x40 - use primary only */
val = pcicfgread(ide, 0x40) &~ 03;
val |= 02;
pcicfgwrite(ide, 0x40, val);
#else
/*
* //// IDE fixup ////
* - "compatiblity mode" (ide 0x09)
* - use primary only (ide 0x40)
* - remove PCI pin assignment (ide 0x3d)
*/
/* ide: 0x09 - programming interface; 1000'SsPp */
val = pcicfgread(ide, 0x08) & 0xffff00ff;
val |= (0x8a << 8);
pcicfgwrite(ide, 0x08, val);
/* ide: 0x10-20 */
/*
experiment shows writing ide: 0x09 changes these
register behaviour. The pcicfgwrite() above writes
0x8a at ide: 0x09 to make sure legacy IDE. Then
reading BAR0-3 is to return value 0s even though
pcisetup() has written range assignments. Value
overwrite makes no effect. Having 0x8f for native
PCIIDE doesn't change register values and brings no
weirdness.
*/
/* ide: 0x40 - use primary only */
val = pcicfgread(ide, 0x40) &~ 03;
val |= 02;
pcicfgwrite(ide, 0x40, val);
/* ide: 0x3d/3c - turn off PCI pin */
val = pcicfgread(ide, 0x3c) & 0xffff00ff;
pcicfgwrite(ide, 0x3c, val);
#endif
/*
* //// USBx2, audio, and modem fixup ////
* - disable USB #0 and #1 (pcib 0x48 and 0x85)
* - disable AC97 audio and MC97 modem (pcib 0x85)
*/
/* pcib: 0x48 - disable USB #0 at function 2 */
val = pcicfgread(pcib, 0x48);
pcicfgwrite(pcib, 0x48, val | 04);
/* pcib: 0x85 - disable USB #1 at function 3 */
/* pcib: 0x85 - disable AC97/MC97 at function 5/6 */
val = pcicfgread(pcib, 0x84);
pcicfgwrite(pcib, 0x84, val | 0x1c00);
/*
* //// fxp fixup ////
* - use PCI pin A line 25 (fxp 0x3d/3c)
*/
/* 0x3d/3c - PCI pin/line */
val = pcicfgread(nic, 0x3c) & 0xffff0000;
val |= (('A' - '@') << 8) | 25;
pcicfgwrite(nic, 0x3c, val);
}
void
kurosetup(struct brdprop *brd)
{
if (PCI_VENDOR(pcicfgread(pcimaketag(0, 11, 0), PCI_ID_REG)) == 0x10ec)
brd->extclk = 32768000; /* decr 2457600Hz */
else
brd->extclk = 32521333; /* decr 2439100Hz */
}
void
kurobrdfix(struct brdprop *brd)
{
init_uart(uart2base, 9600, LCR_8BITS | LCR_PEVEN);
/* Stop Watchdog */
send_sat("AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK");
}
void
kuropcifix(struct brdprop *brd)
{
unsigned ide, nic, usb, val;
nic = pcimaketag(0, 11, 0);
val = pcicfgread(nic, 0x3c) & 0xffffff00;
val |= 11;
pcicfgwrite(nic, 0x3c, val);
ide = pcimaketag(0, 12, 0);
val = pcicfgread(ide, 0x3c) & 0xffffff00;
val |= 12;
pcicfgwrite(ide, 0x3c, val);
usb = pcimaketag(0, 14, 0);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
usb = pcimaketag(0, 14, 1);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
usb = pcimaketag(0, 14, 2);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
}
void
synosetup(struct brdprop *brd)
{
/* nothing */
}
void
synobrdfix(struct brdprop *brd)
{
init_uart(uart2base, 9600, LCR_8BITS | LCR_PNONE);
/* beep, power LED on, status LED off */
send_sat("247");
}
void
synopcifix(struct brdprop *brd)
{
unsigned ide, nic, usb, val;
ide = pcimaketag(0, 13, 0);
val = pcicfgread(ide, 0x3c) & 0xffffff00;
val |= 13;
pcicfgwrite(ide, 0x3c, val);
usb = pcimaketag(0, 14, 0);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
usb = pcimaketag(0, 14, 1);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
usb = pcimaketag(0, 14, 2);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
nic = pcimaketag(0, 15, 0);
val = pcicfgread(nic, 0x3c) & 0xffffff00;
val |= 15;
pcicfgwrite(nic, 0x3c, val);
}
void
qnappcifix(struct brdprop *brd)
{
unsigned ide, nic, usb, val;
ide = pcimaketag(0, 13, 0);
val = pcicfgread(ide, 0x3c) & 0xffffff00;
val |= 13;
pcicfgwrite(ide, 0x3c, val);
usb = pcimaketag(0, 14, 0);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
usb = pcimaketag(0, 14, 1);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
usb = pcimaketag(0, 14, 2);
val = pcicfgread(usb, 0x3c) & 0xffffff00;
val |= 14;
pcicfgwrite(usb, 0x3c, val);
nic = pcimaketag(0, 15, 0);
val = pcicfgread(nic, 0x3c) & 0xffffff00;
val |= 15;
pcicfgwrite(nic, 0x3c, val);
}
void
synoreset()
{
send_sat("C");
/*NOTRECHED*/
}
void
_rtt(void)
{
if (brdprop->reset != NULL)
(*brdprop->reset)();
else
run(0, 0, 0, 0, (void *)0xFFF00100); /* reset entry */
/*NOTREACHED*/
}
satime_t
getsecs(void)
{
u_quad_t tb = mftb();
return (tb / ticks_per_sec);
}
/*
* Wait for about n microseconds (at least!).
*/
void
delay(u_int n)
{
u_quad_t tb;
u_long scratch, tbh, tbl;
tb = mftb();
tb += (n * 1000 + ns_per_tick - 1) / ns_per_tick;
tbh = tb >> 32;
tbl = tb;
asm volatile ("1: mftbu %0; cmpw %0,%1; blt 1b; bgt 2f; mftb %0; cmpw 0, %0,%2; blt 1b; 2:" : "=&r"(scratch) : "r"(tbh), "r"(tbl));
}
void
_wb(uint32_t adr, uint32_t siz)
{
uint32_t bnd;
asm volatile("eieio");
for (bnd = adr + siz; adr < bnd; adr += dcache_line_size)
asm volatile ("dcbst 0,%0" :: "r"(adr));
asm volatile ("sync");
}
void
_wbinv(uint32_t adr, uint32_t siz)
{
uint32_t bnd;
asm volatile("eieio");
for (bnd = adr + siz; adr < bnd; adr += dcache_line_size)
asm volatile ("dcbf 0,%0" :: "r"(adr));
asm volatile ("sync");
}
void
_inv(uint32_t adr, uint32_t siz)
{
uint32_t bnd, off;
off = adr & (dcache_line_size - 1);
adr -= off;
siz += off;
asm volatile ("eieio");
if (off != 0) {
/* wbinv() leading unaligned dcache line */
asm volatile ("dcbf 0,%0" :: "r"(adr));
if (siz < dcache_line_size)
goto done;
adr += dcache_line_size;
siz -= dcache_line_size;
}
bnd = adr + siz;
off = bnd & (dcache_line_size - 1);
if (off != 0) {
/* wbinv() trailing unaligned dcache line */
asm volatile ("dcbf 0,%0" :: "r"(bnd)); /* it's OK */
if (siz < dcache_line_size)
goto done;
siz -= off;
}
for (bnd = adr + siz; adr < bnd; adr += dcache_line_size) {
/* inv() intermediate dcache lines if ever */
asm volatile ("dcbi 0,%0" :: "r"(adr));
}
done:
asm volatile ("sync");
}
static inline uint32_t
cputype(void)
{
uint32_t pvr;
__asm volatile ("mfpvr %0" : "=r"(pvr));
return pvr >> 16;
}
static inline u_quad_t
mftb(void)
{
u_long scratch;
u_quad_t tb;
asm ("1: mftbu %0; mftb %0+1; mftbu %1; cmpw %0,%1; bne 1b"
: "=r"(tb), "=r"(scratch));
return (tb);
}
static void
init_uart(unsigned base, unsigned speed, uint8_t lcr)
{
unsigned div;
div = busclock / speed / 16;
UART_WRITE(base, LCR, 0x80); /* turn on DLAB bit */
UART_WRITE(base, FCR, 0x00);
UART_WRITE(base, DMB, div >> 8); /* set speed */
UART_WRITE(base, DLB, div & 0xff);
UART_WRITE(base, LCR, lcr);
UART_WRITE(base, FCR, 0x07); /* FIFO on, TXRX FIFO reset */
UART_WRITE(base, IER, 0x00); /* make sure INT disabled */
}
/* talk to satellite processor */
static void
send_sat(char *msg)
{
unsigned savedbase;
savedbase = uart1base;
uart1base = uart2base;
while (*msg)
putchar(*msg++);
uart1base = savedbase;
}
void
putchar(int c)
{
unsigned timo, lsr;
if (c == '\n')
putchar('\r');
timo = 0x00100000;
do {
lsr = UART_READ(uart1base, LSR);
} while (timo-- > 0 && (lsr & LSR_THRE) == 0);
if (timo > 0)
UART_WRITE(uart1base, THR, c);
}
unsigned
mpc107memsize()
{
unsigned bankn, end, n, tag, val;
tag = pcimaketag(0, 0, 0);
if (brdtype == BRD_ENCOREPP1) {
/* the brd's PPCBOOT looks to have erroneous values */
unsigned tbl[] = {
#define MPC106_MEMSTARTADDR1 0x80
#define MPC106_EXTMEMSTARTADDR1 0x88
#define MPC106_MEMENDADDR1 0x90
#define MPC106_EXTMEMENDADDR1 0x98
#define MPC106_MEMEN 0xa0
#define BK0_S 0x00000000
#define BK0_E (128 << 20) - 1
#define BK1_S 0x3ff00000
#define BK1_E 0x3fffffff
#define BK2_S 0x3ff00000
#define BK2_E 0x3fffffff
#define BK3_S 0x3ff00000
#define BK3_E 0x3fffffff
#define AR(v, s) ((((v) & SAR_MASK) >> SAR_SHIFT) << (s))
#define XR(v, s) ((((v) & EAR_MASK) >> EAR_SHIFT) << (s))
#define SAR_MASK 0x0ff00000
#define SAR_SHIFT 20
#define EAR_MASK 0x30000000
#define EAR_SHIFT 28
AR(BK0_S, 0) | AR(BK1_S, 8) | AR(BK2_S, 16) | AR(BK3_S, 24),
XR(BK0_S, 0) | XR(BK1_S, 8) | XR(BK2_S, 16) | XR(BK3_S, 24),
AR(BK0_E, 0) | AR(BK1_E, 8) | AR(BK2_E, 16) | AR(BK3_E, 24),
XR(BK0_E, 0) | XR(BK1_E, 8) | XR(BK2_E, 16) | XR(BK3_E, 24),
};
tag = pcimaketag(0, 0, 0);
pcicfgwrite(tag, MPC106_MEMSTARTADDR1, tbl[0]);
pcicfgwrite(tag, MPC106_EXTMEMSTARTADDR1, tbl[1]);
pcicfgwrite(tag, MPC106_MEMENDADDR1, tbl[2]);
pcicfgwrite(tag, MPC106_EXTMEMENDADDR1, tbl[3]);
pcicfgwrite(tag, MPC106_MEMEN, 1);
}
bankn = 0;
val = pcicfgread(tag, MPC106_MEMEN);
for (n = 0; n < 4; n++) {
if ((val & (1U << n)) == 0)
break;
bankn = n;
}
bankn = bankn * 8;
val = pcicfgread(tag, MPC106_EXTMEMENDADDR1);
end = ((val >> bankn) & 0x03) << 28;
val = pcicfgread(tag, MPC106_MEMENDADDR1);
end |= ((val >> bankn) & 0xff) << 20;
end |= 0xfffff;
return (end + 1); /* assume the end address matches total amount */
}
struct fis_dir_entry {
char name[16];
uint32_t startaddr;
uint32_t loadaddr;
uint32_t flashsize;
uint32_t entryaddr;
uint32_t filesize;
char pad[256 - (16 + 5 * sizeof(uint32_t))];
};
#define FIS_LOWER_LIMIT 0xfff00000
/*
* Look for a Redboot-style Flash Image System FIS-directory and
* return a pointer to the start address of the requested file.
*/
static void *
redboot_fis_lookup(const char *filename)
{
static const char FISdirname[16] = {
'F', 'I', 'S', ' ',
'd', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', 0, 0, 0
};
struct fis_dir_entry *dir;
/*
* The FIS directory is usually in the last sector of the flash.
* But we do not know the sector size (erase size), so start
* at 0xffffff00 and scan backwards in steps of the FIS directory
* entry size (0x100).
*/
for (dir = (struct fis_dir_entry *)0xffffff00;
(uint32_t)dir >= FIS_LOWER_LIMIT; dir--)
if (memcmp(dir->name, FISdirname, sizeof(FISdirname)) == 0)
break;
if ((uint32_t)dir < FIS_LOWER_LIMIT) {
printf("No FIS directory found!\n");
return NULL;
}
/* Now find filename by scanning the directory from beginning. */
dir = (struct fis_dir_entry *)dir->startaddr;
while (dir->name[0] != 0xff && (uint32_t)dir < 0xffffff00) {
if (strcmp(dir->name, filename) == 0)
return (void *)dir->startaddr; /* found */
dir++;
}
printf("\"%s\" not found in FIS directory!\n", filename);
return NULL;
}
/*
* For cost saving reasons some NAS boxes are missing the ROM for the
* NIC's ethernet address and keep it in their Flash memory.
*/
void
read_mac_from_flash(uint8_t *mac)
{
uint8_t *p;
if (brdtype == BRD_SYNOLOGY) {
p = redboot_fis_lookup("vendor");
if (p != NULL) {
memcpy(mac, p, 6);
return;
}
} else
printf("Warning: This board has no known method defined "
"to determine its MAC address!\n");
/* set to 00:00:00:00:00:00 in case of error */
memset(mac, 0, 6);
}
/* $NetBSD: dev_net.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <lib/libsa/bootp.h>
#include <lib/libsa/nfs.h>
#include <lib/libkern/libkern.h>
#include <machine/bootinfo.h>
#include <machine/stdarg.h>
#include "globals.h"
static int netdev_sock = -1;
static int netdev_opens;
int
net_open(struct open_file *f, ...)
{
va_list ap;
char *file, *proto;
int error;
extern struct btinfo_bootpath bi_path;
va_start(ap, f);
file = va_arg(ap, char *);
proto = va_arg(ap, char *);
va_end(ap);
if (netdev_opens > 0)
return 0;
if ((netdev_sock = netif_open(NULL)) < 0)
return ENXIO; /* never fails indeed */
error = 0;
bootp(netdev_sock); /* send DHCP request */
if (myip.s_addr == 0) {
error = ENOENT; /* IP address was not found */
goto bad;
}
if (file[0] != '\0')
snprintf(bootfile, sizeof(bootfile), file);
else if (bootfile[0] == '\0')
snprintf(bootfile, sizeof(bootfile), "netbsd");
if (strcmp(proto, "nfs") == 0
&& (error = nfs_mount(netdev_sock, rootip, rootpath)) != 0)
goto bad;
snprintf(bi_path.bootpath, sizeof(bi_path.bootpath), bootfile);
f->f_devdata = &netdev_sock;
netdev_opens++;
return 0;
bad:
netif_close(netdev_sock);
netdev_sock = -1;
return error;
}
int
net_close(struct open_file *f)
{
f->f_devdata = NULL;
if (--netdev_opens > 0)
return 0;
netif_close(netdev_sock);
netdev_sock = -1;
return 0;
}
int
net_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
void *p, size_t *rsize)
{
return EIO;
}
/* $NetBSD: devopen.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/nfs.h>
#include <lib/libsa/ufs.h>
#include <lib/libsa/tftp.h>
#include <lib/libkern/libkern.h>
#include "globals.h"
struct devsw devnet = { "net", net_strategy, net_open, net_close, noioctl };
struct devsw devdsk = { "dsk", dsk_strategy, dsk_open, dsk_close, noioctl };
struct fs_ops file_system[1] = { FS_OPS(null) };
int nfsys = 1;
struct fs_ops fs_nfs = FS_OPS(nfs);
struct fs_ops fs_tftp = FS_OPS(tftp);
struct fs_ops fs_ffsv2 = FS_OPS(ffsv2);
struct fs_ops fs_ffsv1 = FS_OPS(ffsv1);
extern char *fsmod;
static void parseunit(const char *, int *, int *, char **);
int
devopen(struct open_file *of, const char *name, char **file)
{
int error;
int unit, part;
extern char bootfile[]; /* handed by DHCP */
if (of->f_flags != F_READ)
return EPERM;
if (strncmp("net:", name, 4) == 0 || strncmp("nfs:", name, 4) == 0) {
of->f_dev = &devnet;
if ((error = net_open(of, &name[4], "nfs")) != 0)
return error;
file_system[0] = fs_nfs;
*file = bootfile; /* resolved fname */
return 0; /* NFS */
}
if (strncmp("tftp:", name, 5) == 0) {
of->f_dev = &devnet;
if ((error = net_open(of, &name[5], "tftp")) != 0)
return error;
file_system[0] = fs_tftp;
*file = bootfile; /* resolved fname */
return 0; /* TFTP */
}
if (name[0] == 'w' && name[1] == 'd') {
parseunit(&name[2], &unit, &part, file);
of->f_dev = &devdsk;
if (*file == NULL || **file <= ' ')
*file = "netbsd";
if ((error = dsk_open(of, unit, part, *file)) != 0)
return error;
file_system[0] = *dsk_fsops(of);
return 0; /* FFS */
}
return ENOENT;
}
static void
parseunit(const char *name, int *unitp, int *partp, char **pathp)
{
const char *p = name;
int unit, part;
unit = part = -1;
while (*p != ':' && *p != '\0') {
if (unit == -1 && *p >= '0' && *p <= '9')
unit = *p - '0';
if (part == -1 && *p >= 'a' && *p < 'a' + 16)
part = *p - 'a';
p += 1;
}
*unitp = (unit == -1) ? 0 : unit;
*partp = (part == -1) ? 0 : part;
*pathp = (*p == ':') ? (char *)p + 1 : NULL;
}
/* ARGSUSED */
int
noioctl(struct open_file *f, u_long cmd, void *data)
{
return EINVAL;
}
/* $NetBSD: dsk.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2010 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.
*
* 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.
*/
/*
* assumptions;
* - up to 4 IDE/SATA drives.
* - a single (master) drive in each IDE channel.
* - all drives are up and spinning.
*/
#include <sys/types.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/ufs.h>
#include <sys/disklabel.h>
#include <sys/bootblock.h>
#include <machine/bootinfo.h>
#include <machine/stdarg.h>
#include "globals.h"
/*
* - no vtophys() translation, vaddr_t == paddr_t.
*/
#define CSR_READ_4(r) in32rb(r)
#define CSR_WRITE_4(r,v) out32rb(r,v)
#define CSR_READ_1(r) *(volatile uint8_t *)(r)
#define CSR_WRITE_1(r,v) *(volatile uint8_t *)(r)=(v)
#define DSK_DECL(xxx) \
int xxx ## _match(unsigned, void *); \
void * xxx ## _init(unsigned, void *)
DSK_DECL(pciide);
DSK_DECL(siisata);
struct dskdv {
char *name;
int (*match)(unsigned, void *);
void *(*init)(unsigned, void *);
void *priv;
};
static struct dskdv ldskdv[] = {
{ "pciide", pciide_match, pciide_init, },
{ "siisata", siisata_match, siisata_init, },
};
static int ndskdv = sizeof(ldskdv)/sizeof(ldskdv[0]);
static int probe_drive(struct dkdev_ata *, int);
static void drive_ident(struct disk *, char *);
static char *mkident(char *, int);
static void set_xfermode(struct dkdev_ata *, int);
static void decode_dlabel(struct disk *, char *);
static int lba_read(struct disk *, uint64_t, uint32_t, void *);
static void issue48(struct dvata_chan *, uint64_t, uint32_t);
static void issue28(struct dvata_chan *, uint64_t, uint32_t);
static struct disk *lookup_disk(int);
static struct disk ldisk[4];
int
dskdv_init(unsigned tag, void **cookie)
{
struct dskdv *dv;
int n;
for (n = 0; n < ndskdv; n++) {
dv = &ldskdv[n];
if ((*dv->match)(tag, NULL) > 0)
goto found;
}
return 0;
found:
dv->priv = (*dv->init)(tag, NULL);
*cookie = dv;
return 1;
}
int
disk_scan(void *cookie)
{
struct dskdv *dv = cookie;
struct dkdev_ata *l = dv->priv;
struct disk *d;
int n, ndrive;
ndrive = 0;
for (n = 0; n < 4; n++) {
if (l->presense[n] == 0)
continue;
if (probe_drive(l, n) == 0) {
l->presense[n] = 0;
continue;
}
d = &ldisk[ndrive];
d->dvops = l;
d->unittag = ndrive;
snprintf(d->xname, sizeof(d->xname), "wd%d", d->unittag);
set_xfermode(l, n);
drive_ident(d, l->iobuf);
decode_dlabel(d, l->iobuf);
ndrive += 1;
}
return ndrive;
}
int
spinwait_unbusy(struct dkdev_ata *l, int n, int milli, const char **err)
{
struct dvata_chan *chan = &l->chan[n];
int sts;
const char *msg;
/*
* For best compatibility it is recommended to wait 400ns and
* read the alternate status byte four times before the status
* is valid.
*/
delay(1);
(void)CSR_READ_1(chan->alt);
(void)CSR_READ_1(chan->alt);
(void)CSR_READ_1(chan->alt);
(void)CSR_READ_1(chan->alt);
sts = CSR_READ_1(chan->cmd + _STS);
while (milli-- > 0
&& sts != 0xff
&& (sts & (ATA_STS_BUSY|ATA_STS_DRDY)) != ATA_STS_DRDY) {
delay(1000);
sts = CSR_READ_1(chan->cmd + _STS);
}
msg = NULL;
if (sts == 0xff)
msg = "returned 0xff";
else if (sts & ATA_STS_ERR)
msg = "returned ERR";
else if (sts & ATA_STS_BUSY)
msg = "remains BUSY";
else if ((sts & ATA_STS_DRDY) == 0)
msg = "no DRDY";
if (err != NULL)
*err = msg;
return msg == NULL;
}
int
perform_atareset(struct dkdev_ata *l, int n)
{
struct dvata_chan *chan = &l->chan[n];
CSR_WRITE_1(chan->ctl, ATA_DREQ);
delay(10);
CSR_WRITE_1(chan->ctl, ATA_SRST|ATA_DREQ);
delay(10);
CSR_WRITE_1(chan->ctl, ATA_DREQ);
return spinwait_unbusy(l, n, 150, NULL);
}
int
satapresense(struct dkdev_ata *l, int n)
{
#define VND_CH(n) (((n&02)<<8)+((n&01)<<7))
#define VND_SC(n) (0x100+VND_CH(n))
#define VND_SS(n) (0x104+VND_CH(n))
uint32_t sc = l->bar[5] + VND_SC(n);
uint32_t ss = l->bar[5] + VND_SS(n);
unsigned val;
val = (00 << 4) | (03 << 8); /* any speed, no pwrmgt */
CSR_WRITE_4(sc, val | 01); /* perform init */
delay(50 * 1000);
CSR_WRITE_4(sc, val);
delay(50 * 1000);
val = CSR_READ_4(ss); /* has completed */
return ((val & 03) == 03); /* active drive found */
}
static int
probe_drive(struct dkdev_ata *l, int n)
{
struct dvata_chan *chan = &l->chan[n];
uint16_t *p;
int i;
CSR_WRITE_1(chan->cmd + _CMD, ATA_CMD_IDENT);
(void)CSR_READ_1(chan->alt);
delay(10 * 1000);
if (spinwait_unbusy(l, n, 1000, NULL) == 0)
return 0;
p = (uint16_t *)l->iobuf;
for (i = 0; i < 512; i += 2) {
/* need to have bswap16 */
*p++ = iole16toh(chan->cmd + _DAT);
}
(void)CSR_READ_1(chan->cmd + _STS);
return 1;
}
static void
drive_ident(struct disk *d, char *ident)
{
uint16_t *p;
uint64_t huge;
p = (uint16_t *)ident;
#if 1
printf("[49]%04x [82]%04x [83]%04x [84]%04x "
"[85]%04x [86]%04x [87]%04x [88]%04x\n",
p[49], p[82], p[83], p[84],
p[85], p[86], p[87], p[88]);
#endif
huge = 0;
printf("%s: ", d->xname);
printf("<%s> ", mkident((char *)ident + 54, 40));
if (p[49] & (1 << 8))
printf("DMA ");
if (p[49] & (1 << 9)) {
printf("LBA ");
huge = p[60] | (p[61] << 16);
}
if ((p[83] & 0xc000) == 0x4000 && (p[83] & (1 << 10))) {
printf("LBA48 ");
huge = p[100] | (p[101] << 16);
huge |= (uint64_t)p[102] << 32;
huge |= (uint64_t)p[103] << 48;
}
huge >>= (1 + 10);
printf("%d MB\n", (int)huge);
memcpy(d->ident, ident, sizeof(d->ident));
d->nsect = huge;
d->lba_read = lba_read;
}
static char *
mkident(char *src, int len)
{
static char local[40];
char *dst, *end, *last;
if (len > sizeof(local))
len = sizeof(local);
dst = last = local;
end = src + len - 1;
/* reserve space for '\0' */
if (len < 2)
goto out;
/* skip leading white space */
while (*src != '\0' && src < end && *src == ' ')
++src;
/* copy string, omitting trailing white space */
while (*src != '\0' && src < end) {
*dst++ = *src;
if (*src++ != ' ')
last = dst;
}
out:
*last = '\0';
return local;
}
static void
decode_dlabel(struct disk *d, char *iobuf)
{
struct mbr_partition *mp, *bsdp;
struct disklabel *dlp;
struct partition *pp;
char *dp;
int i, first;
bsdp = NULL;
(*d->lba_read)(d, 0, 1, iobuf);
if (bswap16(*(uint16_t *)(iobuf + MBR_MAGIC_OFFSET)) != MBR_MAGIC)
goto skip;
mp = (struct mbr_partition *)(iobuf + MBR_PART_OFFSET);
for (i = 0; i < MBR_PART_COUNT; i++, mp++) {
if (mp->mbrp_type == MBR_PTYPE_NETBSD) {
bsdp = mp;
break;
}
}
skip:
first = (bsdp) ? bswap32(bsdp->mbrp_start) : 0;
(*d->lba_read)(d, first + LABELSECTOR, 1, iobuf);
dp = iobuf /* + LABELOFFSET */;
for (i = 0; i < 512 - sizeof(struct disklabel); i++, dp += 4) {
dlp = (struct disklabel *)dp;
if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) {
goto found;
}
}
d->dlabel = NULL;
printf("%s: no disklabel\n", d->xname);
return;
found:
d->dlabel = allocaligned(sizeof(struct disklabel), 4);
memcpy(d->dlabel, dlp, sizeof(struct disklabel));
#if 1
for (i = 0; i < dlp->d_npartitions; i += 1) {
const char *type;
pp = &dlp->d_partitions[i];
type = NULL;
switch (pp->p_fstype) {
case FS_SWAP: /* swap */
type = "swap";
break;
case FS_BSDFFS:
type = "ffs";
break;
case FS_EX2FS:
type = "ext2fs";
break;
}
if (type != NULL)
printf("%s%c: %s\n", d->xname, i + 'a', type);
}
#endif
}
static void
set_xfermode(struct dkdev_ata *l, int n)
{
struct dvata_chan *chan = &l->chan[n];
CSR_WRITE_1(chan->cmd + _FEA, ATA_XFER);
CSR_WRITE_1(chan->cmd + _NSECT, XFER_PIO0);
CSR_WRITE_1(chan->cmd + _DEV, ATA_DEV_OBS); /* ??? */
CSR_WRITE_1(chan->cmd + _CMD, ATA_CMD_SETF);
spinwait_unbusy(l, n, 1000, NULL);
}
static int
lba_read(struct disk *d, uint64_t bno, uint32_t bcnt, void *buf)
{
struct dkdev_ata *l;
struct dvata_chan *chan;
void (*issue)(struct dvata_chan *, uint64_t, uint32_t);
int n, rdcnt, i, k;
uint16_t *p;
const char *err;
int error;
l = d->dvops;
n = d->unittag;
p = (uint16_t *)buf;
chan = &l->chan[n];
error = 0;
for ( ; bcnt > 0; bno += rdcnt, bcnt -= rdcnt) {
issue = (bno < (1ULL<<28)) ? issue28 : issue48;
rdcnt = (bcnt > 255) ? 255 : bcnt;
(*issue)(chan, bno, rdcnt);
for (k = 0; k < rdcnt; k++) {
if (spinwait_unbusy(l, n, 1000, &err) == 0) {
printf("%s blk %d %s\n",
d->xname, (int)bno, err);
error = EIO;
break;
}
for (i = 0; i < 512; i += 2) {
/* arrives in native order */
*p++ = *(uint16_t *)(chan->cmd + _DAT);
}
/* clear irq if any */
(void)CSR_READ_1(chan->cmd + _STS);
}
}
return error;
}
static void
issue48(struct dvata_chan *chan, uint64_t bno, uint32_t nblk)
{
CSR_WRITE_1(chan->cmd + _NSECT, 0); /* always less than 256 */
CSR_WRITE_1(chan->cmd + _LBAL, (bno >> 24) & 0xff);
CSR_WRITE_1(chan->cmd + _LBAM, (bno >> 32) & 0xff);
CSR_WRITE_1(chan->cmd + _LBAH, (bno >> 40) & 0xff);
CSR_WRITE_1(chan->cmd + _NSECT, nblk);
CSR_WRITE_1(chan->cmd + _LBAL, (bno >> 0) & 0xff);
CSR_WRITE_1(chan->cmd + _LBAM, (bno >> 8) & 0xff);
CSR_WRITE_1(chan->cmd + _LBAH, (bno >> 16) & 0xff);
CSR_WRITE_1(chan->cmd + _DEV, ATA_DEV_LBA);
CSR_WRITE_1(chan->cmd + _CMD, ATA_CMD_READ_EXT);
}
static void
issue28(struct dvata_chan *chan, uint64_t bno, uint32_t nblk)
{
CSR_WRITE_1(chan->cmd + _NSECT, nblk);
CSR_WRITE_1(chan->cmd + _LBAL, (bno >> 0) & 0xff);
CSR_WRITE_1(chan->cmd + _LBAM, (bno >> 8) & 0xff);
CSR_WRITE_1(chan->cmd + _LBAH, (bno >> 16) & 0xff);
CSR_WRITE_1(chan->cmd + _DEV, ((bno >> 24) & 0xf) | ATA_DEV_LBA);
CSR_WRITE_1(chan->cmd + _CMD, ATA_CMD_READ);
}
static struct disk *
lookup_disk(int unit)
{
return &ldisk[unit];
}
int
dsk_open(struct open_file *f, ...)
{
va_list ap;
int unit, part;
const char *name;
struct disk *d;
struct disklabel *dlp;
struct fs_ops *fs;
int error;
extern struct btinfo_bootpath bi_path;
extern struct btinfo_rootdevice bi_rdev;
extern struct fs_ops fs_ffsv2, fs_ffsv1;
va_start(ap, f);
unit = va_arg(ap, int);
part = va_arg(ap, int);
name = va_arg(ap, const char *);
va_end(ap);
if ((d = lookup_disk(unit)) == NULL)
return ENXIO;
f->f_devdata = d;
if ((dlp = d->dlabel) == NULL || part >= dlp->d_npartitions)
return ENXIO;
d->part = part;
snprintf(bi_path.bootpath, sizeof(bi_path.bootpath), name);
if (dlp->d_partitions[part].p_fstype == FS_BSDFFS) {
if ((error = ffsv2_open(name, f)) == 0) {
fs = &fs_ffsv2;
goto found;
}
if (error == EINVAL && (error = ffsv1_open(name, f)) == 0) {
fs = &fs_ffsv1;
goto found;
}
return error;
}
return ENXIO;
found:
#if 0
printf("dsk_open found %s\n", fsmod);
#endif
d->fsops = fs;
f->f_devdata = d;
/* build btinfo to identify disk device */
snprintf(bi_rdev.devname, sizeof(bi_rdev.devname), "wd");
bi_rdev.cookie = d->unittag; /* disk unit number */
return 0;
}
int
dsk_close(struct open_file *f)
{
struct disk *d = f->f_devdata;
struct fs_ops *fs = d->fsops;
(*fs->close)(f);
d->fsops = NULL;
f->f_devdata = NULL;
return 0;
}
int
dsk_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
void *p, size_t *rsize)
{
struct disk *d = devdata;
struct disklabel *dlp;
uint64_t bno;
#if 0
printf("%s %d %d\n", d->xname, (int)dblk, size);
#endif
if (size == 0)
return 0;
if (rw != F_READ)
return EOPNOTSUPP;
bno = dblk;
if ((dlp = d->dlabel) != NULL)
bno += dlp->d_partitions[d->part].p_offset;
(*d->lba_read)(d, bno, size / 512, p);
if (rsize != NULL)
*rsize = size;
return 0;
}
struct fs_ops *
dsk_fsops(struct open_file *f)
{
struct disk *d = f->f_devdata;
return d->fsops;
}
/* $NetBSD: entry.S,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
#include <powerpc/psl.h>
#include <powerpc/spr.h>
#include <powerpc/oea/spr.h>
#include <powerpc/oea/bat.h>
#include <powerpc/oea/hid.h>
.text
.globl _start
_start:
mr 30,3
mr 31,4
mfspr 11,SPR_HID0
andi. 0,11,HID0_DCE
ori 11,11,HID0_ICE
ori 8,11,HID0_ICFI
bne 1f /* don't invalidate the D-cache */
ori 8,8,HID0_DCFI /* unless it wasn't enabled */
1:
mfmsr 0
andi. 0,0,PSL_DR
beq 2f
lis 5, 0xfec00000@ha /* CONFIG_ADDR of PCI */
lis 6, 0xfee00000@ha /* CONFIG_DATA of PCI */
mfspr 3,SPR_DBAT0U
mfspr 4,SPR_DBAT0L
bl dbat_sanity_check
beq 3f
mfspr 3,SPR_DBAT1U
mfspr 4,SPR_DBAT1L
bl dbat_sanity_check
beq 3f
mfspr 3,SPR_DBAT2U
mfspr 4,SPR_DBAT2L
bl dbat_sanity_check
beq 3f
mfspr 3,SPR_DBAT3U
mfspr 4,SPR_DBAT3L
bl dbat_sanity_check
beq 3f
2: /* Disable D-cache */
li 0,HID0_DCE
andc 11,11,0
b 4f
3: /* Enable D-cache */
ori 11,11,HID0_DCE
4:
lis 1,BAT123@ha
addi 1,1,BAT123@l
lwz 3,0(1)
lwz 4,4(1)
mtdbatl 1,3
mtdbatu 1,4
lwz 3,8(1)
lwz 4,12(1)
mtdbatl 2,3
mtdbatu 2,4
lwz 3,16(1)
lwz 4,20(1)
mtdbatl 3,3
mtdbatu 3,4
sync
mtspr SPR_HID0,8 /* enable and invalidate caches */
sync
mtspr SPR_HID0,11 /* enable caches */
sync
isync
/* make sure .bss gets zeroed. */
li 0,0
lis 8,edata@ha
addi 8,8,edata@l
lis 9,end@ha
addi 9,9,end@l
5: cmpw 0,8,9 /* edata & end are >= word aligned */
bge 6f
stw 0,0(8)
addi 8,8,4
b 5b
6:
/* prepare stack at +1MB from _start. */
lis 1,_start@h
ori 1,1,_start@l
addis 1,1,0x10
addi 1,1,-4
bl brdsetup
mr 3,30
mr 4,31
bl main
hang: b hang
/* NOTREACHED */
dbat_sanity_check:
andi. 0,3,BAT_Vs
beq 2f
andi. 0,4,BAT_I|BAT_PP_RW
cmpwi 0,0,BAT_I|BAT_PP_RW
bnelr
rlwinm 0,3,15,4,14
andis. 3,3,0xfffe0000@ha /* BAT_EPI */
andis. 4,4,BAT_RPN@ha
cmplw 0,3,4
bnelr
add 4,4,0
oris 4,4,0x0001ffff@ha
ori 4,4,0x0001ffff@l
cmplw 0,3,5
bgt 1f
cmplw 0,5,4
bgt 1f
li 5,0
1: cmplw 0,3,6
bgt 2f
cmplw 0,6,4
bgt 2f
li 6,0
2: cmplw 0,5,6
blr
/*
* run(startsym, endsym, howto, bootinfo, entry)
*/
.globl run
run:
mtctr 7 /* hat trick jump to entry point */
bctr
/*
* reverse endian access to mimic outw/outl/inw/inl
*/
.globl out16rb
.globl iohtole16
out16rb:
iohtole16:
sthbrx 4,0,3
eieio
blr
.globl out32rb
.globl iohtole32
out32rb:
iohtole32:
stwbrx 4,0,3
eieio
blr
.global in16rb
.global iole16toh
in16rb:
iole16toh:
lhbrx 3,0,3
eieio
blr
.global in32rb
.global iole32toh
in32rb:
iole32toh:
lwbrx 3,0,3
eieio
blr
.data
#define xBATL(pa, wimg, pp) \
((pa) | (wimg) | (pp))
#define xBATU(va, len, v) \
((va) | ((len) & BAT_BL) | ((v) & BAT_V))
BAT123:
.long xBATL(0x80000000, BAT_I|BAT_G, BAT_PP_RW)
.long xBATU(0x80000000, BAT_BL_256M, BAT_Vs)
.long xBATL(0xfc000000, BAT_I|BAT_G, BAT_PP_RW)
.long xBATU(0xfc000000, BAT_BL_64M, BAT_Vs)
.long 0
.long 0
/* $NetBSD: fxp.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*
* most of the following code was imported from dev/ic/i82557.c; the
* original copyright notice is as below.
*/
/*-
* Copyright (c) 1997, 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
* NASA Ames Research Center.
*
* 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.
*
* 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.
*/
/*
* Copyright (c) 1995, David Greenman
* Copyright (c) 2001 Jonathan Lemon <jlemon@freebsd.org>
* All rights reserved.
*
* 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 unmodified, 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* Id: if_fxp.c,v 1.113 2001/05/17 23:50:24 jlemon
*/
#include <sys/param.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <dev/ic/i82557reg.h>
#include "globals.h"
#define FRAMESIZE 1536
/*
* 82559ER 8086.1209/1229
*
* - reverse endian access for 16bit/32bit register.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_WRITE_1(l, r, v) *(volatile uint8_t *)((l)->iobase+(r)) = (v)
#define CSR_READ_1(l, r) *(volatile uint8_t *)((l)->iobase+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->iobase+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->iobase+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->iobase+(r), (v))
#define CSR_READ_4(l, r) in32rb((l)->iobase+(r))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct txdesc {
volatile uint16_t cb_status;
volatile uint16_t cb_command;
volatile uint32_t link_addr;
volatile uint32_t tbd_array_addr;
volatile uint16_t byte_count;
volatile uint8_t tx_threshold;
volatile uint8_t tbd_number;
volatile uint32_t tx_buf_addr0;
volatile uint32_t tx_buf_size0;
volatile uint32_t tx_buf_addr1;
volatile uint32_t tx_buf_size1;
}; /* mimic extended TxCB layout */
struct rxdesc {
volatile uint16_t rfa_status;
volatile uint16_t rfa_control;
volatile uint32_t link_addr;
volatile uint32_t rbd_addr;
volatile uint16_t actual_size;
volatile uint16_t size;
}; /* 16B rfa */
struct local {
struct txdesc txd;
uint8_t store[sizeof(struct rxdesc) + FRAMESIZE];
unsigned iobase;
unsigned eeprom_addr;
};
static void autosize_eeprom(struct local *);
static int read_eeprom(struct local *, int);
static void fxp_scb_wait(struct local *);
static int fxp_mdi_read(struct local *, int, int);
/*
* Template for default configuration parameters.
* See struct fxp_cb_config for the bit definitions.
*/
static uint8_t fxp_cb_config_template[] = {
0x0, 0x0, /* cb_status */
0x80, 0x2, /* cb_command */
0xff, 0xff, 0xff, 0xff, /* link_addr */
0x16, /* 0 */
0x8, /* 1 */
0x0, /* 2 */
0x0, /* 3 */
0x0, /* 4 */
0x80, /* 5 */
0xb2, /* 6 */
0x3, /* 7 */
0x1, /* 8 */
0x0, /* 9 */
0x26, /* 10 */
0x0, /* 11 */
0x60, /* 12 */
0x0, /* 13 */
0xf2, /* 14 */
0x48, /* 15 */
0x0, /* 16 */
0x40, /* 17 */
0xf3, /* 18 */
0x0, /* 19 */
0x3f, /* 20 */
0x5 /* 21 */
};
static struct fxp_cb_config store_cbc;
static struct fxp_cb_ias store_cbi;
int
fxp_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x8086, 0x1209):
case PCI_DEVICE(0x8086, 0x1229):
return 1;
}
return 0;
}
void *
fxp_init(unsigned tag, void *data)
{
struct local *sc;
uint8_t *en = data;
struct fxp_cb_config *cbp = &store_cbc;
struct fxp_cb_ias *cb_ias = &store_cbi;
struct rxdesc *rfa;
unsigned v, i;
sc = ALLOC(struct local, sizeof(struct txdesc)); /* desc alignment */
memset(sc, 0, sizeof(struct local));
sc->iobase = DEVTOV(pcicfgread(tag, 0x10)); /* use mem space */
CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
DELAY(100);
autosize_eeprom(sc);
v = read_eeprom(sc, 0); en[0] = v; en[1] = v >> 8;
v = read_eeprom(sc, 1); en[2] = v; en[3] = v >> 8;
v = read_eeprom(sc, 2); en[4] = v; en[5] = v >> 8;
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x, ",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", fxp_mdi_read(sc, 1, 18),
fxp_mdi_read(sc, 1, 2), fxp_mdi_read(sc, 1, 3));
/*
* Initialize base of CBL and RFA memory. Loading with zero
* sets it up for regular linear addressing.
*/
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0);
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE);
fxp_scb_wait(sc);
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE);
/*
* This memcpy is kind of disgusting, but there are a bunch of must be
* zero and must be one bits in this structure and this is the easiest
* way to initialize them all to proper values.
*/
memcpy(cbp, fxp_cb_config_template, sizeof(fxp_cb_config_template));
#define prm 0
#define phy_10Mbps_only 0
#define all_mcasts 0
cbp->cb_status = 0;
cbp->cb_command = htole16(FXP_CB_COMMAND_CONFIG |
FXP_CB_COMMAND_EL);
cbp->link_addr = -1; /* (no) next command */
cbp->byte_count = 22; /* (22) bytes to config */
cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */
cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */
cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */
cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */
cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */
cbp->dma_mbce = 0; /* (disable) dma max counters */
cbp->late_scb = 0; /* (don't) defer SCB update */
cbp->tno_int_or_tco_en = 0; /* (disable) tx not okay interrupt */
cbp->ci_int = 0; /* interrupt on CU not active */
cbp->save_bf = prm; /* save bad frames */
cbp->disc_short_rx = !prm; /* discard short packets */
cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */
cbp->mediatype = !phy_10Mbps_only; /* interface mode */
cbp->nsai = 1; /* (don't) disable source addr insert */
cbp->preamble_length = 2; /* (7 byte) preamble */
cbp->loopback = 0; /* (don't) loopback */
cbp->linear_priority = 0; /* (normal CSMA/CD operation) */
cbp->linear_pri_mode = 0; /* (wait after xmit only) */
cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */
cbp->promiscuous = prm; /* promiscuous mode */
cbp->bcast_disable = 0; /* (don't) disable broadcasts */
cbp->crscdt = 0; /* (CRS only) */
cbp->stripping = !prm; /* truncate rx packet to byte count */
cbp->padding = 1; /* (do) pad short tx packets */
cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */
cbp->force_fdx = 0; /* (don't) force full duplex */
cbp->fdx_pin_en = 1; /* (enable) FDX# pin */
cbp->multi_ia = 0; /* (don't) accept multiple IAs */
cbp->mc_all = all_mcasts;/* accept all multicasts */
#undef prm
#undef phy_10Mbps_only
#undef all_mcasts
wbinv(cbp, sizeof(*cbp));
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(cbp));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
i = 1000;
while (!(le16toh(cbp->cb_status) & FXP_CB_STATUS_C) && --i > 0) {
DELAY(1);
inv(&cbp->cb_status, sizeof(cbp->cb_status));
}
if (i == 0)
printf("cbp config timeout\n");
/*
* Initialize the station address.
*/
cb_ias->cb_status = 0;
cb_ias->cb_command = htole16(FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL);
cb_ias->link_addr = -1;
memcpy(cb_ias->macaddr, en, 6);
/*
* Start the IAS (Individual Address Setup) command/DMA.
*/
wbinv(cb_ias, sizeof(*cb_ias));
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(cb_ias));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
i = 1000;
while (!(le16toh(cb_ias->cb_status) & FXP_CB_STATUS_C) && --i > 0) {
DELAY(1);
inv(&cb_ias->cb_status, sizeof(cb_ias->cb_status));
}
if (i == 0)
printf("ias config timeout\n");
rfa = (struct rxdesc *)sc->store;
rfa->rfa_status = 0;
rfa->rfa_control = htole16(FXP_RFA_CONTROL_S);
rfa->link_addr = htole32(VTOPHYS(rfa));
rfa->rbd_addr = -1;
rfa->actual_size = 0;
rfa->size = htole16(sizeof(sc->store) - sizeof(struct rxdesc));
wbinv(rfa, sizeof(sc->store));
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(rfa));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START);
return sc;
}
int
fxp_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
struct txdesc *txd;
int loop;
if (len > 1520)
printf("fxp_send: len > 1520 (%u)\n", len);
txd = &l->txd;
txd->cb_status = 0;
txd->cb_command =
htole16(FXP_CB_COMMAND_XMIT|FXP_CB_COMMAND_SF|FXP_CB_COMMAND_EL);
txd->link_addr = -1;
txd->tbd_array_addr = htole32(VTOPHYS(&txd->tx_buf_addr0));
txd->tx_buf_addr0 = htole32(VTOPHYS(buf));
txd->tx_buf_size0 = htole32(len);
txd->byte_count = htole16(0x8000);
txd->tx_threshold = 0x20;
txd->tbd_number = 1;
wbinv(buf, len);
wbinv(txd, sizeof(*txd));
fxp_scb_wait(l);
CSR_WRITE_4(l, FXP_CSR_SCB_GENERAL, VTOPHYS(txd));
CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
loop = 10000;
while (!(le16toh(txd->cb_status) & FXP_CB_STATUS_C) && --loop > 0) {
DELAY(1);
inv(txd, sizeof(struct txdesc));
}
if (loop == 0)
printf("send timeout\n");
return len;
}
int
fxp_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
struct rxdesc *rfa;
unsigned bound, ruscus, len;
fxp_scb_wait(l);
CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME);
bound = 1000 * timo;
do {
ruscus = CSR_READ_1(l, FXP_CSR_SCB_RUSCUS);
if (((ruscus >> 2) & 0x0f) != FXP_SCB_RUS_READY
&& (((ruscus >> 2) & 0x0f) == FXP_SCB_RUS_SUSPENDED))
goto gotone;
DELAY(1000); /* 1 milli second */
} while (--bound > 0);
errno = 0;
return -1;
gotone:
rfa = (struct rxdesc *)l->store;
inv(rfa, sizeof(l->store)); /* whole including received frame */
if ((le16toh(rfa->rfa_status) & FXP_RFA_STATUS_C) == 0)
return 0;
len = le16toh(rfa->actual_size) & 0x7ff;
if (len > maxlen)
len = maxlen;
memcpy(buf, &l->store[sizeof(struct rxdesc)], len);
rfa->rfa_status = 0;
rfa->rfa_control = htole16(FXP_RFA_CONTROL_S);
rfa->actual_size = 0;
wbinv(rfa, sizeof(struct rxdesc));
#if 0
fxp_scb_wait(l);
CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME);
#endif
return len;
}
static void
eeprom_shiftin(struct local *sc, int data, int len)
{
uint16_t reg;
int x;
for (x = 1 << (len - 1); x != 0; x >>= 1) {
DELAY(40);
if (data & x)
reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
else
reg = FXP_EEPROM_EECS;
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
reg | FXP_EEPROM_EESK);
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
}
DELAY(40);
}
void
autosize_eeprom(struct local *sc)
{
int x;
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
DELAY(40);
/* Shift in read opcode. */
eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3);
/*
* Shift in address, wait for the dummy zero following a correct
* address shift.
*/
for (x = 1; x <= 8; x++) {
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
FXP_EEPROM_EECS | FXP_EEPROM_EESK);
DELAY(40);
if ((CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) &
FXP_EEPROM_EEDO) == 0)
break;
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
DELAY(40);
}
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
DELAY(40);
if (x != 6 && x != 8)
printf("fxp: strange EEPROM address size (%d)\n", x);
else
sc->eeprom_addr = x;
}
static int
read_eeprom(struct local *sc, int offset)
{
uint16_t reg;
int x, val;
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
/* Shift in read opcode. */
eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3);
/* Shift in address. */
eeprom_shiftin(sc, offset, sc->eeprom_addr);
reg = FXP_EEPROM_EECS;
val = 0;
/*
* Shift out data.
*/
for (x = 16; x > 0; x--) {
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
reg | FXP_EEPROM_EESK);
DELAY(1);
if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) &
FXP_EEPROM_EEDO)
val |= (1 << (x - 1));
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
DELAY(1);
}
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
DELAY(1);
return val;
}
static void
fxp_scb_wait(struct local *sc)
{
int loop = 5000;
while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --loop > 0)
DELAY(2);
if (loop == 0)
printf("SCB timeout\n");
}
static int
fxp_mdi_read(struct local *sc, int phy, int reg)
{
int count = 10000;
int value;
CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
(FXP_MDI_READ << 26) | (reg << 16) | (phy << 21));
while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) &
0x10000000) == 0 && count--)
DELAY(10);
if (count <= 0)
printf("fxp_mdi_read: timed out\n");
return (value & 0xffff);
}
/* $NetBSD: globals.h,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/* clock feed */
#ifndef EXT_CLK_FREQ
#define EXT_CLK_FREQ 33333333 /* external clock (PCI clock) */
#endif
/* brd type */
extern int brdtype;
#define BRD_SANDPOINTX2 2
#define BRD_SANDPOINTX3 3
#define BRD_ENCOREPP1 10
#define BRD_KUROBOX 100
#define BRD_QNAPTS101 101
#define BRD_SYNOLOGY 102
#define BRD_STORCENTER 103
#define BRD_UNKNOWN -1
struct brdprop {
const char *family;
const char *verbose;
int brdtype;
uint32_t extclk;
char *consname;
int consport;
int consspeed;
void (*setup)(struct brdprop *);
void (*brdfix)(struct brdprop *);
void (*pcifix)(struct brdprop *);
void (*reset)(void);
};
extern uint32_t cpuclock, busclock;
/* board specific support code */
struct brdprop *brd_lookup(int);
unsigned mpc107memsize(void);
void read_mac_from_flash(uint8_t *);
/* PPC processor ctl */
void __syncicache(void *, size_t);
/* byte swap access */
void out16rb(unsigned, unsigned);
void out32rb(unsigned, unsigned);
unsigned in16rb(unsigned);
unsigned in32rb(unsigned);
void iohtole16(unsigned, unsigned);
void iohtole32(unsigned, unsigned);
unsigned iole32toh(unsigned);
unsigned iole16toh(unsigned);
/* far call would never return */
void run(void *, void *, void *, void *, void *);
/* micro second precision delay */
void delay(unsigned);
/* PCI stuff */
void pcisetup(void);
void pcifixup(void);
unsigned pcimaketag(int, int, int);
void pcidecomposetag(unsigned, int *, int *, int *);
int pcifinddev(unsigned, unsigned, unsigned *);
int pcilookup(unsigned, unsigned [][2], int);
unsigned pcicfgread(unsigned, int);
void pcicfgwrite(unsigned, int, unsigned);
#define PCI_ID_REG 0x00
#define PCI_COMMAND_STATUS_REG 0x04
#define PCI_VENDOR(id) ((id) & 0xffff)
#define PCI_PRODUCT(id) (((id) >> 16) & 0xffff)
#define PCI_VENDOR_INVALID 0xffff
#define PCI_DEVICE(v,p) ((v) | ((p) << 16))
#define PCI_CLASS_REG 0x08
#define PCI_CLASS_PPB 0x0604
#define PCI_CLASS_ETH 0x0200
#define PCI_CLASS_IDE 0x0101
#define PCI_CLASS_RAID 0x0104
#define PCI_CLASS_SATA 0x0106
#define PCI_CLASS_MISCSTORAGE 0x0180
#define PCI_BHLC_REG 0x0c
#define PCI_HDRTYPE_TYPE(r) (((r) >> 16) & 0x7f)
#define PCI_HDRTYPE_MULTIFN(r) ((r) & (0x80 << 16))
/*
* "Map B" layout
*
* practice direct mode configuration scheme with CONFIG_ADDR
* (0xfec0'0000) and CONFIG_DATA (0xfee0'0000).
*/
#define PCI_MEMBASE 0x80000000 /* PCI memory space */
#define PCI_MEMLIMIT 0xfbffffff /* EUMB is next to this */
#define PCI_IOBASE 0x00001000 /* reserves room for southbridge */
#define PCI_IOLIMIT 0x000fffff
#define PCI_XIOBASE 0xfe000000 /* ISA/PCI io space */
#define CONFIG_ADDR 0xfec00000
#define CONFIG_DATA 0xfee00000
/* cache ops */
void _wb(uint32_t, uint32_t);
void _wbinv(uint32_t, uint32_t);
void _inv(uint32_t, uint32_t);
/* heap */
void *allocaligned(size_t, size_t);
/* NIF support */
int net_open(struct open_file *, ...);
int net_close(struct open_file *);
int net_strategy(void *, int, daddr_t, size_t, void *, size_t *);
int netif_init(unsigned);
int netif_open(void *);
int netif_close(int);
#define NIF_DECL(xxx) \
int xxx ## _match(unsigned, void *); \
void * xxx ## _init(unsigned, void *); \
int xxx ## _send(void *, char *, unsigned); \
int xxx ## _recv(void *, char *, unsigned, unsigned)
NIF_DECL(fxp);
NIF_DECL(tlp);
NIF_DECL(rge);
NIF_DECL(skg);
/* DSK support */
int dskdv_init(unsigned, void **);
int disk_scan(void *);
int dsk_open(struct open_file *, ...);
int dsk_close(struct open_file *);
int dsk_strategy(void *, int, daddr_t, size_t, void *, size_t *);
struct fs_ops *dsk_fsops(struct open_file *);
/* status */
#define ATA_STS_BUSY 0x80
#define ATA_STS_DRDY 0x40
#define ATA_STS_ERR 0x01
/* command */
#define ATA_CMD_IDENT 0xec
#define ATA_CMD_READ 0x20
#define ATA_CMD_READ_EXT 0x24
#define ATA_CMD_SETF 0xef
/* device */
#define ATA_DEV_LBA 0xe0
#define ATA_DEV_OBS 0x90
/* control */
#define ATA_DREQ 0x08
#define ATA_SRST 0x04
#define ATA_XFER 0x03
#define XFER_PIO4 0x0c
#define XFER_PIO0 0x08
struct dvata_chan {
uint32_t cmd, ctl, alt, dma;
};
#define _DAT 0 /* RW */
#define _ERR 1 /* R */
#define _FEA 1 /* W */
#define _NSECT 2 /* RW */
#define _LBAL 3 /* RW */
#define _LBAM 4 /* RW */
#define _LBAH 5 /* RW */
#define _DEV 6 /* W */
#define _STS 7 /* R */
#define _CMD 7 /* W */
struct dkdev_ata {
unsigned tag;
uint32_t bar[6];
struct dvata_chan chan[4];
int presense[4];
char *iobuf;
};
struct disk {
char xname[8];
void *dvops;
unsigned unittag;
uint16_t ident[128];
uint64_t nsect;
uint64_t first;
void *dlabel;
int part;
void *fsops;
int (*lba_read)(struct disk *, uint64_t, uint32_t, void *);
};
int spinwait_unbusy(struct dkdev_ata *, int, int, const char **);
int perform_atareset(struct dkdev_ata *, int);
int satapresense(struct dkdev_ata *, int);
/* $NetBSD: kse.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2008 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no VTOPHYS() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_READ_4(l, r) in32rb((l)->csr+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->csr+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2, xd3;
};
#define T0_OWN (1U<<31) /* desc is ready to Tx */
#define T1_IC (1U<<31) /* post interrupt on complete */
#define T1_FS (1U<<30) /* first segment of frame */
#define T1_LS (1U<<29) /* last segment of frame */
#define T1_TER (1U<<25) /* end of ring */
#define T1_SPN 0x00300000 /* 21:20 switch port 1/2 */
#define T1_TBS_MASK 0x7ff /* segment size 10:0 */
#define R0_OWN (1U<<31) /* desc is empty */
#define R0_FS (1U<<30) /* first segment of frame */
#define R0_LS (1U<<29) /* last segment of frame */
#define R0_IPE (1U<<28) /* IP checksum error */
#define R0_TCPE (1U<<27) /* TCP checksum error */
#define R0_UDPE (1U<<26) /* UDP checksum error */
#define R0_ES (1U<<25) /* error summary */
#define R0_MF (1U<<24) /* multicast frame */
#define R0_SPN 0x00300000 /* 21:20 switch port 1/2 */
#define R0_RE (1U<<19) /* MII reported error */
#define R0_TL (1U<<18) /* frame too long, beyond 1518 */
#define R0_RF (1U<<17) /* damaged runt frame */
#define R0_CE (1U<<16) /* CRC error */
#define R0_FT (1U<<15) /* frame type */
#define R0_FL_MASK 0x7ff /* frame length 10:0 */
#define R1_RER (1U<<25) /* end of ring */
#define R1_RBS_MASK 0x7fc /* segment size 10:0 */
#define MDTXC 0x000 /* DMA transmit control */
#define MDRXC 0x004 /* DMA receive control */
#define MDTSC 0x008 /* DMA transmit start */
#define MDRSC 0x00c /* DMA receive start */
#define TDLB 0x010 /* transmit descriptor list base */
#define RDLB 0x014 /* receive descriptor list base */
#define MARL 0x200 /* MAC address low */
#define MARM 0x202 /* MAC address middle */
#define MARH 0x204 /* MAC address high */
#define CIDR 0x400 /* chip ID and enable */
#define P1CR4 0x512 /* port 1 control 4 */
#define P1SR 0x514 /* port 1 status */
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, tx, rx;
};
static void mii_dealan(struct local *, unsigned);
int
kse_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x16c6, 0x8841):
case PCI_DEVICE(0x16c6, 0x8842):
return 1;
}
return 0;
}
void *
kse_init(unsigned tag, void *data)
{
struct local *l;
struct desc *txd, *rxd;
unsigned i, val, fdx;
uint8_t *en;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x10));
en = data;
i = CSR_READ_2(l, MARL);
en[0] = i;
en[1] = i >> 8;
i = CSR_READ_2(l, MARM);
en[2] = i;
en[3] = i >> 8;
i = CSR_READ_2(l, MARH);
en[4] = i;
en[5] = i >> 8;
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
CSR_WRITE_2(l, CIDR, 1);
mii_dealan(l, 5);
val = pcicfgread(tag, PCI_ID_REG);
if (PCI_PRODUCT(val) == 0x8841) {
val = CSR_READ_2(l, P1SR);
fdx = !!(val & (1U << 9));
printf("%s", (val & (1U << 8)) ? "100Mbps" : "10Mbps");
if (fdx)
printf("-FDX");
printf("\n");
}
txd = &l->txd[0];
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(R0_OWN);
rxd[0].xd1 = htole32(FRAMESIZE);
rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
rxd[0].xd3 = htole32(VTOPHYS(&rxd[1]));
rxd[1].xd0 = htole32(R0_OWN);
rxd[1].xd1 = htole32(R1_RER | FRAMESIZE);
rxd[1].xd2 = htole32(VTOPHYS(l->rxstore[1]));
rxd[1].xd3 = htole32(VTOPHYS(&rxd[0]));
l->tx = l->rx = 0;
CSR_WRITE_4(l, TDLB, VTOPHYS(txd));
CSR_WRITE_4(l, RDLB, VTOPHYS(rxd));
CSR_WRITE_4(l, MDTXC, 07); /* stretch short, add CRC, Tx enable */
CSR_WRITE_4(l, MDRXC, 01); /* Rx enable */
CSR_WRITE_4(l, MDRSC, 01); /* start receiving */
return l;
}
int
kse_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned txstat, loop;
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd1 = htole32(T1_FS | T1_LS | (len & T1_TBS_MASK));
txd->xd0 = htole32(T0_OWN);
wbinv(txd, sizeof(struct desc));
CSR_WRITE_4(l, MDTSC, 01); /* start transmission */
loop = 100;
do {
txstat = le32toh(txd->xd0);
if ((txstat & T0_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct desc));
} while (--loop != 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
kse_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
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;
return -1;
gotone:
if (rxstat & R0_ES) {
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
CSR_WRITE_4(l, MDRSC, 01); /* restart receiving */
goto again;
}
/* good frame */
len = (rxstat & R0_FL_MASK) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
CSR_WRITE_4(l, MDRSC, 01); /* necessary? */
return len;
}
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned val, bound;
val = (1U << 13) | (1U << 7) | 0x1f /* advertise all caps */;
CSR_WRITE_2(l, P1CR4, val);
bound = getsecs() + timo;
do {
val = CSR_READ_2(l, P1SR);
if (val & (1U << 5)) /* link is found up */
break;
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: main.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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/reboot.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/loadfile.h>
#include <lib/libkern/libkern.h>
#include <machine/bootinfo.h>
#include "globals.h"
static const struct bootarg {
const char *name;
int value;
} bootargs[] = {
{ "multi", RB_AUTOBOOT },
{ "auto", RB_AUTOBOOT },
{ "ask", RB_ASKNAME },
{ "single", RB_SINGLE },
{ "ddb", RB_KDB },
{ "userconf", RB_USERCONF },
{ "norm", AB_NORMAL },
{ "quiet", AB_QUIET },
{ "verb", AB_VERBOSE },
{ "silent", AB_SILENT },
{ "debug", AB_DEBUG }
};
void *bootinfo; /* low memory reserved to pass bootinfo structures */
int bi_size; /* BOOTINFO_MAXSIZE */
char *bi_next;
void bi_init(void *);
void bi_add(void *, int, int);
struct btinfo_memory bi_mem;
struct btinfo_console bi_cons;
struct btinfo_clock bi_clk;
struct btinfo_prodfamily bi_fam;
struct btinfo_bootpath bi_path;
struct btinfo_rootdevice bi_rdev;
struct btinfo_net bi_net;
struct btinfo_modulelist *btinfo_modulelist;
size_t btinfo_modulelist_size;
struct boot_module {
char *bm_kmod;
ssize_t bm_len;
struct boot_module *bm_next;
};
struct boot_module *boot_modules;
char module_base[80];
uint32_t kmodloadp;
int modules_enabled = 0;
void module_add(char *);
void module_load(char *);
int module_open(struct boot_module *);
void main(int, char **);
extern char bootprog_rev[], bootprog_maker[], bootprog_date[];
int brdtype;
uint32_t busclock, cpuclock;
static int check_bootname(char *);
#define BNAME_DEFAULT "nfs:"
void
main(int argc, char *argv[])
{
struct brdprop *brdprop;
unsigned long marks[MARK_MAX];
unsigned lata[1][2], lnif[1][2];
unsigned tag, dsk;
int b, d, f, fd, howto, i, n;
char *bname;
void *dev;
printf("\n");
printf(">> NetBSD/sandpoint Boot, Revision %s\n", bootprog_rev);
printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
brdprop = brd_lookup(brdtype);
printf("%s, cpu %u MHz, bus %u MHz, %dMB SDRAM\n", brdprop->verbose,
cpuclock / 1000000, busclock / 1000000, bi_mem.memsize >> 20);
n = pcilookup(PCI_CLASS_IDE, lata, sizeof(lata)/sizeof(lata[0]));
if (n == 0)
n = pcilookup(PCI_CLASS_MISCSTORAGE, lata,
sizeof(lata)/sizeof(lata[0]));
if (n == 0) {
dsk = ~0;
printf("no IDE found\n");
}
else {
dsk = lata[0][1];
pcidecomposetag(dsk, &b, &d, &f);
printf("%04x.%04x IDE %02d:%02d:%02d\n",
PCI_VENDOR(lata[0][0]), PCI_PRODUCT(lata[0][0]),
b, d, f);
}
n = pcilookup(PCI_CLASS_ETH, lnif, sizeof(lnif)/sizeof(lnif[0]));
if (n == 0) {
tag = ~0;
printf("no NIC found\n");
}
else {
tag = lnif[0][1];
pcidecomposetag(tag, &b, &d, &f);
printf("%04x.%04x NIC %02d:%02d:%02d\n",
PCI_VENDOR(lnif[0][0]), PCI_PRODUCT(lnif[0][0]),
b, d, f);
}
pcisetup();
pcifixup();
if (dskdv_init(dsk, &dev) == 0 || disk_scan(dev) == 0)
printf("no IDE/SATA device driver was found\n");
if (netif_init(tag) == 0)
printf("no NIC device driver was found\n");
howto = RB_AUTOBOOT; /* default is autoboot = 0 */
/* get boot options and determine bootname */
for (n = 1; n < argc; n++) {
for (i = 0; i < sizeof(bootargs) / sizeof(bootargs[0]); i++) {
if (strncasecmp(argv[n], bootargs[i].name,
strlen(bootargs[i].name)) == 0) {
howto |= bootargs[i].value;
break;
}
}
if (i >= sizeof(bootargs) / sizeof(bootargs[0]))
break; /* break on first unknown string */
}
if (n >= argc)
bname = BNAME_DEFAULT;
else {
bname = argv[n];
if (check_bootname(bname) == 0) {
printf("%s not a valid bootname\n", bname);
goto loadfail;
}
}
if ((fd = open(bname, 0)) < 0) {
if (errno == ENOENT)
printf("\"%s\" not found\n", bi_path.bootpath);
goto loadfail;
}
printf("loading \"%s\" ", bi_path.bootpath);
marks[MARK_START] = 0;
if (fdloadfile(fd, marks, LOAD_KERNEL) < 0)
goto loadfail;
close(fd);
printf("entry=%p, ssym=%p, esym=%p\n",
(void *)marks[MARK_ENTRY],
(void *)marks[MARK_SYM],
(void *)marks[MARK_END]);
bootinfo = (void *)0x4000;
bi_init(bootinfo);
bi_add(&bi_cons, BTINFO_CONSOLE, sizeof(bi_cons));
bi_add(&bi_mem, BTINFO_MEMORY, sizeof(bi_mem));
bi_add(&bi_clk, BTINFO_CLOCK, sizeof(bi_clk));
bi_add(&bi_path, BTINFO_BOOTPATH, sizeof(bi_path));
bi_add(&bi_rdev, BTINFO_ROOTDEVICE, sizeof(bi_rdev));
bi_add(&bi_fam, BTINFO_PRODFAMILY, sizeof(bi_fam));
if (brdtype == BRD_SYNOLOGY) {
/* need to set MAC address for Marvell-SKnet */
bi_add(&bi_net, BTINFO_NET, sizeof(bi_net));
}
if (modules_enabled) {
module_add(fsmod);
if (fsmod2 != NULL && strcmp(fsmod, fsmod2) != 0)
module_add(fsmod2);
kmodloadp = marks[MARK_END];
btinfo_modulelist = NULL;
module_load(bname);
if (btinfo_modulelist != NULL && btinfo_modulelist->num > 0)
bi_add(btinfo_modulelist, BTINFO_MODULELIST,
btinfo_modulelist_size);
}
__syncicache((void *)marks[MARK_ENTRY],
(u_int)marks[MARK_SYM] - (u_int)marks[MARK_ENTRY]);
run((void *)marks[MARK_SYM], (void *)marks[MARK_END],
(void *)howto, bootinfo, (void *)marks[MARK_ENTRY]);
/* should never come here */
printf("exec returned. Restarting...\n");
_rtt();
loadfail:
printf("load failed. Restarting...\n");
_rtt();
}
void
bi_init(void *addr)
{
struct btinfo_magic bi_magic;
memset(addr, 0, BOOTINFO_MAXSIZE);
bi_next = (char *)addr;
bi_size = 0;
bi_magic.magic = BOOTINFO_MAGIC;
bi_add(&bi_magic, BTINFO_MAGIC, sizeof(bi_magic));
}
void
bi_add(void *new, int type, int size)
{
struct btinfo_common *bi;
if (bi_size + size > BOOTINFO_MAXSIZE)
return; /* XXX error? */
bi = new;
bi->next = size;
bi->type = type;
memcpy(bi_next, new, size);
bi_next += size;
}
void
module_add(char *name)
{
struct boot_module *bm, *bmp;
while (*name == ' ' || *name == '\t')
++name;
bm = alloc(sizeof(struct boot_module) + strlen(name) + 1);
if (bm == NULL) {
printf("couldn't allocate module %s\n", name);
return;
}
bm->bm_kmod = (char *)(bm + 1);
bm->bm_len = -1;
bm->bm_next = NULL;
strcpy(bm->bm_kmod, name);
if ((bmp = boot_modules) == NULL)
boot_modules = bm;
else {
while (bmp->bm_next != NULL)
bmp = bmp->bm_next;
bmp->bm_next = bm;
}
}
#define PAGE_SIZE 4096
#define alignpg(x) (((x)+PAGE_SIZE-1) & ~(PAGE_SIZE-1))
void
module_load(char *kernel_path)
{
struct boot_module *bm;
struct bi_modulelist_entry *bi;
struct stat st;
char *p;
int size, fd;
strcpy(module_base, kernel_path);
if ((p = strchr(module_base, ':')) == NULL)
return; /* eeh?! */
p += 1;
size = sizeof(module_base) - (p - module_base);
if (netbsd_version / 1000000 % 100 == 99) {
/* -current */
snprintf(p, size,
"/stand/sandpoint/%d.%d.%d/modules",
netbsd_version / 100000000,
netbsd_version / 1000000 % 100,
netbsd_version / 100 % 100);
}
else if (netbsd_version != 0) {
/* release */
snprintf(p, size,
"/stand/sandpoint/%d.%d/modules",
netbsd_version / 100000000,
netbsd_version / 1000000 % 100);
}
/*
* 1st pass; determine module existence
*/
size = 0;
for (bm = boot_modules; bm != NULL; bm = bm->bm_next) {
fd = module_open(bm);
if (fd == -1)
continue;
if (fstat(fd, &st) == -1 || st.st_size == -1) {
printf("WARNING: couldn't stat %s\n", bm->bm_kmod);
close(fd);
continue;
}
bm->bm_len = (int)st.st_size;
close(fd);
size += sizeof(struct bi_modulelist_entry);
}
if (size == 0)
return;
size += sizeof(struct btinfo_modulelist);
btinfo_modulelist = alloc(size);
if (btinfo_modulelist == NULL) {
printf("WARNING: couldn't allocate module list\n");
return;
}
btinfo_modulelist_size = size;
btinfo_modulelist->num = 0;
/*
* 2nd pass; load modules into memory
*/
kmodloadp = alignpg(kmodloadp);
bi = (struct bi_modulelist_entry *)(btinfo_modulelist + 1);
for (bm = boot_modules; bm != NULL; bm = bm->bm_next) {
if (bm->bm_len == -1)
continue; /* already found unavailable */
fd = module_open(bm);
printf("module \"%s\" ", bm->bm_kmod);
size = read(fd, (char *)kmodloadp, SSIZE_MAX);
if (size < bm->bm_len)
printf("WARNING: couldn't load");
else {
snprintf(bi->kmod, sizeof(bi->kmod), bm->bm_kmod);
bi->type = BI_MODULE_ELF;
bi->len = size;
bi->base = kmodloadp;
btinfo_modulelist->num += 1;
printf("loaded at 0x%08x size 0x%x", kmodloadp, size);
kmodloadp += alignpg(size);
bi += 1;
}
printf("\n");
close(fd);
}
btinfo_modulelist->endpa = kmodloadp;
}
int
module_open(struct boot_module *bm)
{
char path[80];
int fd;
snprintf(path, sizeof(path),
"%s/%s/%s.kmod", module_base, bm->bm_kmod, bm->bm_kmod);
fd = open(path, 0);
return fd;
}
#if 0
static const char *cmdln[] = {
"console=ttyS0,115200 root=/dev/sda1 rw initrd=0x200000,2M",
"console=ttyS0,115200 root=/dev/nfs ip=dhcp"
};
void
mkatagparams(unsigned addr, char *kcmd)
{
struct tag {
unsigned siz;
unsigned tag;
unsigned val[1];
};
struct tag *p;
#define ATAG_CORE 0x54410001
#define ATAG_MEM 0x54410002
#define ATAG_INITRD 0x54410005
#define ATAG_CMDLINE 0x54410009
#define ATAG_NONE 0x00000000
#define tagnext(p) (struct tag *)((unsigned *)(p) + (p)->siz)
#define tagsize(n) (2 + (n))
p = (struct tag *)addr;
p->tag = ATAG_CORE;
p->siz = tagsize(3);
p->val[0] = 0; /* flags */
p->val[1] = 0; /* pagesize */
p->val[2] = 0; /* rootdev */
p = tagnext(p);
p->tag = ATAG_MEM;
p->siz = tagsize(2);
p->val[0] = 64 * 1024 * 1024;
p->val[1] = 0; /* start */
p = tagnext(p);
if (kcmd != NULL) {
p = tagnext(p);
p->tag = ATAG_CMDLINE;
p->siz = tagsize((strlen(kcmd) + 1 + 3) >> 2);
strcpy((void *)p->val, kcmd);
}
p = tagnext(p);
p->tag = ATAG_NONE;
p->siz = 0;
}
#endif
void *
allocaligned(size_t size, size_t align)
{
uint32_t p;
if (align-- < 2)
return alloc(size);
p = (uint32_t)alloc(size + align);
return (void *)((p + align) & ~align);
}
static int
check_bootname(char *s)
{
/*
* nfs:
* nfs:<bootfile>
* tftp:
* tftp:<bootfile>
* wd[N[P]]:<bootfile>
*
* net is a synonym of nfs.
*/
if (strncmp(s, "nfs:", 4) == 0 || strncmp(s, "net:", 4) == 0)
return 1;
if (strncmp(s, "tftp:", 5) == 0)
return 1;
if (s[0] == 'w' && s[1] == 'd') {
s += 2;
if (*s != ':' && *s >= '0' && *s <= '3') {
++s;
if (*s != ':' && *s >= 'a' && *s <= 'p')
++s;
}
return *s == ':';
}
return 0;
}
/* $NetBSD: nif.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <machine/bootinfo.h>
#include "globals.h"
struct nifdv {
char *name;
int (*match)(unsigned, void *);
void *(*init)(unsigned, void *);
int (*send)(void *, char *, unsigned);
int (*recv)(void *, char *, unsigned, unsigned);
int (*halt)(void *, int);
void *priv;
};
static struct iodesc netdesc;
static struct nifdv lnifdv[] = {
{ "fxp", fxp_match, fxp_init, fxp_send, fxp_recv },
{ "tlp", tlp_match, tlp_init, tlp_send, tlp_recv },
{ "re", rge_match, rge_init, rge_send, rge_recv },
{ "sk", skg_match, skg_init, skg_send, skg_recv },
};
static int nnifdv = sizeof(lnifdv)/sizeof(lnifdv[0]);
int
netif_init(unsigned tag)
{
struct iodesc *s;
struct nifdv *dv;
int n;
uint8_t enaddr[6];
extern struct btinfo_net bi_net;
extern struct btinfo_rootdevice bi_rdev;
for (n = 0; n < nnifdv; n++) {
dv = &lnifdv[n];
if ((*dv->match)(tag, NULL) > 0)
goto found;
}
return 0;
found:
dv->priv = (*dv->init)(tag, enaddr);
s = &netdesc;
s->io_netif = dv;
memcpy(s->myea, enaddr, sizeof(s->myea));
/* build btinfo to identify NIF device */
snprintf(bi_net.devname, sizeof(bi_net.devname), dv->name);
memcpy(bi_net.mac_address, enaddr, sizeof(bi_net.mac_address));
bi_net.cookie = tag;
snprintf(bi_rdev.devname, sizeof(bi_rdev.devname), dv->name);
bi_rdev.cookie = tag;
return 1;
}
int
netif_open(void *cookie)
{
/* single action */
return 0;
}
int
netif_close(int sock)
{
/* nothing to do for the HW */
return 0;
}
/*
* Send a packet. The ether header is already there.
* Return the length sent (or -1 on error).
*/
ssize_t
netif_put(struct iodesc *desc, void *pkt, size_t len)
{
struct nifdv *dv = desc->io_netif;
return dv ? (*dv->send)(dv->priv, pkt, len) : -1;
}
/*
* Receive a packet, including the ether header.
* Return the total length received (or -1 on error).
*/
ssize_t
netif_get(struct iodesc *desc, void *pkt, size_t maxlen, saseconds_t timo)
{
struct nifdv *dv = desc->io_netif;
int len;
len = dv ? (*dv->recv)(dv->priv, pkt, maxlen, timo) : -1;
if (len == -1)
printf("timeout\n");
return len;
}
struct iodesc *
socktodesc(int num)
{
return (num == 0) ? &netdesc : NULL;
}
/* $NetBSD: nvt.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_WRITE_1(l, r, v) *(volatile uint8_t *)((l)->csr+(r)) = (v)
#define CSR_READ_1(l, r) *(volatile uint8_t *)((l)->csr+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->csr+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
#define CSR_READ_4(l, r) in32rb((l)->csr+(r))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2, xd3;
};
#define T0_OWN (1U << 31) /* 1: loaded for HW to send */
#define T0_TERR (1U << 15) /* Tx error; ABT|CBH */
#define T0_UDF (1U << 11) /* FIFO underflow */
#define T0_CRS (1U << 10) /* found carrier sense lost */
#define T0_OWC (1U << 9) /* found out of window collision */
#define T0_ABT (1U << 8) /* excess collision Tx abort */
#define T0_CBH (1U << 7) /* heartbeat check failure */
#define T0_COLS (1U << 4) /* collision detected */
#define T0_NCRMASK 0x3 /* number of collision retries */
#define T1_IC (1U << 23) /* post Tx done interrupt */
#define T1_STP (1U << 22) /* first frame segment */
#define T1_EDP (1U << 21) /* last frame segment */
#define T1_CRC (1U << 16) /* _disable_ CRC generation */
#define T1_CHN (1U << 15) /* "more bit," not the last seg. */
#define T_FLMASK 0x00007fff /* Tx frame/segment length */
#define R0_OWN (1U << 31) /* 1: empty for HW to load anew */
#define R0_FLMASK 0x7fff0000 /* frame length */
#define R0_RXOK (1U << 15)
#define R0_MAR (1U << 13) /* multicast frame */
#define R0_BAR (1U << 12) /* broadcast frame */
#define R0_PHY (1U << 11) /* unicast frame */
#define R0_CHN (1U << 10) /* "more bit," not the last seg. */
#define R0_STP (1U << 9) /* first frame segment */
#define R0_EDP (1U << 8) /* last frame segment */
#define R0_BUFF (1U << 7) /* segment chain was broken */
#define R0_RUNT (1U << 5) /* runt frame received */
#define R0_LONG (1U << 4) /* frame too long */
#define R0_FOV (1U << 3) /* Rx FIFO overflow */
#define R0_FAE (1U << 2) /* frame alignment error */
#define R0_CRCE (1U << 1) /* CRC error */
#define R0_RERR (1U << 0) /* Rx error summary */
#define R1_FLMASK 0x00007ffc /* Rx segment buffer length */
#define VR_PAR0 0x00 /* SA [0] */
#define VR_PAR1 0x01 /* SA [1] */
#define VR_PAR2 0x02 /* SA [2] */
#define VR_PAR3 0x03 /* SA [3] */
#define VR_PAR4 0x04 /* SA [4] */
#define VR_PAR5 0x05 /* SA [5] */
#define VR_RCR 0x06 /* Rx control */
#define RCR_PROM (1U << 4) /* accept any frame */
#define RCR_AB (1U << 3) /* accept broadcast frame */
#define RCR_AM (1U << 2) /* use multicast filter */
#define VR_TCR 0x07 /* Tx control */
#define VR_CTL0 0x08 /* control #0 */
#define CTL0_RDMD (1U << 6) /* instruct Rx descriptor poll */
#define CTL0_TDMD (1U << 5) /* instruct Tx descriptor poll */
#define CTL0_TXON (1U << 4) /* enable Tx DMA */
#define CTL0_RXON (1U << 3) /* enable Rx DMA */
#define CTL0_STOP (1U << 2) /* activate stop processing */
#define CTL0_START (1U << 1) /* start and activate */
#define VR_CTL1 0x09 /* control #1 */
#define CTL1_RESET (1U << 7) /* SW reset, self-clearing */
#define CTL1_DPOLL (1U << 3) /* _disable_ Tx auto polling */
#define CTL1_FDX (1U << 2) /* set full duplex */
#define VR_ISR 0x0c /* interrupt status */
#define VR_IEN 0x0e /* interrupt enable */
#define VR_RDBA 0x18 /* Rx descriptor list base */
#define VR_TDBA 0x1c /* Tx descriptor list base */
#define VR_MIICFG 0x6c /* 4:0 PHY number */
#define VR_MIISR 0x6d /* MII status */
#define VR_MIICR 0x70 /* MII control */
#define MIICR_MAUTO (1U << 7) /* activate autopoll mode */
#define MIICR_RCMD (1U << 6) /* MII read operation */
#define MIICR_WCMD (1U << 5) /* MII write operation */
#define VR_MIIADR 0x71 /* MII indirect */
#define MIIADR_MIDLE (1U << 7) /* not in auto polling */
#define VR_MIIDATA 0x72 /* MII read/write */
#define VR_RXC 0x7e /* Rx feature control */
#define VR_TXC 0x7f /* Tx feature control */
#define VR_MCR0 0x80 /* misc control #0 */
#define MCR0_RFDXFLC (1U << 3) /* FCR1? */
#define MCR0_HDXFLC (1U << 2) /* FCR2? */
#define VR_MCR1 0x81 /* misc control #1 */
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, tx, rx;
unsigned phy, bmsr, anlpar;
unsigned ctl0;
};
static void mii_autopoll(struct local *);
static void mii_stoppoll(struct local *);
static int mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_dealan(struct local *, unsigned);
int
nvt_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x1106, 0x3053):
case PCI_DEVICE(0x1106, 0x3065):
return 1;
}
return 0;
}
void *
nvt_init(unsigned tag, void *data)
{
unsigned val, fdx;
struct local *l;
struct desc *txd, *rxd;
uint8_t *en;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = ~01 & DEVTOV(pcicfgread(tag, 0x10)); /* use IO space */
val = CTL1_RESET;
CSR_WRITE_1(l, VR_CTL1, val);
do {
val = CSR_READ_1(l, VR_CTL1);
} while (val & CTL1_RESET);
/* PHY number is loaded from EEPROM */
l->phy = CSR_READ_1(l, VR_MIICFG) & 0x1f;
en = data;
en[0] = CSR_READ_1(l, VR_PAR0);
en[1] = CSR_READ_1(l, VR_PAR1);
en[2] = CSR_READ_1(l, VR_PAR2);
en[3] = CSR_READ_1(l, VR_PAR3);
en[4] = CSR_READ_1(l, VR_PAR4);
en[5] = CSR_READ_1(l, VR_PAR5);
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
mii_dealan(l, 5);
/* speed and duplexity can be seen in MII 20 */
val = mii_read(l, l->phy, 20);
fdx = !!(val & (1U << 0));
printf("%s", (val & (1U << 1)) ? "100Mbps" : "10Mbps");
if (fdx)
printf("-FDX");
printf("\n");
txd = &l->txd[0];
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(R0_OWN);
rxd[0].xd1 = htole32(FRAMESIZE << 16);
rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
rxd[0].xd3 = htole32(VTOPHYS(&rxd[1]));
rxd[1].xd0 = htole32(R0_OWN);
rxd[1].xd1 = htole32(VTOPHYS(l->rxstore[1]));
rxd[1].xd2 = htole32(FRAMESIZE << 16);
rxd[1].xd3 = htole32(VTOPHYS(&rxd[0]));
wbinv(l, sizeof(struct local));
l->tx = l->rx = 0;
/* enable transmitter and receiver */
l->ctl0 = CTL0_TXON | CTL0_RXON | CTL0_START;
CSR_WRITE_4(l, VR_RDBA, VTOPHYS(rxd));
CSR_WRITE_4(l, VR_TDBA, VTOPHYS(txd));
CSR_WRITE_1(l, VR_RCR, 0);
CSR_WRITE_1(l, VR_TCR, 0);
CSR_WRITE_2(l, VR_ISR, ~0);
CSR_WRITE_2(l, VR_IEN, 0);
if (fdx)
CSR_WRITE_1(l, VR_CTL1, CTL1_FDX);
CSR_WRITE_1(l, VR_CTL0, CTL0_START);
CSR_WRITE_1(l, VR_CTL0, l->ctl0);
return l;
}
int
nvt_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned loop;
len = (len & T_FLMASK);
if (len < 60)
len = 60; /* needs to stretch to ETHER_MIN_LEN - 4 */
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd3 = htole32(txd);
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd1 = htole32(T1_STP | T1_EDP | len);
txd->xd0 = htole32(T0_OWN);
wbinv(txd, sizeof(struct desc));
CSR_WRITE_1(l, VR_CTL0, l->ctl0 | CTL0_TDMD);
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:
l->tx ^= 1;
return len;
}
int
nvt_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
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;
return -1;
gotone:
if ((rxstat & R0_RXOK) == 0) {
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
goto again;
}
len = ((rxstat & R0_FLMASK) >> 16) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
return len;
}
static void
mii_autopoll(struct local *l)
{
int v;
CSR_WRITE_1(l, VR_MIICR, 0);
do {
DELAY(1);
v = CSR_READ_1(l, VR_MIISR);
} while ((v & MIIADR_MIDLE) == 0);
CSR_WRITE_1(l, VR_MIICR, MIICR_MAUTO);
do {
DELAY(1);
v = CSR_READ_1(l, VR_MIISR);
} while ((v & MIIADR_MIDLE) != 0);
}
static void
mii_stoppoll(struct local *l)
{
int v;
CSR_WRITE_1(l, VR_MIICR, 0);
do {
DELAY(1);
v = CSR_READ_1(l, VR_MIISR);
} while ((v & MIIADR_MIDLE) == 0);
}
static int
mii_read(struct local *l, int phy, int reg)
{
int v;
mii_stoppoll(l);
CSR_WRITE_1(l, VR_MIICFG, phy);
CSR_WRITE_1(l, VR_MIIADR, reg);
CSR_WRITE_1(l, VR_MIICR, MIICR_RCMD);
do {
v = CSR_READ_1(l, VR_MIICR);
} while (v & MIICR_RCMD);
v = CSR_READ_2(l, VR_MIIDATA);
mii_autopoll(l);
return v;
}
static void
mii_write(struct local *l, int phy, int reg, int data)
{
int v;
mii_stoppoll(l);
CSR_WRITE_2(l, VR_MIIDATA, data);
CSR_WRITE_1(l, VR_MIICFG, phy);
CSR_WRITE_1(l, VR_MIIADR, reg);
CSR_WRITE_1(l, VR_MIICR, MIICR_WCMD);
do {
v = CSR_READ_1(l, VR_MIICR);
} while (v & MIICR_WCMD);
mii_autopoll(l);
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: pci.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <lib/libsa/stand.h>
#define MAXNDEVS 32
#include "globals.h"
static unsigned cfgread(int, int, int, int);
static void cfgwrite(int, int, int, int, unsigned);
static void _buswalk(int,
int (*)(int, int, int, unsigned long), unsigned long);
static int _pcilookup(int,
int (*)(int, int, int, unsigned long), unsigned long,
unsigned [][2], int, int);
static int deviceinit(int, int, int, unsigned long);
static void memassign(int, int, int);
static int devmatch(int, int, int, unsigned long);
static int clsmatch(int, int, int, unsigned long);
unsigned memstart, memlimit;
unsigned iostart, iolimit;
unsigned maxbus;
void
pcisetup(void)
{
memstart = PCI_MEMBASE;
memlimit = PCI_MEMLIMIT;
iostart = PCI_IOBASE;
iolimit = PCI_IOLIMIT;
maxbus = 0;
(void)_buswalk(0, deviceinit, 0UL);
}
int
pcifinddev(unsigned vend, unsigned prod, unsigned *tag)
{
unsigned pciid, target[1][2];
pciid = PCI_DEVICE(vend, prod);
if (_pcilookup(0, devmatch, pciid, target, 0, 1)) {
*tag = target[0][1];
return 0;
}
*tag = ~0;
return -1;
}
int
pcilookup(type, list, max)
unsigned type;
unsigned list[][2];
int max;
{
return _pcilookup(0, clsmatch, type, list, 0, max);
}
unsigned
pcimaketag(int b, int d, int f)
{
return (1U << 31) | (b << 16) | (d << 11) | (f << 8);
}
void
pcidecomposetag(unsigned tag, int *b, int *d, int *f)
{
if (b != NULL)
*b = (tag >> 16) & 0xff;
if (d != NULL)
*d = (tag >> 11) & 0x1f;
if (f != NULL)
*f = (tag >> 8) & 0x7;
return;
}
unsigned
pcicfgread(unsigned tag, int off)
{
unsigned cfg;
cfg = tag | (off &~ 03);
iohtole32(CONFIG_ADDR, cfg);
return iole32toh(CONFIG_DATA);
}
void
pcicfgwrite(unsigned tag, int off, unsigned val)
{
unsigned cfg;
cfg = tag | (off &~ 03);
iohtole32(CONFIG_ADDR, cfg);
iohtole32(CONFIG_DATA, val);
}
static unsigned
cfgread(int b, int d, int f, int off)
{
unsigned cfg;
off &= ~03;
cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
iohtole32(CONFIG_ADDR, cfg);
return iole32toh(CONFIG_DATA);
}
static void
cfgwrite(int b, int d, int f, int off, unsigned val)
{
unsigned cfg;
off &= ~03;
cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
iohtole32(CONFIG_ADDR, cfg);
iohtole32(CONFIG_DATA, val);
}
static void
_buswalk(int bus, int (*proc)(int, int, int, unsigned long), unsigned long data)
{
int device, function, nfunctions;
unsigned pciid, bhlcr;
for (device = 0; device < MAXNDEVS; device++) {
pciid = cfgread(bus, device, 0, PCI_ID_REG);
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
continue;
if (PCI_VENDOR(pciid) == 0)
continue;
bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
nfunctions = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
for (function = 0; function < nfunctions; function++) {
pciid = cfgread(bus, device, function, PCI_ID_REG);
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
continue;
if (PCI_VENDOR(pciid) == 0)
continue;
if ((*proc)(bus, device, function, data) != 0)
goto out; /* early exit */
}
}
out:;
}
static int
deviceinit(int bus, int dev, int func, unsigned long data)
{
unsigned val;
/* 0x00 */
printf("%02d:%02d:%02d:", bus, dev, func);
val = cfgread(bus, dev, func, 0x00);
printf(" chip %04x.%04x", val & 0xffff, val>>16);
val = cfgread(bus, dev, func, 0x2c);
printf(" card %04x.%04x", val & 0xffff, val>>16);
val = cfgread(bus, dev, func, 0x08);
printf(" rev %02x class %02x.%02x.%02x",
val & 0xff, (val>>24), (val>>16) & 0xff, (val>>8) & 0xff);
val = cfgread(bus, dev, func, 0x0c);
printf(" hdr %02x\n", (val>>16) & 0xff);
/* 0x04 */
val = cfgread(bus, dev, func, 0x04);
val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */
cfgwrite(bus, dev, func, 0x04, val);
/* 0x0c */
val = 0x80 << 8 | 0x08 /* 32B cache line */;
cfgwrite(bus, dev, func, 0x0c, val);
/* skip legacy mode IDE controller BAR assignment */
val = cfgread(bus, dev, func, PCI_CLASS_REG);
if ((val >> 16) == PCI_CLASS_IDE && ((val >> 8) & 0x05) == 0)
return 0;
memassign(bus, dev, func);
/* descending toward PCI-PCI bridge */
if ((cfgread(bus, dev, func, 0x08) >> 16) == PCI_CLASS_PPB) {
unsigned new;
/* 0x18 */
new = (maxbus += 1);
val = (0xff << 16) | (new << 8) | bus;
cfgwrite(bus, dev, func, 0x18, val);
/* 0x1c and 0x30 */
val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */
iostart = val;
val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8;
cfgwrite(bus, dev, func, 0x1c, val);
val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16;
cfgwrite(bus, dev, func, 0x30, val);
/* 0x20 */
val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */
memstart = val;
val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16;
cfgwrite(bus, dev, func, 0x20, val);
/* redo 0x04 */
val = cfgread(bus, dev, func, 0x04);
val |= 0xffff0107;
cfgwrite(bus, dev, func, 0x04, val);
_buswalk(new, deviceinit, data);
/* adjust 0x18 */
val = cfgread(bus, dev, func, 0x18);
val = (maxbus << 16) | (val & 0xffff);
cfgwrite(bus, dev, func, 0x18, val);
}
return 0;
}
static void
memassign(int bus, int dev, int func)
{
unsigned val, maxbar, mapr, req, mapbase, size;
val = cfgread(bus, dev, func, 0x0c);
switch (PCI_HDRTYPE_TYPE(val)) {
case 0:
maxbar = 0x10 + 6 * 4; break;
case 1:
maxbar = 0x10 + 2 * 4; break;
default:
maxbar = 0x10 + 1 * 4; break;
}
for (mapr = 0x10; mapr < maxbar; mapr += 4) {
cfgwrite(bus, dev, func, mapr, 0xffffffff);
val = cfgread(bus, dev, func, mapr);
if (val & 01) {
/* PCI IO space */
req = ~(val & 0xfffffffc) + 1;
if (req & (req - 1)) /* power of 2 */
continue;
if (req == 0) /* ever exists */
continue;
size = (req > 0x10) ? req : 0x10;
mapbase = (iostart + size - 1) & ~(size - 1);
if (mapbase + size > iolimit)
continue;
iostart = mapbase + size;
/* establish IO space */
cfgwrite(bus, dev, func, mapr, mapbase | 01);
}
else {
/* PCI memory space */
req = ~(val & 0xfffffff0) + 1;
if (req & (req - 1)) /* power of 2 */
continue;
if (req == 0) /* ever exists */
continue;
val &= 0x6;
if (val == 2 || val == 6)
continue;
size = (req > 0x1000) ? req : 0x1000;
mapbase = (memstart + size - 1) & ~(size - 1);
if (mapbase + size > memlimit)
continue;
memstart = mapbase + size;
/* establish memory space */
cfgwrite(bus, dev, func, mapr, mapbase);
if (val == 4) {
mapr += 4;
cfgwrite(bus, dev, func, mapr, 0);
}
}
printf("%s base %x size %x\n", (val & 01) ? "i/o" : "mem", mapbase, size);
}
}
static int
devmatch(int bus, int dev, int func, unsigned long data)
{
unsigned pciid;
pciid = cfgread(bus, dev, func, PCI_ID_REG);
return (pciid == (unsigned)data);
}
static int
clsmatch(int bus, int dev, int func, unsigned long data)
{
unsigned class;
class = cfgread(bus, dev, func, PCI_CLASS_REG);
return ((class >> 16) == (unsigned)data);
}
static int
_pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, unsigned list[][2], int index, int limit)
{
int device, function, nfuncs;
unsigned pciid, bhlcr, class;
for (device = 0; device < MAXNDEVS; device++) {
pciid = cfgread(bus, device, 0, PCI_ID_REG);
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
continue;
if (PCI_VENDOR(pciid) == 0)
continue;
class = cfgread(bus, device, 0, PCI_CLASS_REG);
if ((class >> 16) == PCI_CLASS_PPB) {
/* exploring bus beyond PCI-PCI bridge */
index = _pcilookup(bus + 1,
match, data, list, index, limit);
if (index >= limit)
goto out;
continue;
}
bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
for (function = 0; function < nfuncs; function++) {
pciid = cfgread(bus, device, function, PCI_ID_REG);
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
continue;
if (PCI_VENDOR(pciid) == 0)
continue;
if ((*match)(bus, device, function, data)) {
list[index][0] = pciid;
list[index][1] =
pcimaketag(bus, device, function);
index += 1;
if (index >= limit)
goto out;
}
}
}
out:
return index;
}
/* $NetBSD: pciide.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2008 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.
*
* 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 <lib/libsa/stand.h>
#include "globals.h"
static uint32_t pciiobase = PCI_XIOBASE;
struct myops {
int (*chipfix)(struct dkdev_ata *);
int (*presense)(struct dkdev_ata *, int);
};
static int cmdidefix(struct dkdev_ata *);
static struct myops cmdideops = { cmdidefix, NULL };
static struct myops *myops = &cmdideops;
int pciide_match(unsigned, void *);
void *pciide_init(unsigned, void *);
int
pciide_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x1095, 0x0680): /* SiI 0680 IDE */
case PCI_DEVICE(0x1283, 0x8211): /* ITE 8211 IDE */
case PCI_DEVICE(0x1106, 0x1571): /* VIA 82C586 IDE */
case PCI_DEVICE(0x10ad, 0x0105): /* Symphony Labs 82C105 IDE */
case PCI_DEVICE(0x10b8, 0x5229): /* ALi IDE */
return 1;
}
return 0;
}
void *
pciide_init(unsigned tag, void *data)
{
int native, n;
unsigned val;
struct dkdev_ata *l;
l = alloc(sizeof(struct dkdev_ata));
memset(l, 0, sizeof(struct dkdev_ata));
l->iobuf = allocaligned(512, 16);
l->tag = tag;
val = pcicfgread(tag, PCI_CLASS_REG);
native = val & 03;
if (native) {
/* native, use BAR 01234 */
l->bar[0] = pciiobase + (pcicfgread(tag, 0x10) &~ 01);
l->bar[1] = pciiobase + (pcicfgread(tag, 0x14) &~ 01);
l->bar[2] = pciiobase + (pcicfgread(tag, 0x18) &~ 01);
l->bar[3] = pciiobase + (pcicfgread(tag, 0x1c) &~ 01);
l->bar[4] = pciiobase + (pcicfgread(tag, 0x20) &~ 01);
l->chan[0].cmd = l->bar[0];
l->chan[0].ctl = l->chan[0].alt = l->bar[1] | 02;
l->chan[0].dma = l->bar[4] + 0x0;
l->chan[1].cmd = l->bar[2];
l->chan[1].ctl = l->chan[1].alt = l->bar[3] | 02;
l->chan[1].dma = l->bar[4] + 0x8;
}
else {
/* legacy */
l->bar[0]= pciiobase + 0x1f0;
l->bar[1]= pciiobase + 0x3f4;
l->bar[2]= pciiobase + 0x170;
l->bar[3]= pciiobase + 0x374;
l->chan[0].cmd = l->bar[0];
l->chan[0].ctl = l->chan[0].alt = l->bar[1] | 02;
l->chan[0].dma = 0;
l->chan[1].cmd = l->bar[2];
l->chan[1].ctl = l->chan[1].alt = l->bar[3] | 02;
l->chan[1].dma = 0;
}
for (n = 0; n < 2; n++) {
if (myops->presense && (*myops->presense)(l, n) == 0)
l->presense[n] = 0; /* found not exist */
else {
/* check to see whether soft reset works */
l->presense[n] = perform_atareset(l, n);
}
if (l->presense[n])
printf("channel %d present\n", n);
}
/* make sure to have PIO0 */
if (myops->chipfix)
(*myops->chipfix)(l);
return l;
}
static int
cmdidefix(struct dkdev_ata *l)
{
int v;
v = pcicfgread(l->tag, 0x80);
pcicfgwrite(l->tag, 0x80, (v & ~0xff) | 0x01);
v = pcicfgread(l->tag, 0x84);
pcicfgwrite(l->tag, 0x84, (v & ~0xff) | 0x01);
v = pcicfgread(l->tag, 0xa4);
pcicfgwrite(l->tag, 0xa4, (v & ~0xffff) | 0x328a);
v = pcicfgread(l->tag, 0xb4);
pcicfgwrite(l->tag, 0xb4, (v & ~0xffff) | 0x328a);
return 1;
}
/* $NetBSD: pcn.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_READ_4(l, r) in32rb((l)->csr+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->csr+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2;
uint32_t hole;
};
#define T1_OWN (1U << 31) /* 1: empty for HW to load anew */
#define T1_STP (1U << 25) /* first frame segment */
#define T1_ENP (1U << 24) /* last frame segment */
#define T1_ONES 0xf000 /* filler */
#define T1_FLMASK 0x0fff /* Tx segment length */
#define R1_OWN (1U << 31) /* 1: loaded for HW to send */
#define R1_ERR (1U << 30) /* Rx error summary */
#define R1_ONES 0xf000 /* filler */
#define R1_FLMASK 0x0fff /* Rx frame length */
#define PCN_RDP 0x10
#define PCN_RAP 0x12
#define PCN_16RESET 0x14
#define PCN_32RESET 0x18
#define PCN_BDP 0x1c
#define PCN_CSR0 0x00
#define C0_IDON (1U << 8) /* initblk done indication */
#define C0_TXON (1U << 5)
#define C0_RXON (1U << 4)
#define C0_TDMD (1U << 3) /* immediate Tx descriptor poll */
#define C0_STOP (1U << 2) /* reset with abrupt abort */
#define C0_STRT (1U << 1) /* activate whole Tx/Rx DMA */
#define C0_INIT (1U << 0) /* instruct to process initblk */
#define PCN_CSR1 0x01
#define PCN_CSR2 0x02
#define PCN_CSR3 0x03
#define C3_MISSM (1U << 12)
#define C3_IDONM (1U << 8)
#define C3_DXSUFLO (1U << 6)
#define PCN_CSR4 0x04
#define C4_DMAPLUS (1U << 14)
#define C4_TXDPOLL (1U << 12) /* _disable_ Tx descriptor polling */
#define C4_APAD_XMT (1U << 11)
#define C4_MFCOM (1U << 8)
#define C4_RCVCCOM (1U << 4)
#define C4_TXSTRTM (1U << 6)
#define PCN_CSR5 0x05
#define PCN_CSR12 0x0c
#define PCN_CSR13 0x0d
#define PCN_CSR14 0x0e
#define PCN_CSR58 0x4a /* mapped to BCR20 */
#define PCN_BCR20 0x14 /* "software style" */
#define PCN_BCR33 0x21
#define PCN_BCR34 0x22
struct pcninit {
uint32_t init_mode;
uint32_t init_padr[2];
uint16_t init_ladrf[4];
uint32_t init_rdra;
uint32_t init_tdra;
uint32_t pad;
};
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, tx, rx;
unsigned phy, bmsr, anlpar;
};
unsigned pcn_mii_read(struct local *, int, int);
void pcn_mii_write(struct local *, int, int, int);
static unsigned pcn_csr_read(struct local *, int);
static void pcn_csr_write(struct local *, int, int);
static unsigned pcn_bcr_read(struct local *, int);
static void pcn_bcr_write(struct local *, int, int);
static void mii_initphy(struct local *l);
int
pcn_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
return (v == PCI_DEVICE(0x1022, 0x2000));
}
void *
pcn_init(unsigned tag, void *data)
{
unsigned val, fdx, loop;
struct local *l;
struct desc *txd, *rxd;
uint8_t *en;
struct pcninit initblock, *ib;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x14)); /* use mem space */
(void)CSR_READ_2(l, PCN_16RESET);
(void)CSR_READ_4(l, PCN_32RESET);
DELAY(1000); /* 1 milli second */
/* go 32bit RW mode */
CSR_WRITE_4(l, PCN_RDP, 0);
/* use 32bit software structure design "2" */
pcn_bcr_write(l, PCN_BCR20, 2);
mii_initphy(l);
en = data;
val = pcn_csr_read(l, PCN_CSR12); en[0] = val; en[1] = (val >> 8);
val = pcn_csr_read(l, PCN_CSR13); en[2] = val; en[3] = (val >> 8);
val = pcn_csr_read(l, PCN_CSR14); en[4] = val; en[5] = (val >> 8);
#if 1
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
#endif
/* speed and duplexity are found in MII ANR24 */
val = pcn_mii_read(l, l->phy, 24);
fdx = !!(val & (1U << 2));
printf("%s", (val & (1U << 0)) ? "100Mbps" : "10Mbps");
if (fdx)
printf("-FDX");
printf("\n");
txd = &l->txd[0];
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(VTOPHYS(l->rxstore[0]));
rxd[0].xd1 = htole32(R1_OWN | R1_ONES | FRAMESIZE);
rxd[1].xd0 = htole32(VTOPHYS(l->rxstore[1]));
rxd[1].xd1 = htole32(R1_OWN | R1_ONES | FRAMESIZE);
l->tx = l->rx = 0;
ib = &initblock;
ib->init_mode = htole32((0 << 28) | (1 << 20) | 0);
ib->init_padr[0] =
htole32(en[0] | (en[1] << 8) | (en[2] << 16) | (en[3] << 24));
ib->init_padr[1] =
htole32(en[4] | (en[5] << 8));
ib->init_rdra = htole32(VTOPHYS(rxd));
ib->init_tdra = htole32(VTOPHYS(txd));
pcn_csr_write(l, PCN_CSR3, C3_MISSM|C3_IDONM|C3_DXSUFLO);
pcn_csr_write(l, PCN_CSR4, C4_DMAPLUS|C4_APAD_XMT|
C4_MFCOM|C4_RCVCCOM|C4_TXSTRTM);
pcn_csr_write(l, PCN_CSR5, 0);
wbinv(&initblock, sizeof(initblock));
pcn_csr_write(l, PCN_CSR1, VTOPHYS(&initblock) & 0xffff);
pcn_csr_write(l, PCN_CSR2, (VTOPHYS(&initblock) >> 16) & 0xffff);
pcn_csr_write(l, PCN_CSR0, C0_INIT);
loop = 10000;
do {
DELAY(10);
} while (--loop > 0 && !(pcn_csr_read(l, PCN_CSR0) & C0_IDON));
if (loop == 0)
printf("pcn: timeout processing init block\n");
pcn_csr_write(l, PCN_CSR0, C0_STRT);
return l;
}
int
pcn_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned loop;
int tlen;
wbinv(buf, len);
txd = &l->txd[l->tx];
tlen = (-len) & T1_FLMASK; /* two's complement */
txd->xd0 = htole32(VTOPHYS(buf));
txd->xd1 = htole32(T1_OWN | T1_STP | T1_ENP | T1_ONES | tlen);
wbinv(txd, sizeof(struct desc));
/* pcn_csr_write(l, PCN_CSR0, C0_TDMD); */
loop = 100;
do {
if ((le32toh(txd->xd1) & T1_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct desc));
} while (--loop > 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
pcn_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
again:
rxd = &l->rxd[l->rx];
do {
inv(rxd, sizeof(struct desc));
rxstat = le32toh(rxd->xd1);
if ((rxstat & R1_OWN) == 0)
goto gotone;
DELAY(1000); /* 1 milli second */
} while (--bound > 0);
errno = 0;
return -1;
gotone:
if (rxstat & R1_ERR) {
rxd->xd1 |= htole32(R1_OWN);
rxd->xd2 = 0;
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
goto again;
}
/* good frame */
len = (rxstat & R1_FLMASK) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd1 |= htole32(R1_OWN);
rxd->xd2 = 0;
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
return len;
}
#define MREG(v) ((v)<< 0)
#define MPHY(v) ((v)<< 5)
#define MIIMD 0xffff
unsigned
pcn_mii_read(struct local *l, int phy, int reg)
{
pcn_bcr_write(l, PCN_BCR33, MREG(reg) | MPHY(phy));
return (pcn_bcr_read(l, PCN_BCR34) & MIIMD);
}
void
pcn_mii_write(struct local *l, int phy, int reg, int val)
{
pcn_bcr_write(l, PCN_BCR33, MREG(reg) | MPHY(phy));
pcn_bcr_write(l, PCN_BCR34, val);
}
static unsigned
pcn_csr_read(struct local *l, int r)
{
CSR_WRITE_4(l, PCN_RAP, r);
return CSR_READ_4(l, PCN_RDP);
}
static void
pcn_csr_write(struct local *l, int r, int v)
{
CSR_WRITE_4(l, PCN_RAP, r);
CSR_WRITE_4(l, PCN_RDP, v);
}
static unsigned
pcn_bcr_read(struct local *l, int r)
{
CSR_WRITE_4(l, PCN_RAP, r);
return CSR_READ_4(l, PCN_BDP);
}
static void
pcn_bcr_write(struct local *l, int r, int v)
{
CSR_WRITE_4(l, PCN_RAP, r);
CSR_WRITE_4(l, PCN_BDP, v);
}
#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 = pcn_mii_read(l, phy, MII_BMCR);
sts = pcn_mii_read(l, phy, MII_BMSR);
if (ctl != 0xffff && sts != 0xffff)
goto found;
}
printf("MII: no PHY found\n");
return;
found:
ctl = pcn_mii_read(l, phy, MII_BMCR);
pcn_mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
bound = 100;
do {
DELAY(10);
ctl = pcn_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;
pcn_mii_write(l, phy, MII_BMCR, ctl);
sts = pcn_mii_read(l, phy, MII_BMSR) |
pcn_mii_read(l, phy, MII_BMSR); /* read twice */
l->phy = phy;
l->bmsr = sts;
}
#if 0
static void
mii_dealan(struct local *, unsigned timo)
{
unsigned anar, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
pcn_mii_write(l, l->phy, MII_ANAR, anar);
pcn_mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = pcn_mii_read(l, l->phy, MII_BMSR) |
pcn_mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = pcn_mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
#endif
/* $NetBSD: printf.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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.
*/
/*
* printf -- format and write output using 'func' to write characters
*/
#include <sys/types.h>
#include <machine/stdarg.h>
#include <lib/libsa/stand.h>
#define MAXSTR 80
static int _doprnt(void (*)(int), const char *, va_list);
static void pr_int(unsigned long, int, char *);
static void sputchar(int);
static char *sbuf, *ebuf;
void
printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_doprnt(putchar, fmt, ap);
va_end(ap);
}
void
vprintf(const char *fmt, va_list ap)
{
_doprnt(putchar, fmt, ap);
}
int
sprintf(char *buf, const char *fmt, ...)
{
va_list ap;
sbuf = buf;
ebuf = buf + -(size_t)buf - 1;
va_start(ap, fmt);
_doprnt(sputchar, fmt, ap);
*sbuf = '\0';
return (sbuf - buf);
}
int
snprintf(char *buf, size_t size, const char *fmt, ...)
{
va_list ap;
sbuf = buf;
ebuf = buf + size - 1;
va_start(ap, fmt);
_doprnt(sputchar, fmt, ap);
*sbuf = '\0';
return (sbuf - buf);
}
static int
_doprnt(func, fmt, ap)
void (*func)(int); /* Function to put a character */
const char *fmt; /* Format string for pr_int/pr_float */
va_list ap; /* Arguments to pr_int/pr_float */
{
int i;
char *str;
char string[20];
int length;
int leftjust;
int longflag;
int fmax, fmin;
int leading;
int outcnt;
char fill;
char sign;
outcnt = 0;
while ((i = *fmt++) != '\0') {
if (i != '%') {
(*func)(i);
outcnt += 1;
continue;
}
if (*fmt == '%') {
(*func)(*fmt++);
outcnt += 1;
continue;
}
leftjust = (*fmt == '-');
if (leftjust)
fmt++;
fill = (*fmt == '0') ? *fmt++ : ' ';
if (*fmt == '*')
fmin = va_arg(ap, int);
else {
fmin = 0;
while ('0' <= *fmt && *fmt <= '9')
fmin = fmin * 10 + *fmt++ - '0';
}
if (*fmt != '.')
fmax = 0;
else {
fmt++;
if (*fmt == '*')
fmax = va_arg(ap, int);
else {
fmax = 0;
while ('0' <= *fmt && *fmt <= '9')
fmax = fmax * 10 + *fmt++ - '0';
}
}
longflag = (*fmt == 'l');
if (longflag)
fmt++;
if ((i = *fmt++) == '\0') {
(*func)('%');
outcnt += 1;
break;
}
str = string;
sign = ' ';
switch (i) {
case 'c':
str[0] = va_arg(ap, int);
str[1] = '\0';
fmax = 0;
fill = ' ';
break;
case 's':
str = va_arg(ap, char *);
fill = ' ';
break;
case 'd':
{
long l = va_arg(ap, long);
if (l < 0) { sign = '-' ; l = -l; }
pr_int((unsigned long)l, 10, str);
}
break;
case 'u':
pr_int(va_arg(ap, unsigned long), 10, str);
break;
case 'o':
pr_int(va_arg(ap, unsigned long), 8, str);
fmax = 0;
break;
case 'X':
case 'x':
pr_int(va_arg(ap, unsigned long), 16, str);
fmax = 0;
break;
case 'p':
pr_int(va_arg(ap, unsigned long), 16, str);
fill = '0';
fmin = 8;
fmax = 0;
(*func)('0'); (*func)('x');
outcnt += 2;
break;
default:
(*func)(i);
break;
}
for (i = 0; str[i] != '\0'; i++)
;
length = i;
if (fmin > MAXSTR || fmin < 0)
fmin = 0;
if (fmax > MAXSTR || fmax < 0)
fmax = 0;
leading = 0;
if (fmax != 0 || fmin != 0) {
if (fmax != 0 && length > fmax)
length = fmax;
if (fmin != 0)
leading = fmin - length;
if (sign == '-')
--leading;
}
outcnt += leading + length;
if (sign == '-')
outcnt += 1;
if (sign == '-' && fill == '0')
(*func)(sign);
if (leftjust == 0)
for (i = 0; i < leading; i++) (*func)(fill);
if (sign == '-' && fill == ' ')
(*func)(sign);
for (i = 0; i < length; i++)
(*func)(str[i]);
if (leftjust != 0)
for (i = 0; i < leading; i++) (*func)(fill);
}
return outcnt;
}
static void pr_int(lval, base, s)
unsigned long lval;
int base;
char *s;
{
char ptmp[12]; /* unsigned long requires 11 digit in octal form */
int i;
char *t = ptmp;
static const char hexdigit[] = "0123456789abcdef";
i = 1;
*t++ = '\0';
do {
*t++ = hexdigit[lval % base];
} while ((lval /= base) != 0 && ++i < sizeof(ptmp));
while ((*s++ = *--t) != '\0')
;
}
static void
sputchar(int c)
{
if (sbuf < ebuf)
*sbuf++ = c;
}
/* $NetBSD: rge.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_WRITE_1(l, r, v) *(volatile uint8_t *)((l)->csr+(r)) = (v)
#define CSR_READ_1(l, r) *(volatile uint8_t *)((l)->csr+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->csr+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
#define CSR_READ_4(l, r) in32rb((l)->csr+(r))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2, xd3;
};
#define T0_OWN 0x80000000 /* loaded for HW to send */
#define T0_EOR 0x40000000 /* end of ring */
#define T0_FS 0x20000000 /* first descriptor */
#define T0_LS 0x10000000 /* last descriptor */
#define T0_FRMASK 0x0000ffff
#define R0_OWN 0x80000000 /* empty for HW to load anew */
#define R0_EOR 0x40000000 /* end mark to form a ring */
#define R0_BUFLEN 0x00003ff8 /* max frag. size to receive */
#define R0_FS 0x20000000 /* start of frame */
#define R0_LS 0x10000000 /* end of frame */
#define R0_RES 0x00200000 /* Rx error summary */
#define R0_RUNT 0x00100000 /* runt frame received */
#define R0_CRC 0x00080000 /* CRC error found */
#define R0_FRMASK 0x00003fff /* 13:0 frame length */
#define RGE_IDR0 0x00 /* MAC address [0] */
#define RGE_IDR1 0x01 /* MAC address [1] */
#define RGE_IDR2 0x02 /* MAC address [2] */
#define RGE_IDR3 0x03 /* MAC address [3] */
#define RGE_IDR4 0x04 /* MAC address [4] */
#define RGE_IDR5 0x05 /* MAC address [5] */
#define RGE_TNPDS 0x20 /* Tx descriptor base paddr */
#define RGE_THPDS 0x28 /* high pro. Tx des. base paddr */
#define RGE_CR 0x37 /* command */
#define CR_RESET (1U << 4) /* reset S1C */
#define CR_RXEN (1U << 3) /* Rx enable */
#define CR_TXEN (1U << 2) /* Tx enable */
#define RGE_TPPOLL 0x38 /* activate desc polling */
#define RGE_IMR 0x3c /* interrupt mask */
#define RGE_ISR 0x3e /* interrupt status */
#define RGE_TCR 0x40 /* Tx control */
#define TCR_MAXDMA 0x0700 /* 10:8 Tx DMA burst size */
#define RGE_RCR 0x44 /* Rx control */
#define RCR_RXTFH 0xe000 /* 15:13 Rx FIFO threshold */
#define RCR_MAXDMA 0x0700 /* 10:8 Rx DMA burst size */
#define RCR_AE (1U << 5) /* accept error frame */
#define RCR_RE (1U << 4) /* accept runt frame */
#define RCR_AB (1U << 3) /* accept broadcast frame */
#define RCR_AM (1U << 2) /* accept multicast frame */
#define RCR_APM (1U << 1) /* accept unicast frame */
#define RCR_AAP (1U << 0) /* promiscuous */
#define RGE_PHYAR 0x60 /* PHY access */
#define RGE_PHYSR 0x6c /* PHY status */
#define RGE_RMS 0xda /* Rx maximum frame size */
#define RGE_RDSAR 0xe4 /* Rx descriptor base paddr */
#define RGE_ETTHR 0xec /* Tx threshold */
#define FRAMESIZE 1536
struct local {
struct desc txd[2]; /* 256B align */
uint8_t _hole0[256 - 2 * sizeof(struct desc)];
struct desc rxd[2]; /* 256B align */
uint8_t _hole1[256 - 2 * sizeof(struct desc)];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, tx, rx;
unsigned phy, bmsr, anlpar;
unsigned tcr, rcr;
};
static int mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_initphy(struct local *);
static void mii_dealan(struct local *, unsigned);
int
rge_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x10ec, 0x8169):
return 1;
}
return 0;
}
void *
rge_init(unsigned tag, void *data)
{
unsigned val;
struct local *l;
struct desc *txd, *rxd;
uint8_t *en = data;
l = ALLOC(struct local, 256); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x14)); /* use mem space */
CSR_WRITE_1(l, RGE_CR, CR_RESET);
do {
val = CSR_READ_1(l, RGE_CR);
} while (val & CR_RESET);
mii_initphy(l);
en = data;
en[0] = CSR_READ_1(l, RGE_IDR0);
en[1] = CSR_READ_1(l, RGE_IDR1);
en[2] = CSR_READ_1(l, RGE_IDR2);
en[3] = CSR_READ_1(l, RGE_IDR3);
en[4] = CSR_READ_1(l, RGE_IDR4);
en[5] = CSR_READ_1(l, RGE_IDR5);
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
mii_dealan(l, 5);
/* speed and duplexity can be seen in PHYSR */
val = CSR_READ_1(l, RGE_PHYSR);
if (val & (1U << 4))
printf("1000Mbps");
if (val & (1U << 3))
printf("100Mbps");
if (val & (1U << 2))
printf("10Mbps");
if (val & (1U << 0))
printf("-FDX");
printf("\n");
txd = &l->txd[0];
txd[1].xd0 = htole32(T0_EOR);
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(R0_OWN | FRAMESIZE);
rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
rxd[1].xd0 = htole32(R0_OWN | R0_EOR | FRAMESIZE);
rxd[1].xd2 = htole32(VTOPHYS(l->rxstore[1]));
wbinv(l, sizeof(struct local));
l->tx = l->rx = 0;
l->tcr = (03 << 24) | (07 << 8);
l->rcr = (07 << 13) | (07 << 8) | RCR_APM;
CSR_WRITE_1(l, RGE_CR, CR_TXEN | CR_RXEN);
CSR_WRITE_1(l, RGE_ETTHR, 0x3f);
CSR_WRITE_2(l, RGE_RMS, FRAMELEN);
CSR_WRITE_4(l, RGE_TCR, l->tcr);
CSR_WRITE_4(l, RGE_RCR, l->rcr);
CSR_WRITE_4(l, RGE_TNPDS, VTOPHYS(txd));
CSR_WRITE_4(l, RGE_RDSAR, VTOPHYS(rxd));
CSR_WRITE_4(l, RGE_TNPDS + 4, 0);
CSR_WRITE_4(l, RGE_RDSAR + 4, 0);
CSR_WRITE_2(l, RGE_ISR, ~0);
CSR_WRITE_2(l, RGE_IMR, 0);
return l;
}
int
rge_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned loop;
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd0 &= htole32(T0_EOR);
txd->xd0 |= htole32(T0_OWN | T0_FS | T0_LS | (len & T0_FRMASK));
wbinv(txd, sizeof(struct desc));
CSR_WRITE_1(l, RGE_TPPOLL, 0x40);
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:
l->tx ^= 1;
return len;
}
int
rge_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned 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;
return -1;
gotone:
if (rxstat & R0_RES) {
rxd->xd0 &= htole32(R0_EOR);
rxd->xd0 |= htole32(R0_OWN | FRAMESIZE);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
goto again;
}
len = rxstat & R0_FRMASK;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd0 &= htole32(R0_EOR);
rxd->xd0 |= htole32(R0_OWN | FRAMESIZE);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
return len;
}
static int
mii_read(struct local *l, int phy, int reg)
{
unsigned v, loop;
v = reg << 16;
CSR_WRITE_4(l, RGE_PHYAR, v);
loop = 100;
do {
v = CSR_READ_4(l, RGE_PHYAR);
} while ((v & (1U << 31)) == 0); /* wait for 0 -> 1 */
return v;
}
static void
mii_write(struct local *l, int phy, int reg, int data)
{
unsigned v;
v = (reg << 16) | (data & 0xffff) | (1U << 31);
CSR_WRITE_4(l, RGE_PHYAR, v);
do {
v = CSR_READ_4(l, RGE_PHYAR);
} while (v & (1U << 31)); /* wait for 1 -> 0 */
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
#define MII_GTCR 0x09 /* 1000baseT control */
#define GANA_1000TFDX 0x0200 /* advertise 1000baseT FDX */
#define GANA_1000THDX 0x0100 /* advertise 1000baseT HDX */
#define MII_GTSR 0x0a /* 1000baseT status */
#define GLPA_1000TFDX 0x0800 /* link partner 1000baseT FDX capable */
#define GLPA_1000THDX 0x0400 /* link partner 1000baseT HDX capable */
#define GLPA_ASM_DIR 0x0200 /* link partner asym. pause dir. capable */
static void
mii_initphy(struct local *l)
{
int phy, ctl, sts, bound;
for (phy = 0; phy < 32; phy++) {
ctl = mii_read(l, phy, MII_BMCR);
sts = mii_read(l, phy, MII_BMSR);
if (ctl != 0xffff && sts != 0xffff)
goto found;
}
printf("MII: no PHY found\n");
return;
found:
ctl = mii_read(l, phy, MII_BMCR);
mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
bound = 100;
do {
DELAY(10);
ctl = 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;
mii_write(l, phy, MII_BMCR, ctl);
sts = mii_read(l, phy, MII_BMSR) |
mii_read(l, phy, MII_BMSR); /* read twice */
l->phy = phy;
l->bmsr = sts;
}
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, gtcr, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
anar |= ANAR_FC;
gtcr = GANA_1000TFDX | GANA_1000THDX;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_GTCR, gtcr);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: siisata.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2008 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.
*
* 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 <lib/libsa/stand.h>
#include "globals.h"
static uint32_t pciiobase = PCI_XIOBASE;
int siisata_match(unsigned, void *);
void *siisata_init(unsigned, void *);
int
siisata_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x1095, 0x3112): /* SiI 3112 SATALink */
case PCI_DEVICE(0x1095, 0x3512): /* 3512 SATALink */
case PCI_DEVICE(0x1095, 0x3114): /* SiI 3114 SATALink */
return 1;
}
return 0;
}
void *
siisata_init(unsigned tag, void *data)
{
unsigned idreg;
int nchan, n;
struct dkdev_ata *l;
l = alloc(sizeof(struct dkdev_ata));
memset(l, 0, sizeof(struct dkdev_ata));
l->iobuf = allocaligned(512, 16);
l->tag = tag;
idreg = pcicfgread(tag, PCI_ID_REG);
l->bar[0] = pciiobase + (pcicfgread(tag, 0x10) &~ 01);
l->bar[1] = pciiobase + (pcicfgread(tag, 0x14) &~ 01);
l->bar[2] = pciiobase + (pcicfgread(tag, 0x18) &~ 01);
l->bar[3] = pciiobase + (pcicfgread(tag, 0x1c) &~ 01);
l->bar[4] = pciiobase + (pcicfgread(tag, 0x20) &~ 01);
l->bar[5] = pcicfgread(tag, 0x24) &~ 0x3ff;
if ((PCI_PRODUCT(idreg) & 0xf) == 0x2) {
/* 3112/3512 */
l->chan[0].cmd = l->bar[0];
l->chan[0].ctl = l->chan[0].alt = l->bar[1] | 02;
l->chan[0].dma = l->bar[4] + 0x0;
l->chan[1].cmd = l->bar[2];
l->chan[1].ctl = l->chan[1].alt = l->bar[3] | 02;
l->chan[1].dma = l->bar[4] + 0x8;
nchan = 2;
}
else {
/* 3114 - assume BA5 access is possible XXX */
l->chan[0].cmd = l->bar[5] + 0x080;
l->chan[0].ctl = l->chan[0].alt = (l->bar[5] + 0x088) | 02;
l->chan[1].cmd = l->bar[5] + 0x0c0;
l->chan[1].ctl = l->chan[1].alt = (l->bar[5] + 0x0c8) | 02;
l->chan[2].cmd = l->bar[5] + 0x280;
l->chan[2].ctl = l->chan[2].alt = (l->bar[5] + 0x288) | 02;
l->chan[3].cmd = l->bar[5] + 0x2c0;
l->chan[3].ctl = l->chan[3].alt = (l->bar[5] + 0x2c8) | 02;
nchan = 4;
}
/* configure PIO transfer mode */
pcicfgwrite(tag, 0x80, 0x00);
pcicfgwrite(tag, 0x84, 0x00);
for (n = 0; n < nchan; n++) {
if (satapresense(l, n)) {
/* drive present, now check whether soft reset works */
if (perform_atareset(l, n)) {
printf("port %d device present\n", n);
l->presense[n] = 1;
}
} else
l->presense[n] = 0;
}
return l;
}
/* $NetBSD: sip.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no VTOPHYS() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_READ(l, r) in32rb((l)->csr+(r))
#define CSR_WRITE(l, r, v) out32rb((l)->csr+(r), (v))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2;
uint32_t hole;
};
#define XD1_OWN (1U << 31)
#define XD1_OK (1U << 27)
#define SIP_CR 0x00
#define CR_RST (1U << 8) /* software reset */
#define CR_RXR (1U << 5) /* Rx abort and reset */
#define CR_TXR (1U << 4) /* Tx abort and reset */
#define CR_RXD (1U << 3) /* graceful Rx stop */
#define CR_RXE (1U << 2) /* run and activate Rx */
#define CR_TXD (1U << 1) /* graceful Tx stop */
#define CR_TXE (1U << 0) /* run and activate Tx */
#define SIP_CFG 0x04
#define SIP_MEAR 0x08
#define MEAR_EESEL (1U << 3) /* SEEP chipselect */
#define MEAR_EECLK (1U << 2) /* clock */
#define MEAR_EEDO (1U << 1) /* bit retrieve */
#define MEAR_EEDI (1U << 0) /* bit feed */
#define SIP_IMR 0x14
#define SIP_IER 0x18
#define SIP_TXDP 0x20
#define SIP_TXCFG 0x24
#define TXCFG_CSI (1U << 31)
#define TXCFG_HBI (1U << 30)
#define TXCFG_ATP (1U << 28)
#define TXCFG_DMA256 0x300000
#define SIP_RXDP 0x30
#define SIP_RXCFG 0x34
#define RXCFG_ATX (1U << 28)
#define RXCFG_DMA256 0x300000
#define SIP_RFCR 0x48
#define RFCR_RFEN (1U << 31) /* activate Rx filter */
#define RFCR_APM (1U << 27) /* accept perfect match */
#define SIP_RFDR 0x4c
#define SIP_MIBC 0x5c
#define SIP_BMCR 0x80
#define SIP_PHYSTS 0xc0
#define SIP_PHYCR 0xe4
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t store[2][FRAMESIZE];
unsigned csr, tx, rx;
unsigned phy, bmsr, anlpar;
unsigned cr;
};
static int read_eeprom(struct local *, int);
static unsigned mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_initphy(struct local *);
static void mii_dealan(struct local *, unsigned);
/* Table and macro to bit-reverse an octet. */
static const uint8_t bbr4[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
#define bbr(v) ((bbr4[(v)&0xf] << 4) | bbr4[((v)>>4) & 0xf])
int
sip_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x100b, 0x0020):
return 1;
}
return 0;
}
void *
sip_init(unsigned tag, void *data)
{
unsigned val, i, fdx, txc, rxc;
struct local *l;
struct desc *txd, *rxd;
uint16_t eedata[4], *ee;
uint8_t *en;
val = pcicfgread(tag, PCI_ID_REG);
if (PCI_DEVICE(0x100b, 0x0020) != val)
return NULL;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x14)); /* use mem space */
CSR_WRITE(l, SIP_IER, 0);
CSR_WRITE(l, SIP_IMR, 0);
CSR_WRITE(l, SIP_RFCR, 0);
CSR_WRITE(l, SIP_CR, CR_RST);
do {
val = CSR_READ(l, SIP_CR);
} while (val & CR_RST); /* S1C */
mii_initphy(l);
ee = eedata; en = data;
ee[0] = read_eeprom(l, 6);
ee[1] = read_eeprom(l, 7);
ee[2] = read_eeprom(l, 8);
ee[3] = read_eeprom(l, 9);
en[0] = ((*ee & 0x1) << 7);
ee++;
en[0] |= ((*ee & 0xFE00) >> 9);
en[1] = ((*ee & 0x1FE) >> 1);
en[2] = ((*ee & 0x1) << 7);
ee++;
en[2] |= ((*ee & 0xFE00) >> 9);
en[3] = ((*ee & 0x1FE) >> 1);
en[4] = ((*ee & 0x1) << 7);
ee++;
en[4] |= ((*ee & 0xFE00) >> 9);
en[5] = ((*ee & 0x1FE) >> 1);
for (i = 0; i < 6; i++)
en[i] = bbr(en[i]);
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x, ",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
mii_dealan(l, 5);
/* speed and duplexity are found in CFG */
val = CSR_READ(l, SIP_CFG);
fdx = !!(val & (1U << 29));
printf("%s", (val & (1U << 30)) ? "100Mbps" : "10Mbps");
if (fdx)
printf("-FDX");
printf("\n");
txd = &l->txd[0];
txd->xd0 = htole32(VTOPHYS(txd));
rxd = l->rxd;
rxd[0].xd0 = htole32(VTOPHYS(&rxd[1]));
rxd[0].xd1 = htole32(XD1_OWN | FRAMESIZE);
rxd[0].xd2 = htole32(VTOPHYS(l->store[0]));
rxd[1].xd0 = htole32(VTOPHYS(&rxd[0]));
rxd[1].xd1 = htole32(XD1_OWN | FRAMESIZE);
rxd[1].xd2 = htole32(VTOPHYS(l->store[1]));
wbinv(l, sizeof(struct local));
l->tx = l->rx = 0;
CSR_WRITE(l, SIP_RFCR, 0);
CSR_WRITE(l, SIP_RFDR, (en[1] << 8) | en[0]);
CSR_WRITE(l, SIP_RFCR, 2);
CSR_WRITE(l, SIP_RFDR, (en[3] << 8) | en[2]);
CSR_WRITE(l, SIP_RFCR, 4);
CSR_WRITE(l, SIP_RFDR, (en[5] << 8) | en[4]);
CSR_WRITE(l, SIP_RFCR, RFCR_RFEN | RFCR_APM);
txc = TXCFG_ATP | TXCFG_DMA256 | 0x1002;
rxc = RXCFG_DMA256 | 0x20;
if (fdx) {
txc |= TXCFG_CSI | TXCFG_HBI;
rxc |= RXCFG_ATX;
}
l->cr = CR_RXE;
CSR_WRITE(l, SIP_TXDP, VTOPHYS(txd));
CSR_WRITE(l, SIP_RXDP, VTOPHYS(rxd));
CSR_WRITE(l, SIP_TXCFG, txc);
CSR_WRITE(l, SIP_RXCFG, rxc);
CSR_WRITE(l, SIP_CR, l->cr);
return l;
}
int
sip_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned loop;
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd1 = htole32(XD1_OWN | (len & 0xfff));
wbinv(txd, sizeof(struct desc));
CSR_WRITE(l, SIP_CR, l->cr | CR_TXE);
loop = 100;
do {
if ((le32toh(txd->xd1) & XD1_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct desc));
} while (--loop != 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
sip_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
again:
rxd = &l->rxd[l->rx];
do {
inv(rxd, sizeof(struct desc));
rxstat = le32toh(rxd->xd1);
if ((rxstat & XD1_OWN) == 0)
goto gotone;
DELAY(1000); /* 1 milli second */
} while (--bound > 0);
errno = 0;
return -1;
gotone:
if ((rxstat & XD1_OK) == 0) {
rxd->xd1 = htole32(XD1_OWN | FRAMESIZE);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
goto again;
}
/* good frame */
len = (rxstat & 0xfff) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->store[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd1 = htole32(XD1_OWN | FRAMESIZE);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
CSR_WRITE(l, SIP_CR, l->cr);
return len;
}
static int
read_eeprom(struct local *l, int loc)
{
#define R110 06 /* SEEPROM READ op. */
unsigned data, v, i;
/* hold chip select */
v = MEAR_EESEL;
CSR_WRITE(l, SIP_MEAR, v);
data = (R110 << 6) | (loc & 0x3f); /* 6 bit addressing */
/* instruct R110 op. at loc in MSB first order */
for (i = (1 << 8); i != 0; i >>= 1) {
if (data & i)
v |= MEAR_EEDI;
else
v &= ~MEAR_EEDI;
CSR_WRITE(l, SIP_MEAR, v);
CSR_WRITE(l, SIP_MEAR, v | MEAR_EECLK);
DELAY(4);
CSR_WRITE(l, SIP_MEAR, v);
DELAY(4);
}
v = MEAR_EESEL;
/* read 16bit quantity in MSB first order */
data = 0;
for (i = 0; i < 16; i++) {
CSR_WRITE(l, SIP_MEAR, v | MEAR_EECLK);
DELAY(4);
data = (data << 1) | !!(CSR_READ(l, SIP_MEAR) & MEAR_EEDO);
CSR_WRITE(l, SIP_MEAR, v);
DELAY(4);
}
/* turn off chip select */
CSR_WRITE(l, SIP_MEAR, 0);
DELAY(4);
return data;
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
unsigned
mii_read(struct local *l, int phy, int reg)
{
unsigned val;
do {
val = CSR_READ(l, SIP_BMCR + (reg << 2));
} while (reg == MII_BMSR && val == 0);
return val & 0xffff;
}
void
mii_write(struct local *l, int phy, int reg, int val)
{
CSR_WRITE(l, SIP_BMCR + (reg << 2), val);
}
void
mii_initphy(struct local *l)
{
int phy, ctl, sts, bound;
for (phy = 0; phy < 32; phy++) {
ctl = mii_read(l, phy, MII_BMCR);
sts = mii_read(l, phy, MII_BMSR);
if (ctl != 0xffff && sts != 0xffff)
goto found;
}
printf("MII: no PHY found\n");
return;
found:
ctl = mii_read(l, phy, MII_BMCR);
mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
bound = 100;
do {
DELAY(10);
ctl = 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;
mii_write(l, phy, MII_BMCR, ctl);
sts = mii_read(l, phy, MII_BMSR) |
mii_read(l, phy, MII_BMSR); /* read twice */
l->phy = phy; /* should be 0 */
l->bmsr = sts;
}
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: skg.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2010 Frank Wille.
* All rights reserved.
*
* Written by Frank Wille for The NetBSD Project.
*
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_WRITE_1(l, r, v) *(volatile uint8_t *)((l)->csr+(r)) = (v)
#define CSR_READ_1(l, r) *(volatile uint8_t *)((l)->csr+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->csr+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
#define CSR_READ_4(l, r) in32rb((l)->csr+(r))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2, xd3, xd4;
uint32_t rsrvd[3];
};
#define CTL_LS 0x20000000
#define CTL_FS 0x40000000
#define CTL_OWN 0x80000000
#define CTL_DEFOPC 0x00550000
#define FRAMEMASK 0x0000ffff
#define RXSTAT_RXOK 0x00000100
#define SK_CSR 0x0004
#define CSR_SW_RESET 0x0001
#define CSR_SW_UNRESET 0x0002
#define CSR_MASTER_RESET 0x0004
#define CSR_MASTER_UNRESET 0x0008
#define SK_IMR 0x000c
#define SK_BMU_RX_CSR0 0x0060
#define SK_BMU_TXS_CSR0 0x0068
#define SK_MAC0 0x0100
#define SK_MAC1 0x0108
#define SK_GPIO 0x015c
#define SK_RAMCTL 0x01a0
#define SK_TXAR1_COUNTERCTL 0x0210
#define TXARCTL_ON 0x02
#define TXARCTL_FSYNC_ON 0x80
#define SK_RXQ1_CURADDR_LO 0x0420
#define SK_RXQ1_CURADDR_HI 0x0424
#define SK_RXQ1_BMU_CSR 0x0434
#define RXBMU_CLR_IRQ_EOF 0x00000002
#define RXBMU_RX_START 0x00000010
#define RXBMU_RX_STOP 0x00000020
#define RXBMU_POLL_ON 0x00000080
#define RXBMU_TRANSFER_SM_UNRESET 0x00000200
#define RXBMU_DESCWR_SM_UNRESET 0x00000800
#define RXBMU_DESCRD_SM_UNRESET 0x00002000
#define RXBMU_SUPERVISOR_SM_UNRESET 0x00008000
#define RXBMU_PFI_SM_UNRESET 0x00020000
#define RXBMU_FIFO_UNRESET 0x00080000
#define RXBMU_DESC_UNRESET 0x00200000
#define SK_TXQS1_CURADDR_LO 0x0620
#define SK_TXQS1_CURADDR_HI 0x0624
#define SK_TXQS1_BMU_CSR 0x0634
#define TXBMU_CLR_IRQ_EOF 0x00000002
#define TXBMU_TX_START 0x00000010
#define TXBMU_TX_STOP 0x00000020
#define TXBMU_POLL_ON 0x00000080
#define TXBMU_TRANSFER_SM_UNRESET 0x00000200
#define TXBMU_DESCWR_SM_UNRESET 0x00000800
#define TXBMU_DESCRD_SM_UNRESET 0x00002000
#define TXBMU_SUPERVISOR_SM_UNRESET 0x00008000
#define TXBMU_PFI_SM_UNRESET 0x00020000
#define TXBMU_FIFO_UNRESET 0x00080000
#define TXBMU_DESC_UNRESET 0x00200000
#define SK_RXRB1_START 0x0800
#define SK_RXRB1_END 0x0804
#define SK_RXRB1_WR_PTR 0x0808
#define SK_RXRB1_RD_PTR 0x080c
#define SK_RXRB1_CTLTST 0x0828
#define RBCTL_UNRESET 0x02
#define RBCTL_ON 0x08
#define RBCTL_STORENFWD_ON 0x20
#define SK_TXRBS1_START 0x0a00
#define SK_TXRBS1_END 0x0a04
#define SK_TXRBS1_WR_PTR 0x0a08
#define SK_TXRBS1_RD_PTR 0x0a0c
#define SK_TXRBS1_CTLTST 0x0a28
#define SK_RXMF1_CTRL_TEST 0x0c48
#define RFCTL_OPERATION_ON 0x00000008
#define RFCTL_RESET_CLEAR 0x00000002
#define SK_TXMF1_CTRL_TEST 0x0D48
#define TFCTL_OPERATION_ON 0x00000008
#define TFCTL_RESET_CLEAR 0x00000002
#define SK_GMAC_CTRL 0x0f00
#define GMAC_LOOP_OFF 0x00000010
#define GMAC_PAUSE_ON 0x00000008
#define GMAC_RESET_CLEAR 0x00000002
#define GMAC_RESET_SET 0x00000001
#define SK_GPHY_CTRL 0x0f04
#define GPHY_INT_POL_HI 0x08000000
#define GPHY_DIS_FC 0x02000000
#define GPHY_DIS_SLEEP 0x01000000
#define GPHY_ENA_XC 0x00040000
#define GPHY_ENA_PAUSE 0x00002000
#define GPHY_RESET_CLEAR 0x00000002
#define GPHY_RESET_SET 0x00000001
#define GPHY_ANEG_ALL 0x0009c000
#define GPHY_COPPER 0x00f00000
#define SK_LINK_CTRL 0x0f10
#define LINK_RESET_CLEAR 0x0002
#define LINK_RESET_SET 0x0001
#define YUKON_GPCR 0x2804
#define GPCR_TXEN 0x1000
#define GPCR_RXEN 0x0800
#define YUKON_SA1 0x281c
#define YUKON_SA2 0x2828
#define YUKON_SMICR 0x2880
#define SMICR_PHYAD(x) (((x) & 0x1f) << 11)
#define SMICR_REGAD(x) (((x) & 0x1f) << 6)
#define SMICR_OP_READ 0x0020
#define SMICR_OP_WRITE 0x0000
#define SMICR_READ_VALID 0x0010
#define SMICR_BUSY 0x0008
#define YUKON_SMIDR 0x2884
#define MII_PSSR 0x11 /* MAKPHY status register */
#define PSSR_DUPLEX 0x2000 /* FDX */
#define PSSR_RESOLVED 0x0800 /* speed and duplex resolved */
#define PSSR_LINK 0x0400 /* link indication */
#define PSSR_SPEED(x) (((x) >> 14) & 0x3)
#define SPEED10 0
#define SPEED100 1
#define SPEED1000 2
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, rx, tx, phy;
uint16_t pssr, anlpar;
};
static int mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_initphy(struct local *);
static void mii_dealan(struct local *, unsigned);
int
skg_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x11ab, 0x4320):
return 1;
}
return 0;
}
void *
skg_init(unsigned tag, void *data)
{
struct local *l;
struct desc *txd, *rxd;
uint8_t *en;
unsigned i;
uint16_t reg;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x10)); /* use mem space */
/* reset the chip */
CSR_WRITE_2(l, SK_CSR, CSR_SW_RESET);
CSR_WRITE_2(l, SK_CSR, CSR_MASTER_RESET);
CSR_WRITE_2(l, SK_LINK_CTRL, LINK_RESET_SET);
DELAY(1000);
CSR_WRITE_2(l, SK_CSR, CSR_SW_UNRESET);
DELAY(2);
CSR_WRITE_2(l, SK_CSR, CSR_MASTER_UNRESET);
CSR_WRITE_2(l, SK_LINK_CTRL, LINK_RESET_CLEAR);
CSR_WRITE_4(l, SK_RAMCTL, 2); /* enable RAM interface */
mii_initphy(l);
/* read ethernet address */
en = data;
if (brdtype == BRD_SYNOLOGY)
read_mac_from_flash(en);
else
for (i = 0; i < 6; i++)
en[i] = CSR_READ_1(l, SK_MAC0 + i);
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
/* set station address */
for (i = 0; i < 3; i++)
CSR_WRITE_2(l, YUKON_SA1 + i * 4,
(en[i * 2] << 8) | en[i * 2 + 1]);
/* configure RX and TX MAC FIFO */
CSR_WRITE_1(l, SK_RXMF1_CTRL_TEST, RFCTL_RESET_CLEAR);
CSR_WRITE_4(l, SK_RXMF1_CTRL_TEST, RFCTL_OPERATION_ON);
CSR_WRITE_1(l, SK_TXMF1_CTRL_TEST, TFCTL_RESET_CLEAR);
CSR_WRITE_4(l, SK_TXMF1_CTRL_TEST, TFCTL_OPERATION_ON);
mii_dealan(l, 5);
switch (PSSR_SPEED(l->pssr)) {
case SPEED1000:
printf("1000Mbps");
break;
case SPEED100:
printf("100Mbps");
break;
case SPEED10:
printf("10Mbps");
break;
}
if (l->pssr & PSSR_DUPLEX)
printf("-FDX");
printf("\n");
/* configure RAM buffers, assuming 64k RAM */
CSR_WRITE_4(l, SK_RXRB1_CTLTST, RBCTL_UNRESET);
CSR_WRITE_4(l, SK_RXRB1_START, 0);
CSR_WRITE_4(l, SK_RXRB1_WR_PTR, 0);
CSR_WRITE_4(l, SK_RXRB1_RD_PTR, 0);
CSR_WRITE_4(l, SK_RXRB1_END, 0xfff);
CSR_WRITE_4(l, SK_RXRB1_CTLTST, RBCTL_ON);
CSR_WRITE_4(l, SK_TXRBS1_CTLTST, RBCTL_UNRESET);
CSR_WRITE_4(l, SK_TXRBS1_CTLTST, RBCTL_STORENFWD_ON);
CSR_WRITE_4(l, SK_TXRBS1_START, 0x1000);
CSR_WRITE_4(l, SK_TXRBS1_WR_PTR, 0x1000);
CSR_WRITE_4(l, SK_TXRBS1_RD_PTR, 0x1000);
CSR_WRITE_4(l, SK_TXRBS1_END, 0x1fff);
CSR_WRITE_4(l, SK_TXRBS1_CTLTST, RBCTL_ON);
/* setup descriptors and BMU */
CSR_WRITE_1(l, SK_TXAR1_COUNTERCTL, TXARCTL_ON|TXARCTL_FSYNC_ON);
txd = &l->txd[0];
txd[0].xd1 = htole32(VTOPHYS(&txd[1]));
txd[1].xd1 = htole32(VTOPHYS(&txd[0]));
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(FRAMESIZE|CTL_DEFOPC|CTL_LS|CTL_FS|CTL_OWN);
rxd[0].xd1 = htole32(VTOPHYS(&rxd[1]));
rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
rxd[1].xd0 = htole32(FRAMESIZE|CTL_DEFOPC|CTL_LS|CTL_FS|CTL_OWN);
rxd[1].xd1 = htole32(VTOPHYS(&rxd[0]));
rxd[1].xd2 = htole32(VTOPHYS(l->rxstore[1]));
wbinv(l, sizeof(struct local));
CSR_WRITE_4(l, SK_RXQ1_BMU_CSR,
RXBMU_TRANSFER_SM_UNRESET|RXBMU_DESCWR_SM_UNRESET|
RXBMU_DESCRD_SM_UNRESET|RXBMU_SUPERVISOR_SM_UNRESET|
RXBMU_PFI_SM_UNRESET|RXBMU_FIFO_UNRESET|
RXBMU_DESC_UNRESET);
CSR_WRITE_4(l, SK_RXQ1_CURADDR_LO, VTOPHYS(rxd));
CSR_WRITE_4(l, SK_RXQ1_CURADDR_HI, 0);
CSR_WRITE_4(l, SK_TXQS1_BMU_CSR,
TXBMU_TRANSFER_SM_UNRESET|TXBMU_DESCWR_SM_UNRESET|
TXBMU_DESCRD_SM_UNRESET|TXBMU_SUPERVISOR_SM_UNRESET|
TXBMU_PFI_SM_UNRESET|TXBMU_FIFO_UNRESET|
TXBMU_DESC_UNRESET|TXBMU_POLL_ON);
CSR_WRITE_4(l, SK_TXQS1_CURADDR_LO, VTOPHYS(txd));
CSR_WRITE_4(l, SK_TXQS1_CURADDR_HI, 0);
CSR_WRITE_4(l, SK_IMR, 0);
CSR_WRITE_4(l, SK_RXQ1_BMU_CSR, RXBMU_RX_START);
reg = CSR_READ_2(l, YUKON_GPCR);
reg |= GPCR_TXEN | GPCR_RXEN;
CSR_WRITE_2(l, YUKON_GPCR, reg);
return l;
}
int
skg_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned loop;
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd0 = htole32((len & FRAMEMASK)|CTL_DEFOPC|CTL_FS|CTL_LS|CTL_OWN);
wbinv(txd, sizeof(struct desc));
CSR_WRITE_4(l, SK_BMU_TXS_CSR0, TXBMU_TX_START);
loop = 100;
do {
if ((le32toh(txd->xd0) & CTL_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct desc));
} while (--loop > 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
skg_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned bound, ctl, 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));
ctl = le32toh(rxd->xd0);
if ((ctl & CTL_OWN) == 0)
goto gotone;
DELAY(1000); /* 1 milli second */
} while (--bound > 0);
errno = 0;
return -1;
gotone:
rxstat = le32toh(rxd->xd4);
if ((rxstat & RXSTAT_RXOK) == 0) {
rxd->xd0 = htole32(FRAMESIZE|CTL_DEFOPC|CTL_LS|CTL_FS|CTL_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
goto again;
}
len = ctl & FRAMEMASK;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd0 = htole32(FRAMESIZE|CTL_DEFOPC|CTL_LS|CTL_FS|CTL_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
return len;
}
static int
mii_read(struct local *l, int phy, int reg)
{
unsigned loop, v;
CSR_WRITE_2(l, YUKON_SMICR, SMICR_PHYAD(phy) | SMICR_REGAD(reg) |
SMICR_OP_READ);
loop = 1000;
do {
DELAY(1);
v = CSR_READ_2(l, YUKON_SMICR);
} while ((v & SMICR_READ_VALID) == 0 && --loop);
if (loop == 0) {
printf("mii_read timeout!\n");
return 0;
}
return CSR_READ_2(l, YUKON_SMIDR);
}
static void
mii_write(struct local *l, int phy, int reg, int data)
{
unsigned loop, v;
CSR_WRITE_2(l, YUKON_SMIDR, data);
CSR_WRITE_2(l, YUKON_SMICR, SMICR_PHYAD(phy) | SMICR_REGAD(reg) |
SMICR_OP_WRITE);
loop = 1000;
do {
DELAY(1);
v = CSR_READ_2(l, YUKON_SMICR);
} while ((v & SMICR_BUSY) != 0 && --loop);
if (loop == 0)
printf("mii_write timeout!\n");
}
#define MII_BMCR 0x00 /* Basic mode control register (rw) */
#define BMCR_AUTOEN 0x1000 /* autonegotiation enable */
#define BMCR_STARTNEG 0x0200 /* restart autonegotiation */
#define MII_BMSR 0x01 /* Basic mode status register (ro) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
#define MII_GTCR 0x09 /* 1000baseT control */
#define GANA_1000TFDX 0x0200 /* advertise 1000baseT FDX */
#define GANA_1000THDX 0x0100 /* advertise 1000baseT HDX */
#define MII_GTSR 0x0a /* 1000baseT status */
#define GLPA_1000TFDX 0x0800 /* link partner 1000baseT FDX capable */
#define GLPA_1000THDX 0x0400 /* link partner 1000baseT HDX capable */
static void
mii_initphy(struct local *l)
{
unsigned val;
l->phy = 0;
/* take PHY out of reset */
val = CSR_READ_4(l, SK_GPIO);
CSR_WRITE_4(l, SK_GPIO, (val | 0x2000000) & ~0x200);
/* GMAC and GPHY reset */
CSR_WRITE_4(l, SK_GPHY_CTRL, GPHY_RESET_SET);
CSR_WRITE_4(l, SK_GMAC_CTRL, GMAC_RESET_SET);
DELAY(1000);
CSR_WRITE_4(l, SK_GMAC_CTRL, GMAC_RESET_CLEAR);
CSR_WRITE_4(l, SK_GMAC_CTRL, GMAC_RESET_SET);
DELAY(1000);
val = GPHY_INT_POL_HI | GPHY_DIS_FC | GPHY_DIS_SLEEP | GPHY_ENA_XC |
GPHY_ANEG_ALL | GPHY_ENA_PAUSE | GPHY_COPPER;
CSR_WRITE_4(l, SK_GPHY_CTRL, val | GPHY_RESET_SET);
DELAY(1000);
CSR_WRITE_4(l, SK_GPHY_CTRL, val | GPHY_RESET_CLEAR);
CSR_WRITE_4(l, SK_GMAC_CTRL, GMAC_LOOP_OFF | GMAC_PAUSE_ON |
GMAC_RESET_CLEAR);
}
static void
mii_dealan(struct local *l, unsigned timo)
{
unsigned bmsr, bound;
mii_write(l, l->phy, MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD |
ANAR_10 | ANAR_CSMA | ANAR_FC);
mii_write(l, l->phy, MII_GTCR, GANA_1000TFDX | GANA_1000THDX);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((bmsr & BMSR_LINK) && (bmsr & BMSR_ACOMP)) {
l->pssr = mii_read(l, l->phy, MII_PSSR);
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
if ((l->pssr & PSSR_RESOLVED) == 0)
continue;
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
}
/* $NetBSD: sme.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* Copyright (c) 2008 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no VTOPHYS() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_READ(l, r) in32rb((l)->csr+(r))
#define CSR_WRITE(l, r, v) out32rb((l)->csr+(r), (v))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2, xd3;
};
#define T0_OWN (1U<<31) /* */
#define T0_ES (1U<<15) /* error summary */
#define T0_FL 0x7fff0000 /* frame length */
#define T1_LS (1U<<30) /* last descriptor of Tx frame */
#define T1_FS (1U<<29) /* first descriptor of Tx frame */
#define T1_TER (1U<<25) /* wrap mark to form a ring */
#define T1_TCH (1U<<24) /* TDES3 points the next desc */
#define T1_FL 0x00007ff /* Tx frame/segment length */
#define R0_OWN (1U<<31) /* */
#define R0_FL 0x3fff0000 /* frame length */
#define R0_ES (1U<<15) /* error summary */
#define R1_RER (1U<<25) /* wrap mark to form a ring */
#define R1_RCH (1U<<24) /* RDES3 points the next desc */
/* RDES1 will be never changed while operation */
#define BUSMODE 0x00
#define TXPOLLD 0x04 /* start transmission */
#define RXPOLLD 0x08 /* start receiving */
#define RXDBASE 0x0c /* Rx descriptor list base */
#define TXDBASE 0x10 /* Tx descriptor list base */
#define DMACCTL 0x18 /* DMAC control */
#define DMACCTL_ST (1U<<13) /* start/stop Tx DMA */
#define DMACCTL_SR (1U<< 1) /* start/stop Rx DMA */
#define MAC_CR 0x80 /* MAC control */
#define MACCR_FDPX (1U<<20) /* full duplex operation */
#define MACCR_TXEN (1U<< 3) /* enable xmit */
#define MACCR_RXEN (1U<< 2) /* enable recv */
#define ADDRH 0x84 /* ea 5:4 */
#define ADDRL 0x88 /* ea 3:0 */
#define MIIADDR 0x94 /* MII control */
#define MIIDATA 0x98 /* MII data */
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, tx, rx;
unsigned phy, bmsr, anlpar;
};
static int mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_dealan(struct local *, unsigned);
int
sme_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x1055, 0xe940):
return 1;
}
return 0;
}
void *
sme_init(unsigned tag, void *data)
{
struct local *l;
struct desc *txd, *rxd;
unsigned mac32, mac16, val, fdx;
uint8_t *en;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x1c)); /* BAR3 mem space, LE */
l->phy = 1; /* 9420 internal PHY */
en = data;
mac32 = CSR_READ(l, ADDRL);
mac16 = CSR_READ(l, ADDRH);
en[0] = mac32;
en[1] = mac32 >> 8;
en[2] = mac32 >> 16;
en[3] = mac32 >> 24;
en[4] = mac16;
en[5] = mac16 >> 8;
#if 1
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
#endif
mii_dealan(l, 5);
/* speed and duplexity can be seen in MII 31 */
val = mii_read(l, l->phy, 31);
fdx = !!(val & (1U << 4));
printf("%s", (val & (1U << 3)) ? "100Mbps" : "10Mbps");
if (fdx)
printf("-FDX");
printf("\n");
txd = &l->txd[0];
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(R0_OWN);
rxd[0].xd1 = htole32(R1_RCH | FRAMESIZE);
rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
rxd[0].xd3 = htole32(VTOPHYS(&rxd[1]));
rxd[1].xd0 = htole32(R0_OWN);
rxd[1].xd1 = htole32(R1_RER | FRAMESIZE);
rxd[1].xd2 = htole32(VTOPHYS(l->rxstore[1]));
/* R1_RER neglects xd3 */
l->tx = l->rx = 0;
wbinv(l, sizeof(struct local));
CSR_WRITE(l, TXDBASE, VTOPHYS(txd));
CSR_WRITE(l, RXDBASE, VTOPHYS(rxd));
val = MACCR_TXEN | MACCR_RXEN;
if (fdx)
val |= MACCR_FDPX;
CSR_WRITE(l, BUSMODE, 0);
CSR_WRITE(l, DMACCTL, DMACCTL_ST | DMACCTL_SR);
CSR_WRITE(l, MAC_CR, val); /* (FDX), Tx/Rx enable */
CSR_WRITE(l, RXPOLLD, 01); /* start receiving */
return l;
}
int
sme_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned txstat, loop;
/* send a single frame with no T1_TER|T1_TCH designation */
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd1 = htole32(T1_FS | T1_LS | (len & T1_FL));
txd->xd0 = htole32(T0_OWN | (len & T0_FL) << 16);
wbinv(txd, sizeof(struct desc));
CSR_WRITE(l, TXPOLLD, 01); /* start transmission */
loop = 100;
do {
txstat = le32toh(txd->xd0);
if (txstat & T0_ES)
break;
if ((txstat & T0_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct desc));
} while (--loop != 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
sme_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
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;
return -1;
gotone:
if (rxstat & R0_ES) {
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
CSR_WRITE(l, RXPOLLD, 01); /* restart receiving */
goto again;
}
/* good frame */
len = (rxstat & R0_FL) >> 16 /* no FCS included */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
CSR_WRITE(l, RXPOLLD, 01); /* necessary? */
return len;
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
static int
mii_read(struct local *l, int phy, int reg)
{
uint32_t ctl;
do {
ctl = CSR_READ(l, MIIADDR);
} while (ctl & 01);
ctl = (phy << 11) | (reg << 6) | (0 << 1); /* READ op */
CSR_WRITE(l, MIIADDR, ctl);
do {
ctl = CSR_READ(l, MIIADDR);
} while (ctl & 01);
return CSR_READ(l, MIIDATA);
}
void
mii_write(struct local *l, int phy, int reg, int val)
{
uint32_t ctl;
do {
ctl = CSR_READ(l, MIIADDR);
} while (ctl & 01);
ctl = (phy << 11) | (reg << 6) | (1 << 1); /* WRITE op */
CSR_WRITE(l, MIIDATA, val);
}
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: tlp.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access for CSR register.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_READ(l, r) in32rb((l)->csr+(r))
#define CSR_WRITE(l, r, v) out32rb((l)->csr+(r), (v))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct desc {
uint32_t xd0, xd1, xd2, xd3;
};
#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_TER (1U<<25) /* end of ring mark */
#define T1_TCH (1U<<24) /* TDES3 points the next desc */
#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 R1_RCH (1U<<24) /* RDES3 points the next desc */
#define R0_FLMASK 0x3fff0000 /* frame length 29:16 */
#define R1_RBS_MASK 0x7ff /* segment size 10:0 */
#define PAR_CSR0 0x00 /* bus mode */
#define PAR_DEFAULTS 0x00001000 /* PDF sez it should be ... */
#define PAR_SWR 01
#define TDR_CSR1 0x08 /* T0_OWN poll demand */
#define RDR_CSR2 0x10 /* R0_OWN poll demand */
#define RDB_CSR3 0x18 /* Rx descriptor base */
#define TDB_CSR4 0x20 /* Tx descriptor base */
#define SR_CSR5 0x28 /* interrupt stauts */
#define NAR_CSR6 0x30 /* operation mode */
#define NAR_NOSQE (1U<<19) /* _not_ use SQE signal */
#define NAR_TEN (1U<<13) /* instruct start/stop Tx */
#define NAR_REN (1U<< 1) /* instruct start/stop Rx */
#define IER_CSR7 0x38 /* interrupt enable mask */
#define SPR_CSR9 0x48 /* SEEPROM and MII management */
#define MII_MDI (1U<<19) /* 0/1 presense after read op */
#define MII_MIDIR (1U<<18) /* 1 for PHY->HOST */
#define MII_MDO (1U<<17) /* 0/1 for write op */
#define MII_MDC (1U<<16) /* MDIO clock */
#define SROM_RD (1U<<14) /* read operation */
#define SROM_WR (1U<<13) /* write openration */
#define SROM_SR (1U<<11) /* SEEPROM select */
#define PAR0_CSR25 0xa4 /* MAC 3:0 */
#define PAR1_CSR26 0xa8 /* MAC 5:4 */
#define AN_OMODE 0xfc /* operation mode */
#define FRAMESIZE 1536
struct local {
struct desc txd[2];
struct desc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, omr, tx, rx;
unsigned phy, bmsr, anlpar;
};
static unsigned mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_initphy(struct local *);
static void mii_dealan(struct local *, unsigned);
int
tlp_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x1317, 0x0985): /* ADMTek/Infineon 983B/BX */
return 1;
}
return 0;
}
void *
tlp_init(unsigned tag, void *data)
{
struct local *l;
struct desc *txd, *rxd;
unsigned i, val, fdx;
uint8_t *en;
l = ALLOC(struct local, 2 * sizeof(struct desc)); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x14)); /* use mem space */
CSR_WRITE(l, PAR_CSR0, PAR_SWR);
i = 100;
do {
DELAY(10);
} while (i-- > 0 && (CSR_READ(l, PAR_CSR0) & PAR_SWR) != 0);
CSR_WRITE(l, PAR_CSR0, PAR_DEFAULTS);
l->omr = NAR_NOSQE;
CSR_WRITE(l, NAR_CSR6, l->omr);
CSR_WRITE(l, SR_CSR5, ~0);
CSR_WRITE(l, IER_CSR7, 0);
en = data;
val = CSR_READ(l, PAR0_CSR25);
en[0] = val & 0xff;
en[1] = (val >> 8) & 0xff;
en[2] = (val >> 16) & 0xff;
en[3] = (val >> 24) & 0xff;
val = CSR_READ(l, PAR1_CSR26);
en[4] = val & 0xff;
en[5] = (val >> 8) & 0xff;
#if 1
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
#endif
mii_initphy(l);
mii_dealan(l, 5);
val = CSR_READ(l, AN_OMODE);
if (val & (1U << 29)) {
printf("%s", (val & (1U << 31)) ? "100Mbps" : "10Mbps");
fdx = !!(val & (1U << 30));
if (fdx)
printf("-FDX");
printf("\n");
}
txd = &l->txd[0];
txd[1].xd1 = htole32(T1_TER);
rxd = &l->rxd[0];
rxd[0].xd0 = htole32(R0_OWN);
rxd[0].xd1 = htole32(FRAMESIZE);
rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
rxd[1].xd0 = htole32(R0_OWN);
rxd[1].xd1 = htole32(R1_RER | FRAMESIZE);
rxd[1].xd2 = htole32(VTOPHYS(l->rxstore[1]));
l->tx = l->rx = 0;
/* make sure the entire descriptors transfered to memory */
wbinv(l, sizeof(struct local));
CSR_WRITE(l, TDB_CSR4, VTOPHYS(txd));
CSR_WRITE(l, RDB_CSR3, VTOPHYS(rxd));
/* start Tx/Rx */
CSR_WRITE(l, NAR_CSR6, l->omr | NAR_TEN | NAR_REN);
return l;
}
int
tlp_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct desc *txd;
unsigned txstat, loop;
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->xd2 = htole32(VTOPHYS(buf));
txd->xd1 &= htole32(T1_TER);
txd->xd1 |= htole32(T1_FS | T1_LS | (len & T1_TBS_MASK));
txd->xd0 = htole32(T0_OWN);
wbinv(txd, sizeof(struct desc));
CSR_WRITE(l, TDR_CSR1, 01);
loop = 100;
do {
txstat = le32toh(txd->xd0);
if ((txstat & T0_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct desc));
} while (--loop != 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
tlp_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct desc *rxd;
unsigned 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;
return -1;
gotone:
if (rxstat & R0_ES) {
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
goto again;
}
/* good frame */
len = ((rxstat & R0_FLMASK) >> 16) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->xd0 = htole32(R0_OWN);
wbinv(rxd, sizeof(struct desc));
l->rx ^= 1;
return len;
}
/*
* bare MII access with bitbang'ing
*/
#define R110 6 /* SEEPROM/MDIO read op */
#define W101 5 /* SEEPROM/MDIO write op */
#define CS (1U << 0) /* hold chip select */
#define CLK (1U << 1) /* clk bit */
#define D1 (1U << 2) /* bit existence */
#define VV (1U << 3) /* taken 0/1 from SEEPROM */
static unsigned
mii_read(struct local *l, int phy, int reg)
{
unsigned data, rv, v, i;
data = (R110 << 10) | (phy << 5) | reg;
CSR_WRITE(l, SPR_CSR9, MII_MDO);
for (i = 0; i < 32; i++) {
CSR_WRITE(l, SPR_CSR9, MII_MDO | MII_MDC);
DELAY(1);
CSR_WRITE(l, SPR_CSR9, MII_MDO);
DELAY(1);
}
CSR_WRITE(l, SPR_CSR9, 0);
v = 0; /* 4OP + 5ADDR + 5REG */
for (i = (1 << 13); i != 0; i >>= 1) {
if (data & i)
v |= MII_MDO;
else
v &= ~MII_MDO;
CSR_WRITE(l, SPR_CSR9, v);
DELAY(1);
CSR_WRITE(l, SPR_CSR9, v | MII_MDC);
DELAY(1);
CSR_WRITE(l, SPR_CSR9, v);
DELAY(1);
}
rv = 0; /* 2TA + 16MDI */
for (i = 0; i < 18; i++) {
CSR_WRITE(l, SPR_CSR9, MII_MIDIR);
DELAY(1);
rv = (rv << 1) | !!(CSR_READ(l, SPR_CSR9) & MII_MDI);
CSR_WRITE(l, SPR_CSR9, MII_MIDIR | MII_MDC);
DELAY(1);
}
CSR_WRITE(l, SPR_CSR9, 0);
return rv & 0xffff;
}
static void
mii_write(struct local *l, int phy, int reg, int val)
{
unsigned data, v, i;
data = (W101 << 28) | (phy << 23) | (reg << 18) | (02 << 16);
data |= val & 0xffff;
CSR_WRITE(l, SPR_CSR9, MII_MDO);
for (i = 0; i < 32; i++) {
CSR_WRITE(l, SPR_CSR9, MII_MDO | MII_MDC);
DELAY(1);
CSR_WRITE(l, SPR_CSR9, MII_MDO);
DELAY(1);
}
CSR_WRITE(l, SPR_CSR9, 0);
v = 0; /* 4OP + 5ADDR + 5REG + 2TA + 16DATA */
for (i = (1 << 31); i != 0; i >>= 1) {
if (data & i)
v |= MII_MDO;
else
v &= ~MII_MDO;
CSR_WRITE(l, SPR_CSR9, v);
DELAY(1);
CSR_WRITE(l, SPR_CSR9, v | MII_MDC);
DELAY(1);
CSR_WRITE(l, SPR_CSR9, v);
DELAY(1);
}
CSR_WRITE(l, SPR_CSR9, 0);
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
static void
mii_initphy(struct local *l)
{
int phy, ctl, sts, bound;
for (phy = 0; phy < 32; phy++) {
ctl = mii_read(l, phy, MII_BMCR);
sts = mii_read(l, phy, MII_BMSR);
if (ctl != 0xffff && sts != 0xffff)
goto found;
}
printf("MII: no PHY found\n");
return;
found:
ctl = mii_read(l, phy, MII_BMCR);
mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
bound = 100;
do {
DELAY(10);
ctl = 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;
mii_write(l, phy, MII_BMCR, ctl);
sts = mii_read(l, phy, MII_BMSR) |
mii_read(l, phy, MII_BMSR); /* read twice */
l->phy = phy;
l->bmsr = sts;
}
static void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: vge.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_WRITE_1(l, r, v) *(volatile uint8_t *)((l)->csr+(r)) = (v)
#define CSR_READ_1(l, r) *(volatile uint8_t *)((l)->csr+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->csr+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
#define CSR_READ_4(l, r) in32rb((l)->csr+(r))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct tdesc {
uint32_t t0, t1;
struct {
uint32_t lo;
uint32_t hi;
} tf[7];
};
struct rdesc {
uint32_t r0, r1, r2, r3;
};
#define T0_OWN (1U << 31) /* 1: loaded for HW to send */
#define T0_TERR (1U << 15) /* Tx error summary */
#define T0_UDF (1U << 12) /* found link down when Tx */
#define T0_SHDN (1U << 10) /* transfer was shutdowned */
#define T0_CRS (1U << 9) /* found carrier sense lost */
#define T0_CDH (1U << 8) /* heartbeat check failure */
#define T0_ABT (1U << 7) /* excessive collision Tx abort */
#define T0_OWT (1U << 6) /* jumbo Tx frame was aborted */
#define T0_OWC (1U << 5) /* found out of window collision */
#define T0_COLS (1U << 4) /* collision detected */
#define T0_NCRMASK 0xf /* number of collision retries */
#define T1_EOF (1U << 25) /* TCP large last segment */
#define T1_SOF (1U << 24) /* TCP large first segment */
#define T1_TIC (1U << 13) /* post Tx done interrupt */
#define T1_PIC (1U << 22) /* post priority interrupt */
#define T1_VTAG (1U << 21) /* insert VLAG tag */
#define T1_IPCK (1U << 20) /* generate IPv4 csum */
#define T1_UDPCK (1U << 19) /* generate UDPv4 csum */
#define T1_TCPCK (1U << 18) /* generate TCPv4 csum */
#define T1_JUMBO (1U << 17) /* jumbo frame */
#define T1_CRC (1U << 16) /* _disable_ CRC generation */
#define T1_PRIO 0x0000e000 /* VLAN priority value */
#define T1_CFI (1U << 12) /* VLAN CFI */
#define T1_VID 0x00000fff /* VLAN ID 11:0 */
#define T_FLMASK 0x00003fff /* Tx frame/segment length */
#define TF0_Q (1U << 31) /* "Q" bit of tf[0].hi */
#define R0_OWN (1U << 31) /* 1: empty for HW to load anew */
#define R0_FLMASK 0x3fff0000 /* frame length */
#define R0_RXOK (1U << 15)
#define R0_MAR (1U << 13) /* multicast frame */
#define R0_BAR (1U << 12) /* broadcast frame */
#define R0_PHY (1U << 11) /* unicast frame */
#define R0_VTAG (1U << 10) /* VTAG indicator */
#define R0_STP (1U << 9) /* first frame segment */
#define R0_EDP (1U << 8) /* last frame segment */
#define R0_DETAG (1U << 7) /* VTAG has removed */
#define R0_SNTAG (1U << 6) /* tagged SNAP frame */
#define R0_SYME (1U << 5) /* symbol error */
#define R0_LENE (1U << 4) /* frame length error */
#define R0_CSUME (1U << 3) /* TCP/IP bad csum */
#define R0_FAE (1U << 2) /* frame alignment error */
#define R0_CRCE (1U << 1) /* CRC error */
#define R0_VIDM (1U << 0) /* VTAG filter miss */
#define R1_IPOK (1U << 22) /* IP csum was fine */
#define R1_TUPOK (1U << 21) /* TCP/UDP csum was fine */
#define R1_FRAG (1U << 20) /* fragmented IP */
#define R1_CKSMZO (1U << 19) /* UDP csum field was zero */
#define R1_IPKT (1U << 18) /* frame was IPv4 */
#define R1_TPKT (1U << 17) /* frame was TCPv4 */
#define R1_UPKT (1U << 16) /* frame was UDPv4 */
#define R3_IC (1U << 31) /* post Rx interrupt */
#define R_FLMASK 0x00003ffd /* Rx segment buffer length */
#define VR_PAR0 0x00 /* SA [0] */
#define VR_PAR1 0x01 /* SA [1] */
#define VR_PAR2 0x02 /* SA [2] */
#define VR_PAR3 0x03 /* SA [3] */
#define VR_PAR4 0x04 /* SA [4] */
#define VR_PAR5 0x05 /* SA [5] */
#define VR_CAM0 0x10 /* 0..7 */
#define VR_RCR 0x06 /* Rx control */
#define RCR_AP (1U << 6) /* accept unicast frame */
#define RCR_AL (1U << 5) /* accept long VTAG frame */
#define RCR_PROM (1U << 4) /* accept any frame */
#define RCR_AB (1U << 3) /* accept broadcast frame */
#define RCR_AM (1U << 2) /* use multicast filter */
#define VR_TCR 0x07 /* Tx control */
#define VR_CTL0 0x08 /* control #0 */
#define CTL0_TXON (1U << 3) /* enable Tx DMA */
#define CTL0_RXON (1U << 2) /* enable Rx DMA */
#define CTL0_STOP (1U << 1) /* activate stop processing */
#define CTL0_START (1U << 0) /* start and activate */
#define VR_CTL1 0x09 /* control #1 */
#define CTL1_RESET (1U << 7)
#define CTL1_DPOLL (1U << 3) /* _disable_ TDES/RDES polling */
#define VR_CTL2 0x0a /* control #2 */
#define CTL2_3XFLC (1U << 7) /* 802.3x PAUSE flow control */
#define CTL2_TPAUSE (1U << 6) /* handle PAUSE on transmit side */
#define CTL2_RPAUSE (1U << 5) /* handle PAUSE on receive side */
#define CTL2_HDXFLC (1U << 4) /* HDX jabber flow control */
#define VR_CTL3 0x0b /* control #3 */
#define CTL3_GIEN (1U << 1) /* global interrupt enable */
#define VR_DESCHI 0x18 /* RDES/TDES base high 63:32 */
#define VR_DATAHI 0x1c /* frame data base high 63:48 */
#define VR_ISR 0x24 /* ISR0123 */
#define VR_IEN 0x28 /* IEN0123 */
#define VR_TDCSR 0x30
#define VR_RDCSR 0x32
#define VR_RDB 0x38 /* RDES base lo 31:0 */
#define VR_TDB0 0x40 /* #0 TDES base lo 31:0 */
#define VR_RDCSIZE 0x50 /* 0..255 */
#define VR_TDCSIZE 0x52 /* 0..4095 */
#define VR_RBRDU 0x5e /* 0..255 */
#define VR_CAMADR 0x68
#define CAM_EN (1U << 7) /* enable to manipulate */
#define SADR_CAM (0U << 6) /* station address table */
#define VTAG_CAM (1U << 6) /* VLAN tag table */
#define VR_CAMCTL 0x69
#define CAMCTL_MULT (00U << 6) /* multicast address hash */
#define CAMCTL_VBIT (01U << 6) /* valid bitmask */
#define CAMCTL_ADDR (02U << 6) /* address data */
#define CAMCTL_RD (1U << 3) /* CAM read op, auto cleared */
#define CAMCTL_WR (1U << 2) /* CAM write op, auto cleared */
#define VR_MIICFG 0x6c /* PHY number 4:0 */
#define VR_MIISR 0x6d /* MII status */
#define MIISR_MIDLE (1U << 7) /* not in auto polling */
#define VR_PHYSR0 0x6e /* PHY status 0 */
#define VR_MIICR 0x70 /* MII control */
#define MIICR_MAUTO (1U << 7) /* activate autopoll mode */
#define MIICR_RCMD (1U << 6) /* MII read operation */
#define MIICR_WCMD (1U << 5) /* MII write operation */
#define VR_MIIADR 0x71 /* MII indirect */
#define VR_MIIDATA 0x72 /* MII read/write */
#define FRAMESIZE 1536
#define NRXDESC 4 /* HW demands multiple of 4 */
struct local {
struct tdesc txd;
struct rdesc rxd[NRXDESC];
uint8_t rxstore[NRXDESC][FRAMESIZE];
unsigned csr, rx;
unsigned phy, bmsr, anlpar;
};
static void mii_autopoll(struct local *);
static void mii_stoppoll(struct local *);
static int mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_dealan(struct local *, unsigned);
int
vge_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x1106, 0x3119):
return 1;
}
return 0;
}
void *
vge_init(unsigned tag, void *data)
{
unsigned val, i, fdx, loop;
struct local *l;
struct tdesc *txd;
struct rdesc *rxd;
uint8_t *en;
l = ALLOC(struct local, 64); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = DEVTOV(pcicfgread(tag, 0x14)); /* use mem space */
val = CTL1_RESET;
CSR_WRITE_1(l, VR_CTL1, val);
do {
val = CSR_READ_1(l, VR_CTL1);
} while (val & CTL1_RESET);
l->phy = CSR_READ_1(l, VR_MIICFG) & 0x1f;
en = data;
en[0] = CSR_READ_1(l, VR_PAR0);
en[1] = CSR_READ_1(l, VR_PAR1);
en[2] = CSR_READ_1(l, VR_PAR2);
en[3] = CSR_READ_1(l, VR_PAR3);
en[4] = CSR_READ_1(l, VR_PAR4);
en[5] = CSR_READ_1(l, VR_PAR5);
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
mii_dealan(l, 5);
/* speed and duplexity can be seen in MII 28 */
val = mii_read(l, l->phy, 28);
fdx = (val >> 5) & 01;
switch ((val >> 3) & 03) {
case 0: printf("10baseT"); break;
case 1: printf("100baseTX"); break;
case 2: printf("1000baseT"); break;
}
if (fdx)
printf("-FDX");
printf("\n");
txd = &l->txd;
rxd = &l->rxd[0];
for (i = 0; i < NRXDESC; i++) {
rxd[i].r0 = htole32(R0_OWN);
rxd[i].r1 = 0;
rxd[i].r2 = htole32(VTOPHYS(l->rxstore[i]));
rxd[i].r3 = htole32(FRAMESIZE << 16);
}
wbinv(l, sizeof(struct local));
l->rx = 0;
/* set own station address into entry #0 */
CSR_WRITE_1(l, VR_CAMCTL, CAMCTL_ADDR);
CSR_WRITE_1(l, VR_CAMADR, CAM_EN | SADR_CAM | 0);
for (i = 0; i < 6; i++)
CSR_WRITE_1(l, VR_CAM0 + i, en[i]);
CSR_WRITE_1(l, VR_CAMCTL, CAMCTL_ADDR | CAMCTL_WR);
loop = 20;
while (--loop > 0 && (i = CSR_READ_1(l, VR_CAMCTL)) & CAMCTL_WR)
DELAY(1);
/* mark entry #0 valid, position 0 of 63:0 */
CSR_WRITE_1(l, VR_CAMCTL, CAMCTL_VBIT);
CSR_WRITE_1(l, VR_CAM0, 01);
for (i = 1; i < 8; i++)
CSR_WRITE_1(l, VR_CAM0 + i, 00);
CSR_WRITE_1(l, VR_CAMADR, 0);
CSR_WRITE_1(l, VR_CAMCTL, 0);
/* prepare descriptor lists */
CSR_WRITE_4(l, VR_RDB, VTOPHYS(rxd));
CSR_WRITE_2(l, VR_RDCSIZE, NRXDESC - 1);
CSR_WRITE_2(l, VR_RBRDU, NRXDESC - 1);
CSR_WRITE_4(l, VR_TDB0, VTOPHYS(txd));
CSR_WRITE_2(l, VR_TDCSIZE, 0);
/* enable transmitter and receiver */
CSR_WRITE_1(l, VR_RDCSR, 01);
CSR_WRITE_1(l, VR_RDCSR, 04);
CSR_WRITE_2(l, VR_TDCSR, 01);
CSR_WRITE_1(l, VR_RCR, RCR_AP);
CSR_WRITE_1(l, VR_TCR, 0);
CSR_WRITE_1(l, VR_CTL0 + 0x4, CTL0_STOP);
CSR_WRITE_1(l, VR_CTL0, CTL0_TXON | CTL0_RXON | CTL0_START);
CSR_WRITE_4(l, VR_ISR, ~0);
CSR_WRITE_4(l, VR_IEN, 0);
return l;
}
int
vge_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct tdesc *txd;
unsigned loop;
len = (len & T_FLMASK);
if (len < 60)
len = 60; /* needs to stretch to ETHER_MIN_LEN - 4 */
wbinv(buf, len);
txd = &l->txd;
txd->tf[0].lo = htole32(VTOPHYS(buf));
txd->tf[0].hi = htole32(len << 16);
txd->t1 = htole32(T1_SOF | T1_EOF | (2 << 28));
txd->t0 = htole32(T0_OWN | len << 16);
wbinv(txd, sizeof(struct tdesc));
CSR_WRITE_2(l, VR_TDCSR, 04);
loop = 100;
do {
if ((le32toh(txd->t0) & T0_OWN) == 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct tdesc));
} while (--loop > 0);
printf("xmit failed\n");
return -1;
done:
return len;
}
int
vge_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct rdesc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
again:
rxd = &l->rxd[l->rx];
do {
inv(rxd, sizeof(struct rdesc));
rxstat = le32toh(rxd->r0);
if ((rxstat & R0_OWN) == 0)
goto gotone;
DELAY(1000); /* 1 milli second */
} while (--bound > 0);
errno = 0;
return -1;
gotone:
if ((rxstat & R0_RXOK) == 0) {
rxd->r0 = htole32(R0_OWN);
rxd->r1 = 0;
wbinv(rxd, sizeof(struct rdesc));
l->rx ^= 1;
goto again;
}
len = ((rxstat & R0_FLMASK) >> 16) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
if ((l->rx & 03) == 3) {
/* needs to set R0_OWN to 4 descriptors at a time */
rxd[00].r0 = htole32(R0_OWN);
rxd[00].r1 = 0;
rxd[-1].r0 = htole32(R0_OWN);
rxd[-1].r1 = 0;
rxd[-2].r0 = htole32(R0_OWN);
rxd[-2].r1 = 0;
rxd[-3].r0 = htole32(R0_OWN);
rxd[-3].r1 = 0;
wbinv(rxd, NRXDESC * sizeof(struct rdesc));
}
l->rx = (l->rx + 1) & (NRXDESC - 1);
return len;
}
static void
mii_autopoll(struct local *l)
{
int v;
CSR_WRITE_1(l, VR_MIICR, 0);
CSR_WRITE_1(l, VR_MIIADR, 1U << 7);
do {
DELAY(1);
v = CSR_READ_1(l, VR_MIISR);
} while ((v & MIISR_MIDLE) == 0);
CSR_WRITE_1(l, VR_MIICR, MIICR_MAUTO);
do {
DELAY(1);
v = CSR_READ_1(l, VR_MIISR);
} while ((v & MIISR_MIDLE) != 0);
}
static void
mii_stoppoll(struct local *l)
{
int v;
CSR_WRITE_1(l, VR_MIICR, 0);
do {
DELAY(1);
v = CSR_READ_1(l, VR_MIISR);
} while ((v & MIISR_MIDLE) == 0);
}
static int
mii_read(struct local *l, int phy, int reg)
{
int v;
mii_stoppoll(l);
CSR_WRITE_1(l, VR_MIICFG, phy);
CSR_WRITE_1(l, VR_MIIADR, reg);
CSR_WRITE_1(l, VR_MIICR, MIICR_RCMD);
do {
v = CSR_READ_1(l, VR_MIICR);
} while (v & MIICR_RCMD);
v = CSR_READ_2(l, VR_MIIDATA);
mii_autopoll(l);
return v;
}
static void
mii_write(struct local *l, int phy, int reg, int data)
{
int v;
mii_stoppoll(l);
CSR_WRITE_2(l, VR_MIIDATA, data);
CSR_WRITE_1(l, VR_MIICFG, phy);
CSR_WRITE_1(l, VR_MIIADR, reg);
CSR_WRITE_1(l, VR_MIICR, MIICR_WCMD);
do {
v = CSR_READ_1(l, VR_MIICR);
} while (v & MIICR_WCMD);
mii_autopoll(l);
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
#define MII_GTCR 0x09 /* 1000baseT control */
#define GANA_1000TFDX 0x0200 /* advertise 1000baseT FDX */
#define GANA_1000THDX 0x0100 /* advertise 1000baseT HDX */
#define MII_GTSR 0x0a /* 1000baseT status */
#define GLPA_1000TFDX 0x0800 /* link partner 1000baseT FDX capable */
#define GLPA_1000THDX 0x0400 /* link partner 1000baseT HDX capable */
#define GLPA_ASM_DIR 0x0200 /* link partner asym. pause dir. capable */
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, gtcr, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
anar |= ANAR_FC;
gtcr = GANA_1000TFDX | GANA_1000THDX;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_GTCR, gtcr);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}
/* $NetBSD: wm.c,v 1.1 2011/01/23 01:05:30 nisimura Exp $ */
/*-
* 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.
*
* 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <dev/pci/if_wmreg.h>
#include "globals.h"
/*
* - reverse endian access every CSR.
* - no vtophys() translation, vaddr_t == paddr_t.
* - PIPT writeback cache aware.
*/
#define CSR_READ(l, r) in32rb((l)->csr+(r))
#define CSR_WRITE(l, r, v) out32rb((l)->csr+(r), (v))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct tdesc {
uint32_t lo; /* 31:0 */
uint32_t hi; /* 63:32 */
uint32_t t2; /* 31:16 command, 15:0 Tx frame length */
uint32_t t3; /* 31:16 VTAG, 15:8 opt, 7:0 Tx status */
};
struct rdesc {
uint32_t lo; /* 31:0 */
uint32_t hi; /* 63:32 */
uint32_t r2; /* 31:16 checksum, 15:0 Rx frame length */
uint32_t r3; /* 31:16 special, 15:8 errors, 7:0 status */
};
/* T2 command */
#define T2_FLMASK 0xffff /* 15:0 */
#define T2_DTYP_C (1U << 20) /* data descriptor */
#define T2_EOP (1U << 24) /* end of packet */
#define T2_IFCS (1U << 25) /* insert FCS */
#define T2_RS (1U << 27) /* report status */
#define T2_RPS (1U << 28) /* report packet sent */
#define T2_DEXT (1U << 29) /* descriptor extention */
#define T2_VLE (1U << 30) /* VLAN enable */
#define T2_IDE (1U << 31) /* interrupt delay enable */
/* T3 status */
#define T3_DD (1U << 0) /* 1: Tx has done and vacant */
/* T3 option */
#define T3_IXSM (1U << 16) /* generate IP csum */
#define T3_TXSM (1U << 17) /* generate TCP/UDP csum */
#define R2_FLMASK 0xffff /* 15:0 */
/* R3 status */
#define R3_DD (1U << 0) /* 1: Rx frame loaded and available */
#define R3_EOP (1U << 1) /* end of packet */
#define R3_IXSM (1U << 2) /* ignore checksum indication */
#define R3_VP (1U << 3) /* VLAN packet */
#define R3_TCPCS (1U << 5) /* TCP csum performed */
#define R3_IPCS (1U << 6) /* IP csum performed */
#define R3_PIF (1U << 7) /* passed in-exact filter */
/* R3 error status */
#define R3_CE (1U << 8) /* CRC error */
#define R3_SE (1U << 9) /* symbol error */
#define R3_SEQ (1U << 10) /* sequence error */
#define R3_CXE (1U << 12) /* carrier extention error */
#define R3_TCPE (1U << 13) /* TCP csum error found */
#define R3_IPE (1U << 14) /* IP csum error found */
#define R3_RXE (1U << 15) /* Rx data error */
#define FRAMESIZE 1536
struct local {
struct tdesc txd[2];
struct rdesc rxd[2];
uint8_t rxstore[2][FRAMESIZE];
unsigned csr, tx, rx;
unsigned ctl, tctl, rctl;
unsigned phy, bmsr, anlpar;
int sromsft;
};
static int read_srom(struct local *, int);
static unsigned mii_read(struct local *, int, int);
static void mii_write(struct local *, int, int, int);
static void mii_initphy(struct local *);
static void mii_dealan(struct local *, unsigned);
int
wm_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x8086, 0x107c):
return 1;
}
return 0;
}
void *
wm_init(unsigned tag, void *data)
{
unsigned val, fdx;
struct local *l;
struct tdesc *txd;
struct rdesc *rxd;
uint8_t *en;
l = ALLOC(struct local, 32); /* desc alignment */
memset(l, 0, sizeof(struct local));
l->csr = pcicfgread(tag, 0x10); /* use mem space */
CSR_WRITE(l, WMREG_TCTL, 0);
CSR_WRITE(l, WMREG_RCTL, 0);
mii_initphy(l);
l->sromsft = 6;
en = data;
val = read_srom(l, 0); en[0] = val; en[1] = (val >> 8);
val = read_srom(l, 1); en[2] = val; en[3] = (val >> 8);
val = read_srom(l, 2); en[4] = val; en[5] = (val >> 8);
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
en[0], en[1], en[2], en[3], en[4], en[5]);
printf("PHY %d (%04x.%04x)\n", l->phy,
mii_read(l, l->phy, 2), mii_read(l, l->phy, 3));
mii_dealan(l, 5);
/* speed and duplexity are found at 82451 internal GPHY reg 17 */
val = mii_read(l, l->phy, 0x11);
fdx = !!(val & 0x0200);
switch (val & 0xc000) {
case 0x4000: printf("10Mbps"); break;
case 0x8000: printf("100Mbps"); break;
case 0xc000: printf("1000Mbps"); break;
}
if (fdx)
printf("-FDX");
printf("\n");
txd = &l->txd[0];
rxd = &l->rxd[0];
rxd[0].lo = htole32(VTOPHYS(l->rxstore[0]));
rxd[0].r2 = 0;
rxd[0].r3 = 0;
rxd[1].lo = htole32(VTOPHYS(l->rxstore[1]));
rxd[1].r2 = 0;
rxd[0].r3 = 0;
l->tx = l->rx = 0;
CSR_WRITE(l, WMREG_TDBAH, 0);
CSR_WRITE(l, WMREG_TDBAL, VTOPHYS(txd));
CSR_WRITE(l, WMREG_TDLEN, sizeof(l->txd));
CSR_WRITE(l, WMREG_TDH, 0);
CSR_WRITE(l, WMREG_TDT, 0);
CSR_WRITE(l, WMREG_TIDV, 64);
CSR_WRITE(l, WMREG_TADV, 128);
CSR_WRITE(l, WMREG_TXDCTL, TXDCTL_PTHRESH(0) |
TXDCTL_HTHRESH(0) | TXDCTL_WTHRESH(0));
CSR_WRITE(l, WMREG_TQSA_LO, 0);
CSR_WRITE(l, WMREG_TQSA_HI, 0);
CSR_WRITE(l, WMREG_RDBAH, 0);
CSR_WRITE(l, WMREG_RDBAL, VTOPHYS(rxd));
CSR_WRITE(l, WMREG_RDLEN, sizeof(l->rxd));
CSR_WRITE(l, WMREG_RDH, 0);
CSR_WRITE(l, WMREG_RDT, 0);
CSR_WRITE(l, WMREG_RDTR, 0 | RDTR_FPD);
CSR_WRITE(l, WMREG_RADV, 128);
CSR_WRITE(l, WMREG_RXDCTL, RXDCTL_PTHRESH(0) |
RXDCTL_HTHRESH(0) | RXDCTL_WTHRESH(1));
CSR_WRITE(l, WMREG_VET, 0);
CSR_WRITE(l, WMREG_IMC, ~0);
CSR_WRITE(l, WMREG_IMS, 0);
l->tctl = TCTL_EN | TCTL_PSP | TCTL_CT(15);
l->rctl = RCTL_EN | RCTL_LBM_NONE | RCTL_RDMTS_1_2;
CSR_WRITE(l, WMREG_TCTL, l->tctl);
CSR_WRITE(l, WMREG_RCTL, l->rctl);
return l;
}
int
wm_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
volatile struct tdesc *txd;
unsigned loop;
wbinv(buf, len);
txd = &l->txd[l->tx];
txd->lo = htole32(VTOPHYS(buf));
txd->t2 = htole32(T2_EOP|T2_IFCS|T2_RS | (len & T2_FLMASK));
txd->t3 = 0;
wbinv(txd, sizeof(struct tdesc));
CSR_WRITE(l, WMREG_TDT, 0);
loop = 100;
do {
if ((le32toh(txd->t3) & T3_DD) != 0)
goto done;
DELAY(10);
inv(txd, sizeof(struct tdesc));
} while (--loop > 0);
printf("xmit failed\n");
return -1;
done:
l->tx ^= 1;
return len;
}
int
wm_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
volatile struct rdesc *rxd;
unsigned bound, rxstat, len;
uint8_t *ptr;
bound = 1000 * timo;
printf("recving with %u sec. timeout\n", timo);
again:
rxd = &l->rxd[l->rx];
do {
inv(rxd, sizeof(struct rdesc));
rxstat = le32toh(rxd->r3);
if ((rxstat & R3_DD) != 0)
goto gotone;
DELAY(1000); /* 1 milli second */
} while (--bound > 0);
errno = 0;
return -1;
gotone:
/* expect this has R3_EOP mark */
if (rxstat & (R3_CE|R3_SE|R3_SEQ|R3_CXE|R3_RXE)) {
rxd->r2 = 0;
rxd->r3 = 0;
wbinv(rxd, sizeof(struct rdesc));
CSR_WRITE(l, WMREG_RDT, l->rx);
l->rx ^= 1;
goto again;
}
len = (rxstat & R2_FLMASK) - 4 /* HASFCS */;
if (len > maxlen)
len = maxlen;
ptr = l->rxstore[l->rx];
inv(ptr, len);
memcpy(buf, ptr, len);
rxd->r2 = 0;
rxd->r3 = 0;
wbinv(rxd, sizeof(struct rdesc));
CSR_WRITE(l, WMREG_RDT, l->rx);
l->rx ^= 1;
return len;
}
/*
* 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 VV (1U << 3) /* taken 0/1 from SEEPROM */
static int
read_srom(struct local *l, int off)
{
unsigned data, v, i;
data = off & 0xff; /* A5/A7-A0 */
data |= R110 << l->sromsft; /* 110 for READ */
v = CSR_READ(l, WMREG_EECD) & ~(EECD_SK | EECD_DI);
CSR_WRITE(l, WMREG_EECD, v);
v |= EECD_CS; /* hold CS */
CSR_WRITE(l, WMREG_EECD, v);
DELAY(2);
/* instruct R110 op. at off in MSB first order */
for (i = (1 << (l->sromsft + 2)); i != 0; i >>= 1) {
if (data & i)
v |= EECD_DI;
else
v &= ~EECD_DI;
CSR_WRITE(l, WMREG_EECD, v);
DELAY(2);
CSR_WRITE(l, WMREG_EECD, v | EECD_SK);
DELAY(2);
CSR_WRITE(l, WMREG_EECD, v);
DELAY(2);
}
v &= ~EECD_DI;
/* read 16bit quantity in MSB first order */
data = 0;
for (i = 0; i < 16; i++) {
CSR_WRITE(l, WMREG_EECD, v | EECD_SK);
DELAY(2);
data = (data << 1) | !!(CSR_READ(l, WMREG_EECD) & EECD_DO);
CSR_WRITE(l, WMREG_EECD, v);
DELAY(2);
}
/* turn off chip select */
v = CSR_READ(l, WMREG_EECD) & ~EECD_CS;
CSR_WRITE(l, WMREG_EECD, v);
DELAY(2);
return data;
}
#define MREG(v) ((v)<< 16)
#define MPHY(v) ((v)<< 21)
unsigned
mii_read(struct local *l, int phy, int reg)
{
unsigned data;
data = (2U << 26) | MPHY(phy) | MREG(reg);
CSR_WRITE(l, WMREG_MDIC, data);
do {
data = CSR_READ(l, WMREG_MDIC);
} while ((data & (1U << 28)) == 0);
return data & 0xffff;
}
void
mii_write(struct local *l, int phy, int reg, int val)
{
unsigned data;
data = (1U << 26) | MPHY(phy) | MREG(reg) | (val & 0xffff);
CSR_WRITE(l, WMREG_MDIC, data);
do {
data = CSR_READ(l, WMREG_MDIC);
} while ((data & (1U << 28)) == 0);
}
#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) */
#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
#define BMSR_LINK 0x0004 /* Link status */
#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
#define ANAR_FC 0x0400 /* local device supports PAUSE */
#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
#define ANAR_TX 0x0080 /* local device supports 100bTx */
#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
#define ANAR_10 0x0020 /* local device supports 10bT */
#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
#define MII_GTCR 0x09 /* 1000baseT control */
#define GANA_1000TFDX 0x0200 /* advertise 1000baseT FDX */
#define GANA_1000THDX 0x0100 /* advertise 1000baseT HDX */
#define MII_GTSR 0x0a /* 1000baseT status */
#define GLPA_1000TFDX 0x0800 /* link partner 1000baseT FDX capable */
#define GLPA_1000THDX 0x0400 /* link partner 1000baseT HDX capable */
#define GLPA_ASM_DIR 0x0200 /* link partner asym. pause dir. capable */
static void
mii_initphy(struct local *l)
{
int phy, ctl, sts, bound;
for (phy = 0; phy < 32; phy++) {
ctl = mii_read(l, phy, MII_BMCR);
sts = mii_read(l, phy, MII_BMSR);
if (ctl != 0xffff && sts != 0xffff)
goto found;
}
printf("MII: no PHY found\n");
return;
found:
ctl = mii_read(l, phy, MII_BMCR);
mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
bound = 100;
do {
DELAY(10);
ctl = 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;
mii_write(l, phy, MII_BMCR, ctl);
sts = mii_read(l, phy, MII_BMSR) |
mii_read(l, phy, MII_BMSR); /* read twice */
l->phy = phy;
l->bmsr = sts;
}
void
mii_dealan(struct local *l, unsigned timo)
{
unsigned anar, gtcr, bound;
anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
anar |= ANAR_FC;
gtcr = GANA_1000TFDX | GANA_1000THDX;
mii_write(l, l->phy, MII_ANAR, anar);
mii_write(l, l->phy, MII_GTCR, gtcr);
mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
l->anlpar = 0;
bound = getsecs() + timo;
do {
l->bmsr = mii_read(l, l->phy, MII_BMSR) |
mii_read(l, l->phy, MII_BMSR); /* read twice */
if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
break;
}
DELAY(10 * 1000);
} while (getsecs() < bound);
return;
}