4.4BSD/usr/src/sys/news3400/if/if_lance.c

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Sony Corp. and Kazumasa Utashiro of Software Research Associates, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 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.
 *
 * from: $Hdr: if_lance.c,v 4.300 91/06/09 06:25:58 root Rel41 $ SONY
 *
 *	@(#)if_lance.c	8.1 (Berkeley) 6/11/93
 */

/*
 * if_lance: Am7990 LANCE driver
 *
 * This driver is available only for single CPU machine.
 */

#define	LANCE_LED

#include "en.h"

#if NEN > 0

#include <machine/adrsmap.h>

#include <sys/param.h>

#include <news3400/if/lancereg.h>
#include <news3400/if/if_lance.h>

#ifdef mips
#define	VOLATILE	volatile
#else
#define	VOLATILE
#endif

#ifdef LANCE_LED
#ifdef news3400
#define	LED_ON	{ \
	VOLATILE u_char *p = (u_char *)DEBUG_PORT; \
	*p = DP_WRITE | (*p & ~DP_LED2); \
}
#define	LED_OFF	{ \
	VOLATILE u_char *p = (u_char *)DEBUG_PORT; \
	*p = DP_WRITE | (*p | DP_LED2); \
}
#else /* news3400 */
#define	LED_ON
#define	LED_OFF
#endif /* news3400 */
#else /* LANCE_LED */
#define	LED_ON
#define	LED_OFF
#endif /* LANCE_LED */

/*
 *	LANCE memory configuration
 */
#define	INIT_BLOCK		0x000000
#define	RECV_MSG_DESC		0x000100
#define	XMIT_MSG_DESC		0x000200
#ifdef mips
#define	RECV_BUFFER		(0x000300 + 2)
				/* for data alignment to long word */
#else /* mips */
#define	RECV_BUFFER		0x000300
#endif /* mips */
#define	XMIT_BUFFER		(RECV_BUFFER+(RECV_BUFFER_SIZE*RECV_BUFFERS))

#define	RECV_RING_LEN		3	/* log2(8) */
#define	XMIT_RING_LEN		1	/* log2(2) */

#define	RECV_BUFFER_SIZE	0x600
#define	XMIT_BUFFER_SIZE	0x600

#define	RECV_BUFFERS		(1 << RECV_RING_LEN)
#define	XMIT_BUFFERS		(1 << XMIT_RING_LEN)

/*
 *	Initialization block
 */
struct init_block init_block = {
	0,
	0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0,
	RECV_MSG_DESC & 0xffff,
	(RECV_RING_LEN << 13) | (RECV_MSG_DESC >> 16),
	XMIT_MSG_DESC & 0xffff,
	(XMIT_RING_LEN << 13) | (XMIT_MSG_DESC >> 16)
};

/*
 *	LANCE control block
 */
Lance_chan lancesw[NEN] = {
      {	(Lance_reg *)LANCE_PORT,
	(caddr_t)LANCE_MEMORY,
	(caddr_t)ETHER_ID },
#if NEN > 1
      {	(Lance_reg *)LANCE_PORT1,
	(caddr_t)LANCE_MEMORY1,
	(caddr_t)ETHER_ID1 },
#endif
#if NEN > 2
      {	(Lance_reg *)LANCE_PORT2,
	(caddr_t)LANCE_MEMORY2,
	(caddr_t)ETHER_ID2 },
#endif
};

lance_probe(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];
	VOLATILE int *p = (VOLATILE int *)lance->lance_memory;

	if (badaddr(lance->lance_addr, 1))
		return (0);

	*p = 0x12345678;

	return (*p == 0x12345678);
}

lance_open(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];
	register recv_msg_desc *rmd;
	register xmit_msg_desc *tmd;
	register struct init_block *ib;
	register int buffer, i;

	if ((lance->lance_flags & LANCE_ACTIVE) == 0) {
		if (lance->lance_addr == (Lance_reg *)0)
			return (-1);

		lance_write_reg(chan, CSR0, CSR_STOP);

		rmd = (recv_msg_desc *)
		    (RECV_MSG_DESC + lance->lance_memory);
		lance->lance_last_rmd =
		    (lance->lance_rmd = rmd) + RECV_BUFFERS - 1;
		buffer = RECV_BUFFER;
		for (i = 0; i < RECV_BUFFERS; i++) {
			rmd->rmd_ladr = buffer & 0xffff;
			rmd->rmd_stat = RMD_OWN | (buffer >> 16);
			rmd->rmd_bcnt = -RECV_BUFFER_SIZE;
			rmd->rmd_mcnt = 0;
			rmd++;
			buffer += RECV_BUFFER_SIZE;
		}

		tmd = (xmit_msg_desc *)
		    (XMIT_MSG_DESC + lance->lance_memory);
		lance->lance_last_tmd =
			(lance->lance_tmd = tmd) + XMIT_BUFFERS - 1;
		buffer = XMIT_BUFFER;
		for (i = 0; i < XMIT_BUFFERS; i++) {
			tmd->tmd_ladr = buffer & 0xffff;
			tmd->tmd_stat = buffer >> 16;
			tmd->tmd_bcnt = 0;
			tmd++;
			buffer += XMIT_BUFFER_SIZE;
		}
		get_hard_addr(chan, lance->lance_stats.ens_addr);

		ib = (struct init_block *)(INIT_BLOCK + lance->lance_memory);
		lance->lance_ib = ib;
		*ib = init_block;
		ib->ib_padr[0] = lance->lance_stats.ens_addr[1];
		ib->ib_padr[1] = lance->lance_stats.ens_addr[0];
		ib->ib_padr[2] = lance->lance_stats.ens_addr[3];
		ib->ib_padr[3] = lance->lance_stats.ens_addr[2];
		ib->ib_padr[4] = lance->lance_stats.ens_addr[5];
		ib->ib_padr[5] = lance->lance_stats.ens_addr[4];

		if (lance->lance_flags & LANCE_PROM)
			ib->ib_mode |= IB_PROM;

		lance->lance_flags |= LANCE_ACTIVE;
		lance_init(chan);

		if (lance_read_reg(chan, CSR0) !=
		    (CSR_INEA|CSR_RXON|CSR_TXON|CSR_STRT|CSR_INIT)) {
			lance->lance_flags &= ~LANCE_ACTIVE;
			return (-1);
		}

	}

	return (0);
}

lance_close(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];

	lance_write_reg(chan, CSR0, CSR_STOP);
	lance->lance_flags &= ~LANCE_ACTIVE;

}

#define	RECEIVE(lance, rmd)	{ \
	register int i; \
	(rmd) = (lance)->lance_last_rmd + 1; \
	if ((rmd) >= (lance)->lance_rmd + RECV_BUFFERS) \
		(rmd) = (lance)->lance_rmd; \
	if (((rmd)->rmd_stat & RMD_OWN) == 0) \
		(lance)->lance_last_rmd = (rmd); \
	else \
		(rmd) = NULL; \
}
#define	RECV_BUF(lance, rmd)	(char *)((rmd)->rmd_ladr \
					+ (((rmd)->rmd_stat & RMD_HADR) << 16) \
					+ (lance)->lance_memory)
#define	RECV_CNT(rmd)		((rmd)->rmd_mcnt - 4)
#define	RECV_ERR(rmd)		((rmd)->rmd_stat & RMD_ERR)
#define	RELEASE_RECV_BUF(rmd)	{ \
	(rmd)->rmd_mcnt = 0; \
	(rmd)->rmd_stat = ((rmd)->rmd_stat & RMD_HADR) | RMD_OWN; \
}


caddr_t
get_recv_buffer(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];
	register recv_msg_desc *rmd;

next:
	RECEIVE(lance, rmd);
	if (rmd == NULL)
		return (NULL);

	if (RECV_ERR(rmd)) {
		recv_error(lance, rmd);
		RELEASE_RECV_BUF(rmd);
		goto next;
	}

	return (RECV_BUF(lance, rmd));
}

get_recv_length(chan)
	int chan;
{

	return (RECV_CNT(lancesw[chan].lance_last_rmd));
}

free_recv_buffer(chan)
	int chan;
{
	register recv_msg_desc *rmd = lancesw[chan].lance_last_rmd;

	RELEASE_RECV_BUF(rmd);
}

#define	GET_XMIT_BUF(lance, tmd)	{ \
	(tmd) = (lance)->lance_last_tmd + 1; \
	if ((tmd) >= (lance)->lance_tmd + XMIT_BUFFERS) \
		(tmd) = (lance)->lance_tmd; \
	if ((tmd)->tmd_stat & TMD_OWN) \
		(tmd) = NULL; \
	else \
		(lance)->lance_last_tmd = (tmd); \
}
#define	XMIT_BUF(lance, tmd)	(char *)((tmd)->tmd_ladr \
					+ (((tmd)->tmd_stat & TMD_HADR) << 16) \
					+ (lance)->lance_memory)
#define	XMIT_ERR(tmd)		((tmd)->tmd_stat & TMD_ERR)
#define	TRANSMIT(lance, tmd, count)	{ \
	(tmd)->tmd_bcnt = -(count); \
	(tmd)->tmd_error = 0; \
	(tmd)->tmd_stat = ((tmd)->tmd_stat & TMD_HADR) | (TMD_OWN|TMD_STP|TMD_ENP); \
	(lance)->lance_addr->rap = CSR0; \
	(lance)->lance_addr->rdp = (CSR_INEA|CSR_TDMD); \
}

caddr_t
get_xmit_buffer(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];
	register xmit_msg_desc *tmd;

	GET_XMIT_BUF(lance, tmd);
	if (tmd == NULL)
		return (NULL);
	return (XMIT_BUF(lance, tmd));
}

lance_transmit(chan, count)
	int chan;
	int count;
{
	register Lance_chan *lance = &lancesw[chan];
	register xmit_msg_desc *tmd;

	tmd = lance->lance_last_tmd;
	TRANSMIT(lance, tmd, count);
}

lance_xmit_error(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];
	register xmit_msg_desc *tmd;

	tmd = lance->lance_last_tmd;
	if (XMIT_ERR(tmd)) {
		xmit_error(lance, tmd);
		return (1);
	}

	return (0);
}

lance_collision(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];

	if (lance->lance_last_tmd->tmd_stat & (TMD_MORE|TMD_ONE)) {
		lance->lance_stats.ens_collis++;
		return (1);
	}

	return (0);
}

lance_get_addr(chan, addr)
	int chan;
	caddr_t addr;
{
	register Lance_chan *lance = &lancesw[chan];

	bcopy(lance->lance_stats.ens_addr, addr,
		sizeof(lance->lance_stats.ens_addr));
}

lance_prom_mode(chan, cmd)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];

	lance_close(chan);
	if (cmd)
		lance->lance_flags |= LANCE_PROM;
	else
		lance->lance_flags &= ~LANCE_PROM;
	lance_open(chan);
}

lance_get_status(chan, addr)
	int chan;
	caddr_t addr;
{
	register Lance_chan *lance = &lancesw[chan];
	register int s;

	s = splimp();
	bcopy(&lance->lance_stats.ens_frames, addr,
	    sizeof(lance->lance_stats) - sizeof (lance->lance_stats.ens_addr));
	bzero(&lance->lance_stats.ens_frames,
	    sizeof(lance->lance_stats) - sizeof (lance->lance_stats.ens_addr));
	(void) splx(s);
}

lance_intr()
{
	register Lance_chan *lance;
	register Lance_reg *reg;
	register int stat, chan;
	int retval = 0;

	LED_ON;

	for (chan = 0, lance = lancesw; chan < NEN ; lance++, chan++) {
		if ((lance->lance_flags & LANCE_ACTIVE) == 0)
			continue;

		reg = lance->lance_addr;
		reg->rap = CSR0;
		stat = reg->rdp & ~CSR_INEA;
		if ((stat & CSR_INTR) == 0)
			continue;

		retval = 1;
		reg->rdp = stat;
		reg->rdp = CSR_INEA;

		if (stat & CSR_ERR) {
			if (stat & CSR_BABL)
				printf("lance %d error: babl\n", chan);
			if (stat & CSR_MISS)
				lance->lance_stats.ens_lost++;
			if (stat & CSR_MERR)
				printf("lance %d error: merr\n", chan);
		}
		if (stat & CSR_RINT) {
			lance->lance_stats.ens_frames++;
			enrint(chan);
		}
		if (stat & CSR_TINT) {
			lance->lance_stats.ens_xmit++;
			enxint(chan);
		}
		if (stat & CSR_IDON)
			lance->lance_flags |= LANCE_IDON;
	}

	LED_OFF;

	return (retval);
}

lance_init(chan)
	int chan;
{
	register Lance_chan *lance = &lancesw[chan];
	register int s;

	s = splimp();
	lance_write_reg(chan, CSR1, INIT_BLOCK & 0xffff);
	lance_write_reg(chan, CSR2, INIT_BLOCK >> 16);
	lance_write_reg(chan, CSR3, CSR_BSWP|CSR_BCON);

	lance_write_reg(chan, CSR0, CSR_INEA|CSR_STRT|CSR_INIT);
	(void) splx(s);

	while ((lance->lance_flags & LANCE_IDON) == 0)
		;
}

recv_error(lance, rmd)
	register Lance_chan *lance;
	register recv_msg_desc *rmd;
{
	register int status = rmd->rmd_stat;
	register int chan = lance - lancesw;

	if (status & RMD_FRAM)
		lance->lance_stats.ens_align++;
	if (status & RMD_OFLO)
		printf("lance %d recv error: overflow\n", chan);
	if (status & RMD_CRC)
		lance->lance_stats.ens_crc++;
	if (status & RMD_BUFF)
		printf("lance %d:recv error: buffer\n", chan);
}

xmit_error(lance, tmd)
	register Lance_chan *lance;
	register xmit_msg_desc *tmd;
{
	register int status = tmd->tmd_error;
	register int chan = lance - lancesw;

	if (status & TMD_BUFF)
		printf("lance %d: xmit error: buffer\n", chan);
	if (status & TMD_UFLO)
		printf("lance %d: xmit error: underflow\n", chan);
	if (status & TMD_LCOL) {
		printf("lance %d: xmit error: late collision\n", chan);
		lance->lance_stats.ens_owcollis++;
	}
	if (status & TMD_LCAR)
		printf("lance %d: xmit error: loss of carrier\n", chan);
	if (status & TMD_RTRY) {
		printf("lance %d: xmit error: retry tdr=%d\n",
		    chan, status & TMD_TDR);
		lance->lance_stats.ens_xcollis++;
	}
}

lance_write_reg(chan, reg, data)
	int chan, reg, data;
{
	register Lance_reg *lance = lancesw[chan].lance_addr;
	register int s;

	s = spl7();
	lance->rap = reg;
	lance->rdp = data;
	(void) splx(s);
}

lance_read_reg(chan, reg)
	int chan, reg;
{
	register Lance_reg *lance = lancesw[chan].lance_addr;
	register int s, d;

	s = spl7();
	lance->rap = reg;
	d = lance->rdp;
	(void) splx(s);

	return (d);
}

get_hard_addr(chan, addr)
	int chan;
	u_short *addr;
{
	register unsigned char *p, *q;
	register int i;
	register Lance_chan *lance = &lancesw[chan];
	unsigned char hard_addr[6];

	p = (unsigned char *)lance->lance_rom + 16;
	q = hard_addr;
	for (i = 0; i < 6; i++) {
		*q = (*p++ & 0xf) << 4;
		*q++ |= *p++ & 0xf;
	}
	
	bcopy(hard_addr, (char *)addr, 6);
}

#if defined(mips) && defined(CPU_SINGLE)
bxcopy(s, d, n)
	caddr_t s, d;
	int n;
{

	if (n <= 0)
		return;
	switch ((((int)s & 03) << 2) + ((int)d & 03)) {

	case 0x0:
		blcopy((long *)s, (long *)d, n);
		return;

	case 0x5:
		*(char *)d = *(char *)s;
		blcopy((long *)(s + 1), (long *)(d + 1), n - 1);
		return;

	case 0xa:
		switch (n) {

		case 1:
			*(char *)d = *(char *)s;
			return;

		case 2:
			*(short *)d = *(short *)s;
			return;

		default:
			*(short *)d = *(short *)s;
			blcopy((long *)(s + 2), (long *)(d + 2), n - 2);
			return;
		}

	case 0xf:
		switch (n) {

		case 1:
			*(char *)d = *(char *)s;
			return;

		case 2:
			*(char *)d = *(char *)s;
			*(char *)(d + 1) = *(char *)(s + 1);
			return;

		case 3:
			*(char *)d = *(char *)s;
			*(short *)(d + 1) = *(short *)(s + 1);
			return;

		default:
			*(char *)d = *(char *)s;
			*(short *)(d + 1) = *(short *)(s + 1);
			blcopy((long *)(s + 3), (long *)(d + 3), n - 3);
			return;
		}

	case 0x7:
	case 0xd:
		switch (n) {

		case 1:
			*(char *)d = *(char *)s;
			return;

		case 2:
			*(char *)d = *(char *)s;
			*(char *)(d + 1) = *(char *)(s + 1);
			return;

		default:
			*(char *)d = *(char *)s;
			bwcopy((short *)(s + 1), (short *)(d + 1), n);
			return;
		}

	case 0x2:
	case 0x8:
		bwcopy((short *)s, (short *)d, n);
		return;

	default:
		bbcopy((char *)s, (char *)d, n);
		return;
	}
}

#define	COPY(s, d, n, t) \
	while ((n) >= 8 * sizeof (t)) { \
		int t0, t1, t2, t3, t4, t5, t6, t7; \
		t0 = (s)[0]; \
		t1 = (s)[1]; \
		t2 = (s)[2]; \
		t3 = (s)[3]; \
		t4 = (s)[4]; \
		t5 = (s)[5]; \
		t6 = (s)[6]; \
		t7 = (s)[7]; \
		(d)[0] = t0; \
		(d)[1] = t1; \
		(d)[2] = t2; \
		(d)[3] = t3; \
		(d)[4] = t4; \
		(d)[5] = t5; \
		(d)[6] = t6; \
		(d)[7] = t7; \
		(s) += 8; \
		(d) += 8; \
		(n) -= 8 * sizeof (t); \
	} \
	while ((n) >= sizeof (t)) { \
		(d)[0] = (s)[0]; \
		(s)++; \
		(d)++; \
		(n) -= sizeof (t); \
	}

blcopy(s, d, n)
	long *s, *d;
	int n;
{

	COPY(s, d, n, long);
	switch (n) {

	case 0:
		return;

	case 1:
		*(char *)d = *(char *)s;
		return;

	case 2:
		*(short *)d = *(short *)s;
		return;

	case 3:
		*(short *)d = *(short *)s;
		*((char *)d + 2) = *((char *)s + 2);
		return;
	}
}

bwcopy(s, d, n)
	short *s, *d;
	int n;
{

	COPY(s, d, n, short);
	if (n == 1)
		*(char *)d = *(char *)s;
}

bbcopy(s, d, n)
	char *s, *d;
	int n;
{

	COPY(s, d, n, char);
}
#endif /* defined(mips) && defined(CPU_SINGLE) */

#endif /* NEN > 0 */