NetBSD/sys/arch/mvme68k/stand/sboot/le_poll.c

324 lines
8.5 KiB
C

/* $NetBSD: le_poll.c,v 1.5 2008/01/12 09:54:33 tsutsui Exp $ */
/*
* Copyright (c) 1993 Adam Glass
* 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, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Adam Glass.
* 4. The name of the Author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 REGENTS 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 "sboot.h"
#include "if_lereg.h"
struct {
struct lereg1 *sc_r1; /* LANCE registers */
struct lereg2 *sc_r2; /* RAM */
int next_rmd;
int next_tmd;
} le_softc;
static void le_error(const char *, struct lereg1 *);
static void le_reset(u_char *);
static int le_poll(void *, int);
static void
le_error(const char *str, struct lereg1 *ler1)
{
/* ler1->ler1_rap = LE_CSRO done in caller */
if (ler1->ler1_rdp & LE_C0_BABL) {
printf("le0: been babbling, found by '%s'\n", str);
callrom();
}
if (ler1->ler1_rdp & LE_C0_CERR) {
ler1->ler1_rdp = LE_C0_CERR;
}
if (ler1->ler1_rdp & LE_C0_MISS) {
ler1->ler1_rdp = LE_C0_MISS;
}
if (ler1->ler1_rdp & LE_C0_MERR) {
printf("le0: memory error in '%s'\n", str);
callrom();
}
}
static void
le_reset(u_char *myea)
{
struct lereg1 *ler1 = le_softc.sc_r1;
struct lereg2 *ler2 = le_softc.sc_r2;
unsigned int a;
int timo = 100000, stat = 0, i;
ler1->ler1_rap = LE_CSR0;
ler1->ler1_rdp = LE_C0_STOP; /* do nothing until we are finished */
memset(ler2, 0, sizeof(*ler2));
ler2->ler2_mode = LE_MODE_NORMAL;
ler2->ler2_padr[0] = myea[1];
ler2->ler2_padr[1] = myea[0];
ler2->ler2_padr[2] = myea[3];
ler2->ler2_padr[3] = myea[2];
ler2->ler2_padr[4] = myea[5];
ler2->ler2_padr[5] = myea[4];
ler2->ler2_ladrf0 = 0;
ler2->ler2_ladrf1 = 0;
a = (u_int)ler2->ler2_rmd;
ler2->ler2_rlen = LE_RLEN | (a >> 16);
ler2->ler2_rdra = a & LE_ADDR_LOW_MASK;
a = (u_int)ler2->ler2_tmd;
ler2->ler2_tlen = LE_TLEN | (a >> 16);
ler2->ler2_tdra = a & LE_ADDR_LOW_MASK;
ler1->ler1_rap = LE_CSR1;
a = (u_int)ler2;
ler1->ler1_rdp = a & LE_ADDR_LOW_MASK;
ler1->ler1_rap = LE_CSR2;
ler1->ler1_rdp = a >> 16;
for (i = 0; i < LERBUF; i++) {
a = (u_int)&ler2->ler2_rbuf[i];
ler2->ler2_rmd[i].rmd0 = a & LE_ADDR_LOW_MASK;
ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN;
ler2->ler2_rmd[i].rmd1_hadr = a >> 16;
ler2->ler2_rmd[i].rmd2 = -LEMTU;
ler2->ler2_rmd[i].rmd3 = 0;
}
for (i = 0; i < LETBUF; i++) {
a = (u_int)&ler2->ler2_tbuf[i];
ler2->ler2_tmd[i].tmd0 = a & LE_ADDR_LOW_MASK;
ler2->ler2_tmd[i].tmd1_bits = 0;
ler2->ler2_tmd[i].tmd1_hadr = a >> 16;
ler2->ler2_tmd[i].tmd2 = 0;
ler2->ler2_tmd[i].tmd3 = 0;
}
ler1->ler1_rap = LE_CSR3;
ler1->ler1_rdp = LE_C3_BSWP;
ler1->ler1_rap = LE_CSR0;
ler1->ler1_rdp = LE_C0_INIT;
do {
if (--timo == 0) {
printf("le0: init timeout, stat = 0x%x\n", stat);
break;
}
stat = ler1->ler1_rdp;
} while ((stat & LE_C0_IDON) == 0);
ler1->ler1_rdp = LE_C0_IDON;
le_softc.next_rmd = 0;
le_softc.next_tmd = 0;
ler1->ler1_rap = LE_CSR0;
ler1->ler1_rdp = LE_C0_STRT;
}
static int
le_poll(void *pkt, int len)
{
struct lereg1 *ler1 = le_softc.sc_r1;
struct lereg2 *ler2 = le_softc.sc_r2;
unsigned int a;
int length;
struct lermd *rmd;
ler1->ler1_rap = LE_CSR0;
if ((ler1->ler1_rdp & LE_C0_RINT) != 0)
ler1->ler1_rdp = LE_C0_RINT;
rmd = &ler2->ler2_rmd[le_softc.next_rmd];
if (rmd->rmd1_bits & LE_R1_OWN) {
return 0;
}
if (ler1->ler1_rdp & LE_C0_ERR)
le_error("le_poll", ler1);
if (rmd->rmd1_bits & LE_R1_ERR) {
printf("le0_poll: rmd status 0x%x\n", rmd->rmd1_bits);
length = 0;
goto cleanup;
}
if ((rmd->rmd1_bits & (LE_R1_STP|LE_R1_ENP)) != (LE_R1_STP|LE_R1_ENP)) {
printf("le_poll: chained packet\n");
callrom();
}
length = rmd->rmd3;
if (length >= LEMTU) {
length = 0;
printf("csr0 when bad things happen: %x\n", ler1->ler1_rdp);
callrom();
goto cleanup;
}
if (length == 0)
goto cleanup;
length -= 4;
if (length > 0)
memcpy(pkt, (char *)&ler2->ler2_rbuf[le_softc.next_rmd],
length);
cleanup:
a = (u_int)&ler2->ler2_rbuf[le_softc.next_rmd];
rmd->rmd0 = a & LE_ADDR_LOW_MASK;
rmd->rmd1_hadr = a >> 16;
rmd->rmd2 = -LEMTU;
le_softc.next_rmd =
(le_softc.next_rmd == (LERBUF - 1)) ? 0 : (le_softc.next_rmd + 1);
rmd->rmd1_bits = LE_R1_OWN;
return length;
}
int le_put(u_char *pkt, size_t len)
{
struct lereg1 *ler1 = le_softc.sc_r1;
struct lereg2 *ler2 = le_softc.sc_r2;
struct letmd *tmd;
int timo = 100000, stat = 0;
unsigned int a;
ler1->ler1_rap = LE_CSR0;
if (ler1->ler1_rdp & LE_C0_ERR)
le_error("le_put(way before xmit)", ler1);
tmd = &ler2->ler2_tmd[le_softc.next_tmd];
while (tmd->tmd1_bits & LE_T1_OWN) {
printf("le0: output buffer busy\n");
}
memcpy((char *)ler2->ler2_tbuf[le_softc.next_tmd], pkt, len);
if (len < 64)
tmd->tmd2 = -64;
else
tmd->tmd2 = -len;
tmd->tmd3 = 0;
if (ler1->ler1_rdp & LE_C0_ERR)
le_error("le_put(before xmit)", ler1);
tmd->tmd1_bits = LE_T1_STP | LE_T1_ENP | LE_T1_OWN;
a = (u_int)&ler2->ler2_tbuf[le_softc.next_tmd];
tmd->tmd0 = a & LE_ADDR_LOW_MASK;
tmd->tmd1_hadr = a >> 16;
ler1->ler1_rdp = LE_C0_TDMD;
if (ler1->ler1_rdp & LE_C0_ERR)
le_error("le_put(after xmit)", ler1);
do {
if (--timo == 0) {
printf("le0: transmit timeout, stat = 0x%x\n",
stat);
if (ler1->ler1_rdp & LE_C0_ERR)
le_error("le_put(timeout)", ler1);
break;
}
stat = ler1->ler1_rdp;
} while ((stat & LE_C0_TINT) == 0);
ler1->ler1_rdp = LE_C0_TINT;
if (ler1->ler1_rdp & LE_C0_ERR) {
if ((ler1->ler1_rdp &
(LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_MERR)) !=
LE_C0_CERR)
printf("le_put: xmit error, buf %d\n",
le_softc.next_tmd);
le_error("le_put(xmit error)", ler1);
}
le_softc.next_tmd = 0;
#if 0
(le_softc.next_tmd == (LETBUF - 1)) ? 0 : le_softc.next_tmd + 1;
#endif
if (tmd->tmd1_bits & LE_T1_ERR) {
printf("le0: transmit error, error = 0x%x\n",
tmd->tmd3);
return -1;
}
return len;
}
int le_get(u_char *pkt, size_t len, u_long timeout)
{
int cc;
int now, then;
int stopat = time() + timeout;
then = 0;
cc = 0;
while ((now = time()) < stopat && !cc) {
cc = le_poll(pkt, len);
if (then != now) {
#ifdef LE_DEBUG
printf("%d \r", stopat - now);
#endif
then = now;
}
if (cc && (pkt[0] != myea[0] || pkt[1] != myea[1] ||
pkt[2] != myea[2] || pkt[3] != myea[3] ||
pkt[4] != myea[4] || pkt[5] != myea[5])) {
cc = 0; /* ignore broadcast / multicast */
#ifdef LE_DEBUG
printf("reject (%d sec left)\n", stopat - now);
#endif
}
}
#ifdef LE_DEBUG
printf("\n");
#endif
return cc;
}
void le_init(void)
{
int *ea = (int *)LANCE_ADDR;
u_long *eram = (u_long *)ERAM_ADDR;
u_long e = *ea;
if ((e & 0x2fffff00) == 0x2fffff00) {
printf("ERROR: ethernet address not set! Use LSAD.\n");
callrom();
}
myea[0] = 0x08;
myea[1] = 0x00;
myea[2] = 0x3e;
e = e >> 8;
myea[5] = e & 0xff;
e = e >> 8;
myea[4] = e & 0xff;
e = e >> 8;
myea[3] = e;
printf("le0: ethernet address: %x:%x:%x:%x:%x:%x\n",
myea[0], myea[1], myea[2], myea[3], myea[4], myea[5]);
memset(&le_softc, 0, sizeof(le_softc));
le_softc.sc_r1 = (struct lereg1 *)LANCE_REG_ADDR;
le_softc.sc_r2 = (struct lereg2 *)(*eram - (1024*1024));
le_reset(myea);
}
void le_end(void)
{
struct lereg1 *ler1 = le_softc.sc_r1;
ler1->ler1_rap = LE_CSR0;
ler1->ler1_rdp = LE_C0_STOP;
}