376 lines
9.4 KiB
C
376 lines
9.4 KiB
C
/* $NetBSD: lance.c,v 1.3 2014/04/16 11:18:00 tsutsui Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2013 Izumi Tsutsui. 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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) 2004 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by UCHIYAMA Yasushi.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* LANCE driver for LUNA
|
|
* based on sys/arch/ews4800mips/stand/common/lance.c
|
|
*/
|
|
|
|
#include <lib/libsa/stand.h>
|
|
#include <lib/libkern/libkern.h>
|
|
|
|
#include <dev/ic/am7990reg.h>
|
|
#include <dev/ic/lancereg.h>
|
|
|
|
#include <luna68k/stand/boot/samachdep.h>
|
|
#include <luna68k/stand/boot/lance.h>
|
|
|
|
static void lance_setup(struct le_softc *);
|
|
static bool lance_set_initblock(struct le_softc *);
|
|
static bool lance_do_initialize(struct le_softc *);
|
|
|
|
#define NLE 1 /* XXX for now */
|
|
static struct le_softc lesc[NLE];
|
|
|
|
void *
|
|
lance_attach(int unit, void *reg, void *mem, uint8_t *eaddr)
|
|
{
|
|
struct le_softc *sc;
|
|
|
|
if (unit >= NLE) {
|
|
printf("%s: invalid unit number\n", __func__);
|
|
return NULL;
|
|
}
|
|
sc = &lesc[unit];
|
|
|
|
if (sc->sc_reg != NULL) {
|
|
printf("%s: unit %d is already attached\n", __func__, unit);
|
|
return NULL;
|
|
}
|
|
sc->sc_reg = reg;
|
|
sc->sc_mem = mem;
|
|
memcpy(sc->sc_enaddr, eaddr, 6);
|
|
|
|
return sc;
|
|
}
|
|
|
|
void *
|
|
lance_cookie(int unit)
|
|
{
|
|
struct le_softc *sc;
|
|
|
|
if (unit >= NLE)
|
|
return NULL;
|
|
|
|
sc = &lesc[unit];
|
|
|
|
if (sc->sc_reg == NULL)
|
|
return NULL;
|
|
|
|
return sc;
|
|
}
|
|
|
|
uint8_t *
|
|
lance_eaddr(void *cookie)
|
|
{
|
|
struct le_softc *sc = cookie;
|
|
|
|
if (sc == NULL || sc->sc_reg == NULL)
|
|
return NULL;
|
|
|
|
return sc->sc_enaddr;
|
|
}
|
|
|
|
bool
|
|
lance_init(void *cookie)
|
|
{
|
|
struct le_softc *sc = cookie;
|
|
|
|
lance_setup(sc);
|
|
|
|
if (!lance_set_initblock(sc))
|
|
return false;
|
|
|
|
if (!lance_do_initialize(sc))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int
|
|
lance_get(void *cookie, void *data, size_t maxlen)
|
|
{
|
|
struct le_softc *sc = cookie;
|
|
struct lereg *lereg = sc->sc_reg;
|
|
struct lemem *lemem = sc->sc_mem;
|
|
struct lermd_v *rmd;
|
|
uint16_t csr __unused;
|
|
int len = -1;
|
|
|
|
lereg->ler_rap = LE_CSR0;
|
|
if ((lereg->ler_rdp & LE_C0_RINT) != 0)
|
|
lereg->ler_rdp = LE_C0_RINT;
|
|
rmd = &lemem->lem_rmd[sc->sc_currmd];
|
|
if ((rmd->rmd1_bits & LE_R1_OWN) != 0)
|
|
return -1;
|
|
|
|
csr = lereg->ler_rdp;
|
|
#if 0
|
|
if ((csr & LE_C0_ERR) != 0)
|
|
printf("%s: RX poll error (CSR=0x%x)\n", __func__, csr);
|
|
#endif
|
|
if ((rmd->rmd1_bits & LE_R1_ERR) != 0) {
|
|
printf("%s: RX error (rmd status=0x%x)\n", __func__,
|
|
rmd->rmd1_bits);
|
|
goto out;
|
|
}
|
|
|
|
len = rmd->rmd3;
|
|
if (len < LEMINSIZE + 4 || len > LEMTU) {
|
|
printf("%s: RX error (bad length %d)\n", __func__, len);
|
|
goto out;
|
|
}
|
|
len -= 4;
|
|
memcpy(data, (void *)lemem->lem_rbuf[sc->sc_currmd], min(len, maxlen));
|
|
|
|
out:
|
|
rmd->rmd2 = -LEMTU;
|
|
rmd->rmd1_bits = LE_R1_OWN; /* return to LANCE */
|
|
sc->sc_currmd = LE_NEXTRMD(sc->sc_currmd);
|
|
|
|
return len;
|
|
}
|
|
|
|
bool
|
|
lance_put(void *cookie, void *data, size_t len)
|
|
{
|
|
struct le_softc *sc = cookie;
|
|
struct lereg *lereg = sc->sc_reg;
|
|
struct lemem *lemem = sc->sc_mem;
|
|
struct letmd_v *tmd;
|
|
uint16_t stat;
|
|
int timeout;
|
|
|
|
lereg->ler_rap = LE_CSR0;
|
|
stat = lereg->ler_rdp;
|
|
lereg->ler_rdp =
|
|
stat & (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_TINT);
|
|
#if 0
|
|
if (stat & (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_MERR))
|
|
printf("%s: TX error before xmit csr0=0x%x\n",
|
|
__func__, stat);
|
|
#endif
|
|
|
|
/* setup TX descriptor */
|
|
tmd = &lemem->lem_tmd[sc->sc_curtmd];
|
|
while (tmd->tmd1_bits & LE_T1_OWN)
|
|
continue;
|
|
tmd->tmd1_bits = LE_T1_STP | LE_T1_ENP;
|
|
memcpy((void *)lemem->lem_tbuf[sc->sc_curtmd], data, len);
|
|
tmd->tmd2 = -max(len, LEMINSIZE);
|
|
tmd->tmd3 = 0;
|
|
|
|
/* start TX */
|
|
tmd->tmd1_bits |= LE_T1_OWN;
|
|
lereg->ler_rap = LE_CSR0;
|
|
lereg->ler_rdp = LE_C0_TDMD;
|
|
|
|
/* check TX complete */
|
|
timeout = 0;
|
|
do {
|
|
lereg->ler_rap = LE_CSR0;
|
|
stat = lereg->ler_rdp;
|
|
#if 0
|
|
if (stat & LE_C0_ERR) {
|
|
printf("%s: TX error (CSR0=%x)\n", __func__, stat);
|
|
if (stat & LE_C0_CERR) {
|
|
lereg->ler_rdp = LE_C0_CERR;
|
|
}
|
|
}
|
|
#endif
|
|
if (timeout++ > 1000) {
|
|
printf("%s: TX timeout (CSR0=%x)\n", __func__, stat);
|
|
return false;
|
|
}
|
|
} while ((stat & LE_C0_TINT) == 0);
|
|
|
|
lereg->ler_rdp = LE_C0_TINT;
|
|
|
|
sc->sc_curtmd = LE_NEXTTMD(sc->sc_curtmd);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lance_end(void *cookie)
|
|
{
|
|
struct le_softc *sc = cookie;
|
|
struct lereg *lereg = sc->sc_reg;
|
|
|
|
lereg->ler_rap = LE_CSR0;
|
|
lereg->ler_rdp = LE_C0_STOP;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* XXX */
|
|
int
|
|
lance_intr(void)
|
|
{
|
|
|
|
return 1;
|
|
}
|
|
|
|
static bool
|
|
lance_set_initblock(struct le_softc *sc)
|
|
{
|
|
struct lereg *lereg = sc->sc_reg;
|
|
uint32_t addr = (uint32_t)sc->sc_mem;
|
|
|
|
lereg->ler_rap = LE_CSR0;
|
|
lereg->ler_rdp = LE_C0_STOP; /* disable all external activity */
|
|
DELAY(100);
|
|
|
|
/* Set the correct byte swapping mode */
|
|
lereg->ler_rap = LE_CSR3;
|
|
lereg->ler_rdp = LE_C3_BSWP;
|
|
|
|
/* Low address of init block */
|
|
lereg->ler_rap = LE_CSR1;
|
|
lereg->ler_rdp = addr & 0xfffe;
|
|
|
|
/* High address of init block */
|
|
lereg->ler_rap = LE_CSR2;
|
|
lereg->ler_rdp = (addr >> 16) & 0x00ff;
|
|
DELAY(100);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
lance_do_initialize(struct le_softc *sc)
|
|
{
|
|
struct lereg *lereg = sc->sc_reg;
|
|
uint16_t reg;
|
|
int timeout;
|
|
|
|
sc->sc_curtmd = 0;
|
|
sc->sc_currmd = 0;
|
|
|
|
/* Initialze LANCE */
|
|
lereg->ler_rap = LE_CSR0;
|
|
lereg->ler_rdp = LE_C0_INIT;
|
|
|
|
/* Wait interrupt */
|
|
timeout = 1000000;
|
|
do {
|
|
lereg->ler_rap = LE_CSR0;
|
|
reg = lereg->ler_rdp;
|
|
if (--timeout == 0) {
|
|
printf("le: init timeout (CSR=0x%x)\n", reg);
|
|
return false;
|
|
}
|
|
DELAY(1);
|
|
} while ((reg & LE_C0_IDON) == 0);
|
|
|
|
lereg->ler_rap = LE_CSR0;
|
|
lereg->ler_rdp = LE_C0_STRT | LE_C0_IDON;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
lance_setup(struct le_softc *sc)
|
|
{
|
|
struct lereg *lereg = sc->sc_reg;
|
|
struct lemem *lemem = sc->sc_mem;
|
|
uint32_t addr;
|
|
int i;
|
|
|
|
/* make sure to stop LANCE chip before setup memory */
|
|
lereg->ler_rap = LE_CSR0;
|
|
lereg->ler_rdp = LE_C0_STOP;
|
|
|
|
memset(lemem, 0, sizeof *lemem);
|
|
|
|
/* Init block */
|
|
lemem->lem_mode = LE_MODE_NORMAL;
|
|
lemem->lem_padr[0] = (sc->sc_enaddr[1] << 8) | sc->sc_enaddr[0];
|
|
lemem->lem_padr[1] = (sc->sc_enaddr[3] << 8) | sc->sc_enaddr[2];
|
|
lemem->lem_padr[2] = (sc->sc_enaddr[5] << 8) | sc->sc_enaddr[4];
|
|
/* Logical address filter */
|
|
for (i = 0; i < 4; i++)
|
|
lemem->lem_ladrf[i] = 0x0000;
|
|
|
|
/* Location of Rx descriptor ring */
|
|
addr = (uint32_t)lemem->lem_rmd;
|
|
lemem->lem_rdra = addr & 0xffff;
|
|
lemem->lem_rlen = LE_RLEN | ((addr >> 16) & 0xff);
|
|
|
|
/* Location of Tx descriptor ring */
|
|
addr = (uint32_t)lemem->lem_tmd;
|
|
lemem->lem_tdra = addr & 0xffff;
|
|
lemem->lem_tlen = LE_TLEN | ((addr >> 16) & 0xff);
|
|
|
|
/* Rx descriptor */
|
|
for (i = 0; i < LERBUF; i++) {
|
|
addr = (uint32_t)lemem->lem_rbuf[i];
|
|
lemem->lem_rmd[i].rmd0 = addr & 0xffff;
|
|
lemem->lem_rmd[i].rmd1_hadr = (addr >> 16) & 0xff;
|
|
lemem->lem_rmd[i].rmd1_bits = LE_R1_OWN;
|
|
lemem->lem_rmd[i].rmd2 = LE_XMD2_ONES | -LEMTU;
|
|
lemem->lem_rmd[i].rmd3 = 0;
|
|
}
|
|
|
|
/* Tx descriptor */
|
|
for (i = 0; i < LETBUF; i++) {
|
|
addr = (uint32_t)lemem->lem_tbuf[i];
|
|
lemem->lem_tmd[i].tmd0 = addr & 0xffff;
|
|
lemem->lem_tmd[i].tmd1_hadr = (addr >> 16) & 0xff;
|
|
lemem->lem_tmd[i].tmd1_bits = 0;
|
|
lemem->lem_tmd[i].tmd2 = LE_XMD2_ONES | 0;
|
|
lemem->lem_tmd[i].tmd3 = 0;
|
|
}
|
|
}
|